From 097c05ff5d1b028d4457f95f987c0c18dbd71650 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 05:31:10 +0000 Subject: [PATCH 1/6] chore: pin effect packages to npm 4.0.5 Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- package.json | 6 +- packages/cli/package.json | 4 +- packages/e2e/package.json | 8 +- packages/effect-app/package.json | 2 +- packages/infra/package.json | 6 +- packages/vue-components/package.json | 2 +- packages/vue/package.json | 8 +- pnpm-lock.yaml | 645 +++++++++++++++------------ pnpm-workspace.yaml | 14 +- 9 files changed, 396 insertions(+), 299 deletions(-) diff --git a/package.json b/package.json index 48ead372c..e2403f0e8 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.2", - "@effect/vitest": "4.0.2", + "@effect/platform-node": "4.0.5", + "@effect/vitest": "4.0.5", "@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.2", + "effect": "4.0.5", "effect-app": "workspace:*", "eslint": "^10.4.1", "json5": "^2.2.3", diff --git a/packages/cli/package.json b/packages/cli/package.json index a3f9aa043..96bad5261 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.2", - "effect": "4.0.2", + "@effect/platform-node": "4.0.5", + "effect": "4.0.5", "js-yaml": "4.2.0" }, "devDependencies": { diff --git a/packages/e2e/package.json b/packages/e2e/package.json index 875d810d2..09064562b 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -10,13 +10,13 @@ "effect-app": "workspace:*" }, "devDependencies": { - "@effect/atom-vue": "^4.0.2", - "@effect/platform-node": "4.0.2", - "@effect/vitest": "4.0.2", + "@effect/atom-vue": "^4.0.5", + "@effect/platform-node": "4.0.5", + "@effect/vitest": "4.0.5", "@tanstack/vue-query": "5.96.2", "@types/node": "25.9.1", "@vitejs/plugin-vue": "^6.0.7", - "effect": "^4.0.2", + "effect": "^4.0.5", "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 df54edc9d..9987ba65a 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.2" + "effect": "^4.0.5" }, "typesVersions": { "*": { diff --git a/packages/infra/package.json b/packages/infra/package.json index 4e7b53aee..d85b0997f 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.2", + "@effect/sql-sqlite-node": "4.0.5", "@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.2", + "@effect/vitest": "^4.0.5", "@sendgrid/helpers": "^8.0.0", "@sendgrid/mail": "^8.1.6", "@sentry/node": "10.55.0", "@sentry/opentelemetry": "10.55.0", - "effect": "^4.0.2", + "effect": "^4.0.5", "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 872c17bb2..e50ac8b7e 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.2", + "effect": "^4.0.5", "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 527fbe233..6fe37b4ea 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.2", - "@effect/platform-browser": "^4.0.2", + "@effect/atom-vue": "^4.0.5", + "@effect/platform-browser": "^4.0.5", "@sentry/browser": "^10.55.0", - "effect": "^4.0.2", + "effect": "^4.0.5", "vue": "^3.5.35" }, "devDependencies": { - "@effect/vitest": "4.0.2", + "@effect/vitest": "4.0.5", "@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 4eff72fcd..64ba5978b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -254,14 +254,14 @@ importers: specifier: 0.87.3 version: 0.87.3 "@effect/platform-node": - specifier: 4.0.2 - version: 4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5)(redis@6.2.1(@opentelemetry/api@1.9.1)) "@effect/tsgo": specifier: ^0.47.2 version: 0.47.2 "@effect/vitest": - 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))) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5)(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.27.2)(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 @@ -270,10 +270,10 @@ importers: version: 25.9.1 "@typescript-eslint/eslint-plugin": specifier: 8.60.0 - version: 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + version: 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/parser": specifier: 8.60.0 - version: 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + version: 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript/native-preview": specifier: 7.0.0-dev.20260626.1 version: 7.0.0-dev.20260626.1 @@ -281,14 +281,14 @@ importers: specifier: ^0.54.0 version: 0.54.0 effect: - specifier: 4.0.2 - version: 4.0.2 + specifier: 4.0.5 + version: 4.0.5 effect-app: specifier: workspace:* version: link:packages/effect-app eslint: specifier: ^10.4.1 - version: 10.4.1(supports-color@8.1.1) + version: 10.4.1(supports-color@7.2.0) json5: specifier: ^2.2.3 version: 2.2.3 @@ -297,7 +297,7 @@ importers: version: 17.3.0 madge: specifier: ^8.0.0 - version: 8.0.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + version: 8.0.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) npm-check-updates: specifier: ^22.2.1 version: 22.2.1 @@ -318,19 +318,19 @@ importers: version: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) vite: specifier: ^8.0.15 - version: 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) + version: 8.0.15(@types/node@25.9.1)(esbuild@0.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0) vitest: specifier: ^4.1.7 - version: 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)) + version: 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.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)) packages/cli: dependencies: "@effect/platform-node": - specifier: 4.0.2 - version: 4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5)(redis@6.2.1(@opentelemetry/api@1.9.1)) effect: - specifier: 4.0.2 - version: 4.0.2 + specifier: 4.0.5 + version: 4.0.5 js-yaml: specifier: 4.2.0 version: 4.2.0 @@ -347,8 +347,8 @@ importers: vitest: specifier: ^4.1.7 version: 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)) - publishDirectory: .publish linkDirectory: false + publishDirectory: .publish packages/e2e: dependencies: @@ -363,14 +363,14 @@ importers: version: link:../effect-app devDependencies: "@effect/atom-vue": - specifier: ^4.0.2 - version: 4.0.2(effect@4.0.2)(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) + specifier: ^4.0.5 + version: 4.0.5(effect@4.0.5)(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) "@effect/platform-node": - specifier: 4.0.2 - version: 4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1)) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5)(redis@6.2.1(@opentelemetry/api@1.9.1)) "@effect/vitest": - 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))) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5)(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=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) @@ -381,8 +381,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=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) effect: - specifier: ^4.0.2 - version: 4.0.2 + specifier: ^4.0.5 + version: 4.0.5 typescript: specifier: ~6.0.3 version: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) @@ -399,8 +399,8 @@ importers: specifier: ^2.0.8 version: 2.0.8 effect: - specifier: ^4.0.2 - version: 4.0.2 + specifier: ^4.0.5 + version: 4.0.5 nanoid: specifier: ^5.1.11 version: 5.1.11 @@ -435,8 +435,8 @@ importers: vitest: specifier: ^4.1.7 version: 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)) - publishDirectory: .publish linkDirectory: false + publishDirectory: .publish packages/eslint-codegen-model: dependencies: @@ -458,15 +458,15 @@ importers: version: link:../effect-app madge: specifier: 8.0.0 - version: 8.0.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + version: 8.0.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) oxlint: specifier: 1.67.0 - version: 1.67.0(oxlint-tsgolint@0.23.0) + version: 1.67.0(oxlint-tsgolint@7.0.2001) typescript: specifier: ~6.0.3 version: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) - publishDirectory: .publish linkDirectory: false + publishDirectory: .publish packages/eslint-shared-config: dependencies: @@ -481,16 +481,16 @@ importers: version: link:../eslint-codegen-model "@eslint/eslintrc": specifier: 3.3.5 - version: 3.3.5(supports-color@8.1.1) + version: 3.3.5(supports-color@7.2.0) "@eslint/js": specifier: 10.0.1 - version: 10.0.1(eslint@10.4.1(supports-color@8.1.1)) + version: 10.0.1(eslint@10.4.1(supports-color@7.2.0)) "@typescript-eslint/eslint-plugin": specifier: 8.60.0 - version: 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + version: 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/parser": specifier: 8.60.0 - version: 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + version: 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) dprint-plugin-malva: specifier: 0.16.0 version: 0.16.0 @@ -502,22 +502,22 @@ importers: version: 0.6.0 eslint: specifier: ^10.4.1 - version: 10.4.1(supports-color@8.1.1) + version: 10.4.1(supports-color@7.2.0) eslint-plugin-formatjs: specifier: 6.4.13 - version: 6.4.13(eslint@10.4.1(supports-color@8.1.1)) + version: 6.4.13(eslint@10.4.1(supports-color@7.2.0)) eslint-plugin-import: specifier: 2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1) + version: 2.32.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0) eslint-plugin-sort-destructure-keys: specifier: 3.0.0 - version: 3.0.0(eslint@10.4.1(supports-color@8.1.1)) + version: 3.0.0(eslint@10.4.1(supports-color@7.2.0)) eslint-plugin-unused-imports: specifier: 4.4.1 - version: 4.4.1(@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1)) + version: 4.4.1(@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0)) eslint-plugin-vue: specifier: 10.9.1 - version: 10.9.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(vue-eslint-parser@10.4.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)) + version: 10.9.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(vue-eslint-parser@10.4.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)) globals: specifier: 17.6.0 version: 17.6.0 @@ -535,13 +535,13 @@ importers: version: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) vue-eslint-parser: specifier: 10.4.0 - version: 10.4.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1) + version: 10.4.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0) packages/infra: dependencies: "@effect/vitest": - 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))) + specifier: ^4.0.5 + version: 4.0.5(effect@4.0.5)(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 @@ -550,13 +550,13 @@ importers: version: 8.0.0 "@sendgrid/mail": specifier: ^8.1.6 - version: 8.1.6(patch_hash=3d2dd1cd5f52d3eef1772e4be48776258a596f9277c00adf0a471f54fcd3e21a)(debug@4.4.3(supports-color@8.1.1)) + version: 8.1.6(patch_hash=3d2dd1cd5f52d3eef1772e4be48776258a596f9277c00adf0a471f54fcd3e21a)(debug@4.4.3(supports-color@7.2.0)) "@sentry/node": specifier: 10.55.0 - version: 10.55.0(supports-color@8.1.1) + version: 10.55.0(supports-color@7.2.0) effect: - specifier: ^4.0.2 - version: 4.0.2 + specifier: ^4.0.5 + version: 4.0.5 effect-app: specifier: workspace:* version: link:../effect-app @@ -578,13 +578,13 @@ importers: devDependencies: "@azure/cosmos": specifier: ^4.9.3 - version: 4.9.3(supports-color@8.1.1) + version: 4.9.3(supports-color@7.2.0) "@azure/service-bus": specifier: ^7.9.5 - version: 7.9.5(supports-color@8.1.1) + version: 7.9.5(supports-color@7.2.0) "@effect/sql-sqlite-node": - specifier: 4.0.2 - version: 4.0.2(effect@4.0.2) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5) "@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) @@ -624,17 +624,17 @@ importers: vitest: specifier: ^4.1.7 version: 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)) - publishDirectory: .publish linkDirectory: false + publishDirectory: .publish packages/vue: dependencies: "@effect/atom-vue": - specifier: ^4.0.2 - version: 4.0.2(effect@4.0.2)(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) + specifier: ^4.0.5 + version: 4.0.5(effect@4.0.5)(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) "@effect/platform-browser": - specifier: ^4.0.2 - version: 4.0.2(effect@4.0.2) + specifier: ^4.0.5 + version: 4.0.5(effect@4.0.5) "@formatjs/intl": specifier: ^4.1.12 version: 4.1.12 @@ -651,8 +651,8 @@ importers: specifier: ^5.4.4 version: 5.4.4 effect: - specifier: ^4.0.2 - version: 4.0.2 + specifier: ^4.0.5 + version: 4.0.5 effect-app: specifier: workspace:* version: link:../effect-app @@ -664,8 +664,8 @@ importers: version: 3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) devDependencies: "@effect/vitest": - 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))) + specifier: 4.0.5 + version: 4.0.5(effect@4.0.5)(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 @@ -687,8 +687,8 @@ importers: vitest: specifier: ^4.1.7 version: 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)) - publishDirectory: .publish linkDirectory: false + publishDirectory: .publish packages/vue-components: dependencies: @@ -705,8 +705,8 @@ importers: specifier: ^1.32.0 version: 1.32.0(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) effect: - specifier: ^4.0.2 - version: 4.0.2 + specifier: ^4.0.5 + version: 4.0.5 effect-app: specifier: workspace:* version: link:../effect-app @@ -1393,12 +1393,12 @@ packages: cpu: [x64] os: [win32] - "@effect/atom-vue@4.0.2": + "@effect/atom-vue@4.0.5": resolution: { - integrity: sha512-ftfF/0tHKEVHsT+K70nfZehcB10Y9I5AyvWSKSc3DqOonUf6y65SGT4+bhlp1gitfo8fJpXRRta+nH+KpK7ppg==, + integrity: sha512-GYnHSbdDE+2Q3AgQSNIxw0lsAUQngeP5RCW2xW9YH8KZ0eLq5DNUk3aHZyZy72M2KgM9dEU/z//vwMDBDB/c2g==, } peerDependencies: - effect: ^4.0.2 + effect: ^4.0.5 vue: ^3.5.35 "@effect/language-service@0.87.3": @@ -1407,36 +1407,36 @@ packages: } hasBin: true - "@effect/platform-browser@4.0.2": + "@effect/platform-browser@4.0.5": resolution: { - integrity: sha512-juVpWmx+2e5pw3MsMSMzwsQ7sut3exkeweaQToAcn+4PKGNWvItZ72+qAalgRNQE5luPiodP507oe44gC36Vsg==, + integrity: sha512-o3H75xA91nkgIWOcp0XouIxxWxJEOFkTf7gG4qkQtZfy0tq46hJBI0U8jrUqsPruo2vCCNfM0yCoj5AG7F5BMQ==, } peerDependencies: - effect: ^4.0.2 + effect: ^4.0.5 - "@effect/platform-node-shared@4.0.2": + "@effect/platform-node-shared@4.0.5": resolution: { - integrity: sha512-xn8aDw9p/QUPt1Yi0C3HUMcycT+MjHfAe19FDUDrUuzcwq0xxskkamVSqbdykF/Dsuus4aZTsCBZA/vkhHIfbQ==, + integrity: sha512-a8rCdEkfZBGe9C6bsIjxVZuRp73svIyK5IHwHdDAVsvZXjrbkxusfI+rZ8WlX31RP1mKizwI/E72GQTNfdMWHw==, } engines: { node: ">=18.0.0" } peerDependencies: - effect: ^4.0.2 + effect: ^4.0.5 - "@effect/platform-node@4.0.2": + "@effect/platform-node@4.0.5": resolution: { - integrity: sha512-RWesXBQStnFHdD465gR5thFKgXO7FSyCKRZ56nWXB7PedNVzr8vV0nb/3MULLdri493sMz/b/pmGBTb6K8cBjQ==, + integrity: sha512-v4KD5fWvEUa4QmWFpZFbBsRhmfnTP6ETCME2r9ifyXVDPjhfpxpTZl41prjgQFMqZBHdWWCl7YC5ImbXv5ui5g==, } engines: { node: ">=18.0.0" } peerDependencies: - effect: ^4.0.2 + effect: ^4.0.5 redis: ">=5.0.0 <7.0.0" - "@effect/sql-sqlite-node@4.0.2": + "@effect/sql-sqlite-node@4.0.5": resolution: { - integrity: sha512-QjhQ6yetczGE08ZV/05Y4Cj7GNGswloRZuk2cZ4tl84C91398Ho0PhDLexihO2RHBTE1YjpUW8Mdl4KH41I/LQ==, + integrity: sha512-SHza5qNwiI4dRtUk45wvUfvZq7vB950DW2wD6npzt2aH/tesFfPlZJSDBIvwhazoK+gvDknBOONfKyFZsIF+tg==, } peerDependencies: - effect: ^4.0.2 + effect: ^4.0.5 "@effect/tsgo-darwin-arm64@0.47.2": resolution: { @@ -1493,12 +1493,12 @@ packages: } hasBin: true - "@effect/vitest@4.0.2": + "@effect/vitest@4.0.5": resolution: { - integrity: sha512-Zb72nu7iUy3rNdBp9J1Xu5oUQGwJ8cOYsUwmYgujj88aqyvASRDjOS01SjlcPViqf77csduetKdCJTVndMNfzw==, + integrity: sha512-q64paZ/i4V2NiRhTyqCmT2KiDbFjlxc2EZrZca8GGt+Nf8844946HyJM5bPk/q+YeRkFSxIrlCs+J+LSfky/wA==, } peerDependencies: - effect: ^4.0.2 + effect: ^4.0.5 vitest: ">=5.0.0 <6.0.0" "@emnapi/core@1.10.0": @@ -4990,11 +4990,6 @@ packages: resolution: { integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==, } - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true ajv-keywords@5.1.0: resolution: { @@ -5864,9 +5859,9 @@ packages: engines: { node: ">=14" } hasBin: true - effect@4.0.2: + effect@4.0.5: resolution: { - integrity: sha512-YeYnLqSFrL4+tCOXRrfNP03w8FILoF/wDl+ukraJAGzXrjcS383W7LAAvGBbLatEyktmLxRq+9xJinx7QzXW5A==, + integrity: sha512-BY4/28NlpOd99yzmV741xbVGmp+1U7tks4oB7F/Png64bTa5tm5eCA6W0RiXsnuLDDoJpjfVSo4Dnq4BZb/89Q==, } electron-to-chromium@1.5.267: @@ -9887,13 +9882,13 @@ snapshots: "@asamuzakjp/nwsapi@2.3.9": {} - "@azure-rest/core-client@2.5.1(supports-color@8.1.1)": + "@azure-rest/core-client@2.5.1(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@typespec/ts-http-runtime": 0.3.2(supports-color@8.1.1) + "@typespec/ts-http-runtime": 0.3.2(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -9906,55 +9901,55 @@ snapshots: dependencies: tslib: 2.8.1 - "@azure/core-amqp@4.4.1(supports-color@8.1.1)": + "@azure/core-amqp@4.4.1(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) buffer: 6.0.3 events: 3.3.0 process: 0.11.10 - rhea: 3.0.4(supports-color@8.1.1) - rhea-promise: 3.0.3(supports-color@8.1.1) + rhea: 3.0.4(supports-color@7.2.0) + rhea-promise: 3.0.3(supports-color@7.2.0) tslib: 2.8.1 util: 0.12.5 transitivePeerDependencies: - supports-color - "@azure/core-auth@1.10.1(supports-color@8.1.1)": + "@azure/core-auth@1.10.1(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-util": 1.13.1(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color - "@azure/core-client@1.10.1(supports-color@8.1.1)": + "@azure/core-client@1.10.1(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color - "@azure/core-http-compat@2.3.1(supports-color@8.1.1)": + "@azure/core-http-compat@2.3.1(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-client": 1.10.1(supports-color@8.1.1) - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-client": 1.10.1(supports-color@7.2.0) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) transitivePeerDependencies: - supports-color - "@azure/core-lro@2.7.2(supports-color@8.1.1)": + "@azure/core-lro@2.7.2(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -9963,14 +9958,14 @@ snapshots: dependencies: tslib: 2.8.1 - "@azure/core-rest-pipeline@1.22.2(supports-color@8.1.1)": + "@azure/core-rest-pipeline@1.22.2(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) - "@typespec/ts-http-runtime": 0.3.2(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) + "@typespec/ts-http-runtime": 0.3.2(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -9979,10 +9974,10 @@ snapshots: dependencies: tslib: 2.8.1 - "@azure/core-util@1.13.1(supports-color@8.1.1)": + "@azure/core-util@1.13.1(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@typespec/ts-http-runtime": 0.3.2(supports-color@8.1.1) + "@typespec/ts-http-runtime": 0.3.2(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -9992,15 +9987,15 @@ snapshots: fast-xml-parser: 5.3.3 tslib: 2.8.1 - "@azure/cosmos@4.9.3(supports-color@8.1.1)": + "@azure/cosmos@4.9.3(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/keyvault-keys": 4.10.0(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/keyvault-keys": 4.10.0(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) fast-json-stable-stringify: 2.1.0 priorityqueuejs: 2.0.0 semaphore: 1.1.0 @@ -10008,62 +10003,62 @@ snapshots: transitivePeerDependencies: - supports-color - "@azure/keyvault-common@2.0.0(supports-color@8.1.1)": + "@azure/keyvault-common@2.0.0(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-client": 1.10.1(supports-color@8.1.1) - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-client": 1.10.1(supports-color@7.2.0) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color - "@azure/keyvault-keys@4.10.0(supports-color@8.1.1)": + "@azure/keyvault-keys@4.10.0(supports-color@7.2.0)": dependencies: - "@azure-rest/core-client": 2.5.1(supports-color@8.1.1) + "@azure-rest/core-client": 2.5.1(supports-color@7.2.0) "@azure/abort-controller": 2.1.2 - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-http-compat": 2.3.1(supports-color@8.1.1) - "@azure/core-lro": 2.7.2(supports-color@8.1.1) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-http-compat": 2.3.1(supports-color@7.2.0) + "@azure/core-lro": 2.7.2(supports-color@7.2.0) "@azure/core-paging": 1.6.2 - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@azure/core-util": 1.13.1(supports-color@8.1.1) - "@azure/keyvault-common": 2.0.0(supports-color@8.1.1) - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) + "@azure/keyvault-common": 2.0.0(supports-color@7.2.0) + "@azure/logger": 1.3.0(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color - "@azure/logger@1.3.0(supports-color@8.1.1)": + "@azure/logger@1.3.0(supports-color@7.2.0)": dependencies: - "@typespec/ts-http-runtime": 0.3.2(supports-color@8.1.1) + "@typespec/ts-http-runtime": 0.3.2(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color - "@azure/service-bus@7.9.5(supports-color@8.1.1)": + "@azure/service-bus@7.9.5(supports-color@7.2.0)": dependencies: "@azure/abort-controller": 1.1.0 - "@azure/core-amqp": 4.4.1(supports-color@8.1.1) - "@azure/core-auth": 1.10.1(supports-color@8.1.1) - "@azure/core-client": 1.10.1(supports-color@8.1.1) + "@azure/core-amqp": 4.4.1(supports-color@7.2.0) + "@azure/core-auth": 1.10.1(supports-color@7.2.0) + "@azure/core-client": 1.10.1(supports-color@7.2.0) "@azure/core-paging": 1.6.2 - "@azure/core-rest-pipeline": 1.22.2(supports-color@8.1.1) + "@azure/core-rest-pipeline": 1.22.2(supports-color@7.2.0) "@azure/core-tracing": 1.3.1 - "@azure/core-util": 1.13.1(supports-color@8.1.1) + "@azure/core-util": 1.13.1(supports-color@7.2.0) "@azure/core-xml": 1.5.0 - "@azure/logger": 1.3.0(supports-color@8.1.1) + "@azure/logger": 1.3.0(supports-color@7.2.0) "@types/is-buffer": 2.0.2 buffer: 6.0.3 is-buffer: 2.0.5 jssha: 3.3.1 long: 5.3.2 process: 0.11.10 - rhea-promise: 3.0.3(supports-color@8.1.1) + rhea-promise: 3.0.3(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -10350,39 +10345,39 @@ snapshots: "@dprint/win32-x64@0.54.0": optional: true - "@effect/atom-vue@4.0.2(effect@4.0.2)(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))": + "@effect/atom-vue@4.0.5(effect@4.0.5)(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))": dependencies: - effect: 4.0.2 + effect: 4.0.5 vue: 3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@effect/language-service@0.87.3": {} - "@effect/platform-browser@4.0.2(effect@4.0.2)": + "@effect/platform-browser@4.0.5(effect@4.0.5)": dependencies: - effect: 4.0.2 + effect: 4.0.5 - "@effect/platform-node-shared@4.0.2(effect@4.0.2)": + "@effect/platform-node-shared@4.0.5(effect@4.0.5)": dependencies: "@types/ws": 8.18.2 - effect: 4.0.2 + effect: 4.0.5 ws: 8.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate - "@effect/platform-node@4.0.2(effect@4.0.2)(redis@6.2.1(@opentelemetry/api@1.9.1))": + "@effect/platform-node@4.0.5(effect@4.0.5)(redis@6.2.1(@opentelemetry/api@1.9.1))": dependencies: - "@effect/platform-node-shared": 4.0.2(effect@4.0.2) - effect: 4.0.2 + "@effect/platform-node-shared": 4.0.5(effect@4.0.5) + effect: 4.0.5 redis: 6.2.1(@opentelemetry/api@1.9.1) undici: 8.11.2 transitivePeerDependencies: - bufferutil - utf-8-validate - "@effect/sql-sqlite-node@4.0.2(effect@4.0.2)": + "@effect/sql-sqlite-node@4.0.5(effect@4.0.5)": dependencies: - effect: 4.0.2 + effect: 4.0.5 "@effect/tsgo-darwin-arm64@0.47.2": optional: true @@ -10415,9 +10410,14 @@ snapshots: "@effect/tsgo-win32-arm64": 0.47.2 "@effect/tsgo-win32-x64": 0.47.2 - "@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)))": + "@effect/vitest@4.0.5(effect@4.0.5)(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.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)))": dependencies: - effect: 4.0.2 + effect: 4.0.5 + 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.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)) + + "@effect/vitest@4.0.5(effect@4.0.5)(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.5 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": @@ -10672,17 +10672,17 @@ snapshots: "@esbuild/win32-x64@0.28.0": optional: true - "@eslint-community/eslint-utils@4.9.1(eslint@10.4.1(supports-color@8.1.1))": + "@eslint-community/eslint-utils@4.9.1(eslint@10.4.1(supports-color@7.2.0))": dependencies: - eslint: 10.4.1(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) eslint-visitor-keys: 3.4.3 "@eslint-community/regexpp@4.12.2": {} - "@eslint/config-array@0.23.5(supports-color@8.1.1)": + "@eslint/config-array@0.23.5(supports-color@7.2.0)": dependencies: "@eslint/object-schema": 3.0.5 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) minimatch: 10.2.4 transitivePeerDependencies: - supports-color @@ -10695,10 +10695,10 @@ snapshots: dependencies: "@types/json-schema": 7.0.15 - "@eslint/eslintrc@3.3.5(supports-color@8.1.1)": + "@eslint/eslintrc@3.3.5(supports-color@7.2.0)": dependencies: ajv: 6.14.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -10709,9 +10709,9 @@ snapshots: transitivePeerDependencies: - supports-color - "@eslint/js@10.0.1(eslint@10.4.1(supports-color@8.1.1))": + "@eslint/js@10.0.1(eslint@10.4.1(supports-color@7.2.0))": optionalDependencies: - eslint: 10.4.1(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) "@eslint/object-schema@3.0.5": {} @@ -10866,12 +10866,12 @@ snapshots: "@opentelemetry/api": 1.9.1 "@opentelemetry/semantic-conventions": 1.40.0 - "@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@8.1.1)": + "@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0)": dependencies: "@opentelemetry/api": 1.9.1 "@opentelemetry/api-logs": 0.214.0 import-in-the-middle: 3.0.0 - require-in-the-middle: 8.0.1(supports-color@8.1.1) + require-in-the-middle: 8.0.1(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -11470,10 +11470,10 @@ snapshots: "@rtsao/scc@1.1.0": {} - "@sendgrid/client@8.1.6(debug@4.4.3(supports-color@8.1.1))": + "@sendgrid/client@8.1.6(debug@4.4.3(supports-color@7.2.0))": dependencies: "@sendgrid/helpers": 8.0.0 - axios: 1.13.2(debug@4.4.3(supports-color@8.1.1)) + axios: 1.13.2(debug@4.4.3(supports-color@7.2.0)) transitivePeerDependencies: - debug @@ -11481,9 +11481,9 @@ snapshots: dependencies: deepmerge: 4.3.1 - "@sendgrid/mail@8.1.6(patch_hash=3d2dd1cd5f52d3eef1772e4be48776258a596f9277c00adf0a471f54fcd3e21a)(debug@4.4.3(supports-color@8.1.1))": + "@sendgrid/mail@8.1.6(patch_hash=3d2dd1cd5f52d3eef1772e4be48776258a596f9277c00adf0a471f54fcd3e21a)(debug@4.4.3(supports-color@7.2.0))": dependencies: - "@sendgrid/client": 8.1.6(debug@4.4.3(supports-color@8.1.1)) + "@sendgrid/client": 8.1.6(debug@4.4.3(supports-color@7.2.0)) "@sendgrid/helpers": 8.0.0 transitivePeerDependencies: - debug @@ -11516,7 +11516,7 @@ snapshots: "@sentry/core@10.55.0": {} - "@sentry/node-core@10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)": + "@sentry/node-core@10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)": dependencies: "@sentry/core": 10.55.0 "@sentry/opentelemetry": 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) @@ -11524,19 +11524,19 @@ snapshots: optionalDependencies: "@opentelemetry/api": 1.9.1 "@opentelemetry/core": 2.6.1(@opentelemetry/api@1.9.1) - "@opentelemetry/instrumentation": 0.214.0(@opentelemetry/api@1.9.1)(supports-color@8.1.1) + "@opentelemetry/instrumentation": 0.214.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0) "@opentelemetry/sdk-trace-base": 2.6.1(@opentelemetry/api@1.9.1) "@opentelemetry/semantic-conventions": 1.40.0 - "@sentry/node@10.55.0(supports-color@8.1.1)": + "@sentry/node@10.55.0(supports-color@7.2.0)": dependencies: "@opentelemetry/api": 1.9.1 "@opentelemetry/core": 2.6.1(@opentelemetry/api@1.9.1) - "@opentelemetry/instrumentation": 0.214.0(@opentelemetry/api@1.9.1)(supports-color@8.1.1) + "@opentelemetry/instrumentation": 0.214.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0) "@opentelemetry/sdk-trace-base": 2.6.1(@opentelemetry/api@1.9.1) "@opentelemetry/semantic-conventions": 1.40.0 "@sentry/core": 10.55.0 - "@sentry/node-core": 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + "@sentry/node-core": 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) "@sentry/opentelemetry": 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) import-in-the-middle: 3.0.0 transitivePeerDependencies: @@ -11839,15 +11839,15 @@ snapshots: dependencies: "@types/node": 25.9.1 - "@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": + "@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": dependencies: "@eslint-community/regexpp": 4.12.2 - "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/scope-manager": 8.60.0 - "@typescript-eslint/type-utils": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) - "@typescript-eslint/utils": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/type-utils": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/utils": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/visitor-keys": 8.60.0 - eslint: 10.4.1(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) @@ -11855,32 +11855,32 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": + "@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": dependencies: "@typescript-eslint/scope-manager": 8.60.0 "@typescript-eslint/types": 8.60.0 - "@typescript-eslint/typescript-estree": 8.60.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/typescript-estree": 8.60.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/visitor-keys": 8.60.0 - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.4.1(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) + eslint: 10.4.1(supports-color@7.2.0) typescript: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) transitivePeerDependencies: - supports-color - "@typescript-eslint/project-service@8.60.0(supports-color@8.1.1)(typescript@5.9.3)": + "@typescript-eslint/project-service@8.60.0(supports-color@7.2.0)(typescript@5.9.3)": dependencies: "@typescript-eslint/tsconfig-utils": 8.60.0(typescript@5.9.3) "@typescript-eslint/types": 8.60.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/project-service@8.60.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": + "@typescript-eslint/project-service@8.60.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": dependencies: "@typescript-eslint/tsconfig-utils": 8.60.0(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/types": 8.60.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) typescript: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) transitivePeerDependencies: - supports-color @@ -11898,13 +11898,13 @@ snapshots: dependencies: typescript: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) - "@typescript-eslint/type-utils@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": + "@typescript-eslint/type-utils@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": dependencies: "@typescript-eslint/types": 8.60.0 - "@typescript-eslint/typescript-estree": 8.60.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) - "@typescript-eslint/utils": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.4.1(supports-color@8.1.1) + "@typescript-eslint/typescript-estree": 8.60.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/utils": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + debug: 4.4.3(supports-color@7.2.0) + eslint: 10.4.1(supports-color@7.2.0) ts-api-utils: 2.5.0(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) typescript: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) transitivePeerDependencies: @@ -11912,13 +11912,13 @@ snapshots: "@typescript-eslint/types@8.60.0": {} - "@typescript-eslint/typescript-estree@8.60.0(supports-color@8.1.1)(typescript@5.9.3)": + "@typescript-eslint/typescript-estree@8.60.0(supports-color@7.2.0)(typescript@5.9.3)": dependencies: - "@typescript-eslint/project-service": 8.60.0(supports-color@8.1.1)(typescript@5.9.3) + "@typescript-eslint/project-service": 8.60.0(supports-color@7.2.0)(typescript@5.9.3) "@typescript-eslint/tsconfig-utils": 8.60.0(typescript@5.9.3) "@typescript-eslint/types": 8.60.0 "@typescript-eslint/visitor-keys": 8.60.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) minimatch: 10.2.4 semver: 7.7.3 tinyglobby: 0.2.17 @@ -11927,13 +11927,13 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/typescript-estree@8.60.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": + "@typescript-eslint/typescript-estree@8.60.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": dependencies: - "@typescript-eslint/project-service": 8.60.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/project-service": 8.60.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/tsconfig-utils": 8.60.0(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) "@typescript-eslint/types": 8.60.0 "@typescript-eslint/visitor-keys": 8.60.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) minimatch: 10.2.4 semver: 7.7.3 tinyglobby: 0.2.17 @@ -11942,13 +11942,13 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/utils@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": + "@typescript-eslint/utils@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))": dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@10.4.1(supports-color@8.1.1)) + "@eslint-community/eslint-utils": 4.9.1(eslint@10.4.1(supports-color@7.2.0)) "@typescript-eslint/scope-manager": 8.60.0 "@typescript-eslint/types": 8.60.0 - "@typescript-eslint/typescript-estree": 8.60.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) - eslint: 10.4.1(supports-color@8.1.1) + "@typescript-eslint/typescript-estree": 8.60.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + eslint: 10.4.1(supports-color@7.2.0) typescript: 6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54) transitivePeerDependencies: - supports-color @@ -12020,10 +12020,10 @@ snapshots: "@typescript/native-preview-win32-arm64": 7.0.0-dev.20260626.1 "@typescript/native-preview-win32-x64": 7.0.0-dev.20260626.1 - "@typespec/ts-http-runtime@0.3.2(supports-color@8.1.1)": + "@typespec/ts-http-runtime@0.3.2(supports-color@7.2.0)": dependencies: - http-proxy-agent: 7.0.2(supports-color@8.1.1) - https-proxy-agent: 7.0.6(supports-color@8.1.1) + http-proxy-agent: 7.0.2(supports-color@7.2.0) + https-proxy-agent: 7.0.6(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -12060,6 +12060,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 + "@vitest/mocker@4.1.7(vite@8.0.15(@types/node@25.9.1)(esbuild@0.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0))": + dependencies: + "@vitest/spy": 4.1.7 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.15(@types/node@25.9.1)(esbuild@0.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0) + "@vitest/mocker@4.1.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))": dependencies: "@vitest/spy": 4.1.7 @@ -12427,8 +12435,8 @@ snapshots: agent-base@7.1.4: {} - ajv-formats@2.1.1(ajv@8.17.1): - optionalDependencies: + ajv-formats@2.1.1: + dependencies: ajv: 8.17.1 optional: true @@ -12587,6 +12595,14 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axios@1.13.2(debug@4.4.3(supports-color@7.2.0)): + dependencies: + follow-redirects: 1.15.11(debug@4.4.3(supports-color@7.2.0)) + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axios@1.13.2(debug@4.4.3(supports-color@8.1.1)): dependencies: follow-redirects: 1.15.11(debug@4.4.3(supports-color@8.1.1)) @@ -12594,6 +12610,7 @@ snapshots: proxy-from-env: 1.1.0 transitivePeerDependencies: - debug + optional: true babel-walk@3.0.0-canary-5: dependencies: @@ -12845,17 +12862,24 @@ snapshots: de-indent@1.0.2: {} - debug@3.2.7(supports-color@8.1.1): + debug@3.2.7(supports-color@7.2.0): dependencies: ms: 2.1.3 optionalDependencies: - supports-color: 8.1.1 + supports-color: 7.2.0 + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 + optional: true decimal.js@10.6.0: {} @@ -12902,11 +12926,11 @@ snapshots: denque@1.5.1: {} - dependency-tree@11.2.0(supports-color@8.1.1): + dependency-tree@11.2.0(supports-color@7.2.0): dependencies: commander: 12.1.0 filing-cabinet: 5.0.3 - precinct: 12.2.0(supports-color@8.1.1) + precinct: 12.2.0(supports-color@7.2.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -12951,16 +12975,16 @@ snapshots: detective-stylus@5.0.1: {} - detective-typescript@14.0.0(supports-color@8.1.1)(typescript@5.9.3): + detective-typescript@14.0.0(supports-color@7.2.0)(typescript@5.9.3): dependencies: - "@typescript-eslint/typescript-estree": 8.60.0(supports-color@8.1.1)(typescript@5.9.3) + "@typescript-eslint/typescript-estree": 8.60.0(supports-color@7.2.0)(typescript@5.9.3) ast-module-types: 6.0.1 node-source-walk: 7.0.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - detective-vue2@2.2.0(supports-color@8.1.1)(typescript@5.9.3): + detective-vue2@2.2.0(supports-color@7.2.0)(typescript@5.9.3): dependencies: "@dependents/detective-less": 5.0.1 "@vue/compiler-sfc": 3.5.35 @@ -12968,7 +12992,7 @@ snapshots: detective-sass: 6.0.1 detective-scss: 5.0.1 detective-stylus: 5.0.1 - detective-typescript: 14.0.0(supports-color@8.1.1)(typescript@5.9.3) + detective-typescript: 14.0.0(supports-color@7.2.0)(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -13026,7 +13050,7 @@ snapshots: minimatch: 9.0.1 semver: 7.7.3 - effect@4.0.2: {} + effect@4.0.5: {} electron-to-chromium@1.5.267: optional: true @@ -13237,48 +13261,48 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-import-resolver-node@0.3.9(supports-color@8.1.1): + eslint-import-resolver-node@0.3.9(supports-color@7.2.0): dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@7.2.0) is-core-module: 2.16.1 resolve: 1.22.11 transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint-import-resolver-node@0.3.9(supports-color@7.2.0))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0): dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@7.2.0) optionalDependencies: - "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) - eslint: 10.4.1(supports-color@8.1.1) - eslint-import-resolver-node: 0.3.9(supports-color@8.1.1) + "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + eslint: 10.4.1(supports-color@7.2.0) + eslint-import-resolver-node: 0.3.9(supports-color@7.2.0) transitivePeerDependencies: - supports-color - eslint-plugin-formatjs@6.4.13(eslint@10.4.1(supports-color@8.1.1)): + eslint-plugin-formatjs@6.4.13(eslint@10.4.1(supports-color@7.2.0)): dependencies: "@formatjs/icu-messageformat-parser": 3.5.10 "@formatjs/ts-transformer": 4.4.11 "@types/picomatch": 4.0.2 "@unicode/unicode-17.0.0": 1.6.16 - eslint: 10.4.1(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) magic-string: 0.30.21 picomatch: 4.0.4 transitivePeerDependencies: - ts-jest - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0): dependencies: "@rtsao/scc": 1.1.0 array-includes: 3.1.9 array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3 array.prototype.flatmap: 1.3.3 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@7.2.0) doctrine: 2.1.0 - eslint: 10.4.1(supports-color@8.1.1) - eslint-import-resolver-node: 0.3.9(supports-color@8.1.1) - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) + eslint-import-resolver-node: 0.3.9(supports-color@7.2.0) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint-import-resolver-node@0.3.9(supports-color@7.2.0))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -13290,35 +13314,35 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-sort-destructure-keys@3.0.0(eslint@10.4.1(supports-color@8.1.1)): + eslint-plugin-sort-destructure-keys@3.0.0(eslint@10.4.1(supports-color@7.2.0)): dependencies: - eslint: 10.4.1(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) natural-compare-lite: 1.4.0 - eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1)): + eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0)): dependencies: - eslint: 10.4.1(supports-color@8.1.1) + eslint: 10.4.1(supports-color@7.2.0) optionalDependencies: - "@typescript-eslint/eslint-plugin": 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/eslint-plugin": 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) - eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@8.1.1))(vue-eslint-parser@10.4.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)): + eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)))(eslint@10.4.1(supports-color@7.2.0))(vue-eslint-parser@10.4.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)): dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@10.4.1(supports-color@8.1.1)) - eslint: 10.4.1(supports-color@8.1.1) + "@eslint-community/eslint-utils": 4.9.1(eslint@10.4.1(supports-color@7.2.0)) + eslint: 10.4.1(supports-color@7.2.0) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 semver: 7.7.3 - vue-eslint-parser: 10.4.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1) + vue-eslint-parser: 10.4.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0) xml-name-validator: 4.0.0 optionalDependencies: - "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) + "@typescript-eslint/parser": 8.60.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) eslint-scope@5.1.1: dependencies: @@ -13339,11 +13363,11 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.4.1(supports-color@8.1.1): + eslint@10.4.1(supports-color@7.2.0): dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@10.4.1(supports-color@8.1.1)) + "@eslint-community/eslint-utils": 4.9.1(eslint@10.4.1(supports-color@7.2.0)) "@eslint-community/regexpp": 4.12.2 - "@eslint/config-array": 0.23.5(supports-color@8.1.1) + "@eslint/config-array": 0.23.5(supports-color@7.2.0) "@eslint/config-helpers": 0.6.0 "@eslint/core": 1.2.1 "@eslint/plugin-kit": 0.7.2 @@ -13353,7 +13377,7 @@ snapshots: "@types/estree": 1.0.8 ajv: 6.14.0 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) escape-string-regexp: 4.0.0 eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 @@ -13501,9 +13525,14 @@ snapshots: dependencies: tabbable: 6.4.0 + follow-redirects@1.15.11(debug@4.4.3(supports-color@7.2.0)): + optionalDependencies: + debug: 4.4.3(supports-color@7.2.0) + follow-redirects@1.15.11(debug@4.4.3(supports-color@8.1.1)): optionalDependencies: debug: 4.4.3(supports-color@8.1.1) + optional: true for-each@0.3.5: dependencies: @@ -13717,17 +13746,17 @@ snapshots: html-void-elements@3.0.0: {} - http-proxy-agent@7.0.2(supports-color@8.1.1): + http-proxy-agent@7.0.2(supports-color@7.2.0): dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6(supports-color@8.1.1): + https-proxy-agent@7.0.6(supports-color@7.2.0): dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -14160,13 +14189,13 @@ snapshots: lz-string@1.5.0: {} - madge@8.0.0(supports-color@8.1.1)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)): + madge@8.0.0(supports-color@7.2.0)(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)): dependencies: chalk: 4.1.2 commander: 7.2.0 commondir: 1.0.1 - debug: 4.4.3(supports-color@8.1.1) - dependency-tree: 11.2.0(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) + dependency-tree: 11.2.0(supports-color@7.2.0) ora: 5.4.1 pluralize: 8.0.0 pretty-ms: 7.0.1 @@ -14549,6 +14578,29 @@ snapshots: "@oxlint/binding-win32-x64-msvc": 1.67.0 oxlint-tsgolint: 0.23.0 + oxlint@1.67.0(oxlint-tsgolint@7.0.2001): + optionalDependencies: + "@oxlint/binding-android-arm-eabi": 1.67.0 + "@oxlint/binding-android-arm64": 1.67.0 + "@oxlint/binding-darwin-arm64": 1.67.0 + "@oxlint/binding-darwin-x64": 1.67.0 + "@oxlint/binding-freebsd-x64": 1.67.0 + "@oxlint/binding-linux-arm-gnueabihf": 1.67.0 + "@oxlint/binding-linux-arm-musleabihf": 1.67.0 + "@oxlint/binding-linux-arm64-gnu": 1.67.0 + "@oxlint/binding-linux-arm64-musl": 1.67.0 + "@oxlint/binding-linux-ppc64-gnu": 1.67.0 + "@oxlint/binding-linux-riscv64-gnu": 1.67.0 + "@oxlint/binding-linux-riscv64-musl": 1.67.0 + "@oxlint/binding-linux-s390x-gnu": 1.67.0 + "@oxlint/binding-linux-x64-gnu": 1.67.0 + "@oxlint/binding-linux-x64-musl": 1.67.0 + "@oxlint/binding-openharmony-arm64": 1.67.0 + "@oxlint/binding-win32-arm64-msvc": 1.67.0 + "@oxlint/binding-win32-ia32-msvc": 1.67.0 + "@oxlint/binding-win32-x64-msvc": 1.67.0 + oxlint-tsgolint: 7.0.2001 + oxlint@1.82.0(oxlint-tsgolint@7.0.2001): optionalDependencies: "@oxlint/binding-android-arm-eabi": 1.82.0 @@ -14710,7 +14762,7 @@ snapshots: tar-fs: 2.1.4 tunnel-agent: 0.6.0 - precinct@12.2.0(supports-color@8.1.1): + precinct@12.2.0(supports-color@7.2.0): dependencies: "@dependents/detective-less": 5.0.1 commander: 12.1.0 @@ -14721,8 +14773,8 @@ snapshots: detective-sass: 6.0.1 detective-scss: 5.0.1 detective-stylus: 5.0.1 - detective-typescript: 14.0.0(supports-color@8.1.1)(typescript@5.9.3) - detective-vue2: 2.2.0(supports-color@8.1.1)(typescript@5.9.3) + detective-typescript: 14.0.0(supports-color@7.2.0)(typescript@5.9.3) + detective-vue2: 2.2.0(supports-color@7.2.0)(typescript@5.9.3) module-definition: 6.0.1 node-source-walk: 7.0.1 postcss: 8.5.15 @@ -14982,9 +15034,9 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@8.0.1(supports-color@8.1.1): + require-in-the-middle@8.0.1(supports-color@7.2.0): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) module-details-from-path: 1.0.4 transitivePeerDependencies: - supports-color @@ -15019,17 +15071,17 @@ snapshots: rfdc@1.4.1: {} - rhea-promise@3.0.3(supports-color@8.1.1): + rhea-promise@3.0.3(supports-color@7.2.0): dependencies: - debug: 4.4.3(supports-color@8.1.1) - rhea: 3.0.4(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) + rhea: 3.0.4(supports-color@7.2.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color - rhea@3.0.4(supports-color@8.1.1): + rhea@3.0.4(supports-color@7.2.0): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -15144,7 +15196,7 @@ snapshots: dependencies: "@types/json-schema": 7.0.15 ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) + ajv-formats: 2.1.1 ajv-keywords: 5.1.0(ajv@8.17.1) optional: true @@ -15707,6 +15759,22 @@ snapshots: sass: 1.100.0 terser: 5.45.0 + vite@8.0.15(@types/node@25.9.1)(esbuild@0.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + "@types/node": 25.9.1 + esbuild: 0.27.2 + fsevents: 2.3.3 + sass: 1.100.0 + terser: 5.45.0 + tsx: 4.22.4 + yaml: 2.9.0 + 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: lightningcss: 1.32.0 @@ -15772,6 +15840,35 @@ snapshots: - typescript - universal-cookie + 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.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)): + dependencies: + "@vitest/expect": 4.1.7 + "@vitest/mocker": 4.1.7(vite@8.0.15(@types/node@25.9.1)(esbuild@0.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0)) + "@vitest/pretty-format": 4.1.7 + "@vitest/runner": 4.1.7 + "@vitest/snapshot": 4.1.7 + "@vitest/spy": 4.1.7 + "@vitest/utils": 4.1.7 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.0.15(@types/node@25.9.1)(esbuild@0.27.2)(sass@1.100.0)(terser@5.45.0)(tsx@4.22.4)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + "@opentelemetry/api": 1.9.1 + "@types/node": 25.9.1 + jsdom: 29.1.1 + transitivePeerDependencies: + - msw + 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: "@vitest/expect": 4.1.7 @@ -15838,10 +15935,10 @@ snapshots: vue: 3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54)) vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.35(typescript@6.0.3(patch_hash=d2a2f8424c52b9d75deeb15b1f2367352c31126ecb19c4b469da7b943f946a54))) - vue-eslint-parser@10.4.0(eslint@10.4.1(supports-color@8.1.1))(supports-color@8.1.1): + vue-eslint-parser@10.4.0(eslint@10.4.1(supports-color@7.2.0))(supports-color@7.2.0): dependencies: - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.4.1(supports-color@8.1.1) + debug: 4.4.3(supports-color@7.2.0) + eslint: 10.4.1(supports-color@7.2.0) eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 espree: 11.2.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b83575b6a..1de2ba1bb 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.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 + - "@effect/atom-vue@4.0.5" + - "@effect/platform-browser@4.0.5" + - "@effect/platform-node-shared@4.0.5" + - "@effect/platform-node@4.0.5" + - "@effect/sql-sqlite-node@4.0.5" + - "@effect/vitest@4.0.5" + - effect@4.0.5 overrides: date-fns: ^4.4.0 fast-check: ^4.9.0 From 132c2d63eea5ba9b76e07de3f2c9718f82e2f2b9 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 05:31:29 +0000 Subject: [PATCH 2/6] Squashed 'repos/effect/' changes from 269a7c8643..7f524e25cf 7f524e25cf Version Packages (#9099) 3ad9a0c633 Fix premature RPC socket ping timeouts (#9187) f665607ee8 fix(Array): make Array.match expose NonEmptyArray for mutable arrays (#9055) 0df6e068b6 GraphQL: per-request url function for layerHttp with URL-aware resolver batching (#9150) 7422b9b9af test(cluster): allow residency cap checks headroom on Deno (#9185) 5951ac0c0f feat(platform-node): add typed unix socket and TLS options to NodeHttpClient (#9158) bc454a377d Make String snake_case conversions total and skip empty noCase words (#9183) 0e55b2f3b0 fix(platform-deno): ignore closeWrite on a closed connection (#9162) 3b87d1d1ed Reduce clock polling in shard-release teardown test (#9184) acc7860651 Use a caller-provided WatchBackend for FileSystem.watch (#9181) ec831c3795 Accept Redacted values as HTTP URL params (#9160) 1aecc4250d Update GraphQL ai-docs for generated struct interfaces (#9180) cd74ea2119 Fix lost replies for persisted local cluster requests (#9176) 2a8ec8be83 perf(schema): keep successful decoding defaults resolved (#9166) d1ad2c71a0 perf(ai): reuse language model response decoders (#9169) f9f43352d6 perf(ai-openai-compat): filter SSE events without child streams (#9167) a0ad8d3a4f perf(ai-openai-compat): emit stream part chunks directly (#9168) f2b3d8a573 Fix TestClock skipping sleeps registered after multi-hop wakeups (#9172) cb2cf5f19d Expose attempt and retry metadata on `Effect.Transaction` (#8166) f7deebc16f Document the browser CORS failure from `b3` and `traceparent` (#9152) ec9332ba2b fix: remove interrupted TestClock sleeps and avoid queue sorting (#9173) b44c2fbd4d Reduce GraphQL generated schema type-checking overhead (#9171) 995c104f7b Fix Duration losing nanosecond precision for large integer millisecond values (#9161) 793d89efa9 ai: support structured decision instructions and descriptions (#9124) a03afb6147 Add Flag.withOptionalValue for CLI flags with an optional value (#9145) e24911e266 Add pull request finalization skill (#9153) c2859bf017 perf(ai): collect tool approvals once per request (#9143) 738bc873a7 perf(ai-openai-compat): return single text parts directly (#9144) e271997a14 Fix MCP resource and prompt schema service requirements (#9151) 742976ce97 Capture PostgreSQL teardown diagnostics in Deno CI (#9142) 91f4c74fe8 ai-openai: add OpenAiDecisionModel for OpenAI's Decisions API (#9140) 12be65e5e3 Redact sensitive query params in spans, inspect output and errors (#9136) 563397fbb8 Add opt-in gzip compression to OTLP exporters (#9139) 3abc63eae4 Add HttpMiddleware.decompression for compressed request bodies (#9138) 52a7d3c8d3 Add HttpClientRequest body compression (#9137) 8dec89d318 Fix resource template completion service requirements (#9135) f159284375 Fix deferred callback wakes before parent workflow suspension (#9131) c462214483 Test SQLite statement reuse after failed-COMMIT cleanup retry (#9134) b8c201d0f4 Add detached option for discarded workflow executions (#9092) 118c9c4fe5 Keep failure-suspended workflows parked until resumed or interrupted (#9091) f05642f777 Fix HttpRouter prefixes with path params breaking request.url (#9118) 73efe552d9 perf(sql-sqlite-do): borrow the connection for statements (#9130) 65d20dd6e8 Wait for GraphQL watcher installation before generating (#9133) bd2c7a3b8b ai-cloudflare: send decision images to Clef (#9121) 1cd7df8a98 feat(sql-pg): expose TCP keepalive settings (#9120) eff9beeee3 Fix Deno CI crashes by pinning v2.9.7 (#9132) dbf442b914 Fix AsyncResult.all previousSuccess shape for failed inputs (#9110) e3ff963eb5 End Stream.timeout while the next chunk is still pending (#9106) a7ce81f601 Record the active span on OpenTelemetry log records (#9125) 7719bda8a5 Fix OTLP export dropping a whole batch when a span has a 64-bit B3 trace id (#9117) 2c5199fcfd fix(sqlite-node): retry enabling WAL on Deno when the database is locked (#9108) 65b04f87b7 Add Path.resolveWithin for root-contained path resolution (#9127) a841b8b144 Stop interruption from hashing causes and looping on throwing hashes (#9113) 816575e11c Use bigint for 64-bit FileSystem stat metadata (#9112) 5d018e1fb1 fix(ai-openai): preserve ultrafast service tier metadata (#9115) 54c51d9e96 Fix Duration conversions throwing for very large millisecond durations (#9109) 74d19304da Fix atom rebuild loops from late dependency reads (#9105) dba4e52f50 fix(schema): reject duplicate tagged union discriminants (#9103) 91efea7860 Keep the unmatched tail in Match array prefix patterns (#9098) 3b7237aef5 fix(sql-pg): restore inferred empty array binding (#9102) 925cf641c7 Fix dotenv variable expansion scanning expanded text again (#9095) 68caf1d642 fix(ai-openai): end a Responses event stream at data: [DONE] (#9097) 7ca8224ebb Fix Sse encoder dropping data after a lone carriage return (#9096) 5d301cc7aa fix(cluster): use entity type as span prefix instead of type+id (#9094) 51c079b90b fix(ai-openai): accept web search actions without patterns (#9100) 352618a562 Reject unknown keys in Match.when object patterns (#9088) e17ecdbe90 Version Packages (#8993) ab04ad0622 fix(sql-pg): invalidate released pins so they cannot reach the next borrower (#9089) ab90be98ea feat: GraphQL client and generator (#8979) 1bfd8fddeb fix(Schema): merge colliding bounds in JSON Schema export instead of emitting allOf (#9081) 711719d28d Bound import-time stack detection probes (#9083) 6c824e0e34 Stage npm releases and promote latest once every package is visible (#9073) e571748085 Relax timeout for compiler-backed api-diff snapshot tests (#9079) e8751695bf Revert "Expand Crypto with hashing, encryption, signing, and key management" (#9077) 09738f4aa6 Honor Retry-After in HttpClient.retryTransient (#9053) de38e53651 Allow OpenAI find_in_page web search actions without a URL (#9075) 984b4abfb2 Regenerate the OpenAI client from the canonical OpenAI spec (#9067) f41a60f59b Wait for PostgreSQL containers to stop before removal (#9074) 797696a2b7 Merge outer headers into a raw Response with immutable headers in HttpServerResponse.toWeb (#9062) 211ea2efec fix(sqlite-node): support node:sqlite without enableLoadExtension (#9070) 892d14d88b perf(Sse): stop re-copying long lines split across chunks (#9065) fdcb47e643 Fix Sharding storage-loop initialization order (#9069) 077c55770d Preserve HTTP server spans during runtime shutdown (#9066) 83df573a79 Refresh migration guidance for terminal, OpenAI and Workers defaults (#9071) 7547a99f81 Bound sparse bracket-path array indices in SchemaGetter.makeTreeRecord (#9030) 8aee3b9caa perf(ai-openai-compat): reuse prepared tool parameter codecs (#9054) b7c1ac6837 Fix streamed prompt ordering and preserve partial assistant text (#9056) 8ae4d04359 Isolate Sharding reset claim-expiry regression from concurrent tests (#9057) 0d92a92ab2 Expand Crypto with hashing, encryption, signing, and key management (#8749) fc349a13f7 Fix Sink.takeUntil dropping the input after the match (#9049) a59b9711af Read Retry-After HTTP dates as dates in withRateLimiter (#9052) a0a26b789c Fix DateTime.add treating hours and minutes as wall-clock time across DST (#9048) 390bc98756 Fix StructWithRest primitive-encoded base constraint (#9020) cea74c92ac Persist cluster sends before local shard lock acquisition (#9046) 8f1ea52f0c Fix grouped RequestResolver batch key retention (#9041) fccf660d83 Add request-scoped MCP HTTP layer (#9031) e1c6053c5e Release aborted web handler requests and streamed bodies (#9037) 7440cf0884 Document Cloudflare Workers shared-fiber limitations (#9033) 4a999a34e8 Clean up Anthropic documentation imports and omitted binding (#9029) eebdf25f99 Batch SQLite Node Resolver fixture inserts (#9028) 5d88d8761d Update repository dependencies (#9027) 689fec6543 Avoid busy polling in Sharding claim-expiry tests (#9022) 22e7129023 Expose Scalar preferred authentication security scheme configuration (#9019) 78e4c435ed Fix Multipart.toPersisted failing on dot and long multi-byte file names (#9013) 5726c655af fix(platform-node): skip taskkill for exited children (#9015) 8b097ac4ba Stabilize cross-queue deduplication delivery assertion (#9021) 95a3ab78e9 Fix Node HTTP text keeping a UTF-8 BOM that breaks json (#9014) 52c91f0080 Keep MSSQL error listener after connection cleanup (#9018) 844a77b18c Fix assistant replay with legacy OpenAI compat config (#9011) cf5179b488 Wake suspended workflow callers when their workflow completes (#9007) 42aceb966a Replay assistant reasoning alongside tool calls in openai-compat (#9005) cc843ea091 Return invalid and undeclared AI tool calls when tool call validation is deferred (#9008) c93485e365 Support servername in client TLS socket upgrades (#9010) 93146666ec perf(Context): optimize pick for zero or one service (#8998) 251f849155 Fix unhandled errors after child process input interruption (#9004) f8379667c9 Release losing DurableDeferred.raceAll waits when the race settles (#8997) 35ec46efd0 Remove fork approval gate from release queue (#8996) 37e088f2af Use decoded response aliases and canonical finite schemas in code generation (#8990) 118deefe7c Add Host.toFullyQualified, Host.toRelative, and Host.parseHostsFile (#8992) fde67a10e9 Add NetAddress.loopbackAddresses (#8991) 967a7dc596 Version Packages (#8887) 4fcdede9a4 Clarify native Array.fromAsync in Stream examples (#8989) 84f48df586 Omit cumulative min/max from delta histogram points in OtlpMetrics (#8987) 1d3419e76c Fix histogram export dropping or duplicating the overflow bucket (#8986) b4e5af825a fix(effect): handle synchronous re-entrancy in Effect.cached* (#8977) b8d6e7a89b cli: export the terminal display-width measure as CliOutput.displayWidth (#8982) 43bb382f68 fix(ai-openai): default unrecognized models to reasoning and preserve explicit includes (#8980) b630acdb8c Revert "Add an explicit maxDepth option to Context.makeUnsafe (#8969)" 0b17b9e2e4 Support WebSocket handshake headers alongside subprotocols (#8978) f9f7a5a16e feat(rpc): report socket ping timeouts through an onPingTimeout option (#8976) 4b1f4aa8ad Add an explicit maxDepth option to Context.makeUnsafe (#8969) 1db5671d77 fix(ai-anthropic): decode replies a fallback model took over (#8971) 76a03f7599 Fix Duration.times throwing for non-integer multipliers of nanosecond durations (#8972) e052f1b30b Avoid copying the Context rebase map twice (#8968) f7c4317345 Test DateTime.toDate wall-clock conversion for zoned values (#8975) 9dbd07e006 Accept address and name server inputs in the Dns services (#8973) e7d84e449a Add fromInput functions to the net data types (#8970) 66257d2922 Hand cluster shards off one at a time on shutdown (#8941) f54c34a754 Restore lease-based SQL shard lock renewal (#8965) bfc5742e18 Record RPC response-encoding failures on the server span (#8958) aef1d1f2e5 Add cluster RPC interrupt-on-termination annotation (#8963) 210fd06398 Keep shard locks held while their runner shuts down (#8961) cd2ab19600 Route shard handoff interrupts to the draining owner and wait for shard locks (#8940) 73fa2873cb Fix safe integer bounds in JSON Schema export (#8956) ae63afed95 Reclaim row shard locks whose holder is no longer a registered runner (#8942) ec8ccb92a4 Document Schema class field overrides (#8960) aa811e454b Failing test: RPC success encode failures are not logged with the RPC tag (#8943) 93410da528 Add stderr terminal detection to Stdio (#8938) 310228c2be Allow scheduler 0.28 in atom-react peers (#8954) b63a4f9ac7 Fix ai-openai-compat splitting streamed reasoning on empty content deltas (#8952) 7ba976c620 Fix interrupted activity replay in memory workflow engine (#8955) d4f7dc3300 Add TLSA records to DNS services (#8953) a9b5148225 Add DNS resolution to effect/net (#8880) 7ff5048db5 Skip continuation-mark probing when stack traces are disabled (#8937) 63bfad88fc Keep durable race and into continuations running beside a parked workflow sibling (#8926) e00eaae8d9 Fix lazy terminal input acquisition for CLI prompts (#8932) 89d5c88810 Fix NO_COLOR handling in CLI prompts and wizard (#8933) ebe487f888 Support configurable Crypto UUID output formats (#8935) 8e5ab22a42 Fix Esc cancellation for CLI prompts (#8934) b210b6be1f Refresh migration guidance for inferred services and telemetry (#8928) 7803e51e94 Hold the shared SQL connection for the whole statement (#8927) 3b934343a0 Start Effect spans with the active OpenTelemetry parent span itself (#8929) d6d515bafc Honor reported remaining budgets in HttpClient rate limiting (#8917) 0a6f270e21 Allow NodeTerminal fixtures time to start under CI load (#8930) 8598157653 Fix Duration.fromInputUnsafe throwing on infinite nanosecond totals (#8921) a494a64788 Allow HTTP MCP servers to disable subscriptions (#8924) ee3a350768 Fix spurious MCP list-change notifications on first subscription (#8925) 950ea4eeb2 Preserve tool call extra_content in ai-openai-compat (#8916) 7e444029d3 fix(http): ignore B3 headers whose ids are not hex (#8913) 0e487c027a Fix Effect spans losing a getter-based OpenTelemetry parent (#8922) 13ace20e6c Update migration bundle size figures (#8911) c755b69701 Add a deterministic TestCrypto layer (#8487) 788eced019 Keep the test clock advancing through PersistedQueue cleanup completion (#8910) 48ed32f846 Add delay option to PersistedQueue.offer (#8907) be560ff3ab Fix Bun worker graceful shutdown without self.close (#8909) 59e58a6b98 Add PowerShell support to --completions (#8904) 7c5133c342 disable experimentalApiUsage b9e7821be3 Add DatagramSocket (UDP) module (#8409) 182e26dae1 Add FileSystem noFollow open option and positional reads (#8900) 738d81f19e Simplify SchemaError serialization (#8903) 0e2b988eec Fix orphaned workflow activity recovery after a crash (#8899) ca068cef44 Allow cluster SQL storage to run without runtime DDL (#8897) 9753be9ecf Deliver cluster WithTransaction replies after the transaction outcome is known (#8891) f686000760 Add compact SchemaError JSON and inspect output (#8895) ea52c245f6 Fix typed SQL transaction COMMIT error recovery (#8892) d6082a25a3 Send MCP cancellation notifications for interrupted reverse requests (#8890) 977096bcea Persist cluster WithTransaction failure replies after rollback (#8886) 133ceb2b77 Answer cancelled MCP HTTP request POSTs with an empty event stream (#8888) d418ce011a test(encoding): cover Base64 binary and padding contracts (#8889) a46592aed9 Preserve query URLs during HTTP router slash normalization (#8885) cf58cb0362 Fix Zsh completions for flag descriptions containing `]` (#8882) git-subtree-dir: repos/effect git-subtree-split: 7f524e25cfb49a71ac39b0f44278d08d40dda5de --- {repos/effect/.agents => .agents}/AGENTS.md | 0 .../skills/ai-docs/SKILL.md | 0 .../skills/bundle-analysis/SKILL.md | 0 .../skills/changesets/SKILL.md | 0 .../skills/changesets/authoring.md | 0 .../skills/ci-maintenance/SKILL.md | 0 .../ci-maintenance/privileged-workflows.md | 0 .../skills/dependency-maintenance/SKILL.md | 0 .../coordinated-upgrades.md | 0 .../dependency-maintenance/manifest-roles.md | 0 .../skills/effect-development/SKILL.md | 0 .../skills/jsdocs/SKILL.md | 0 .../skills/jsdocs/categories.md | 0 .../skills/jsdocs/declarations.md | 0 .../skills/jsdocs/examples.md | 0 .../skills/migration-guidance/SKILL.md | 0 .../skills/migration-guidance/annotations.md | 0 .../skills/migration-guidance/generation.md | 0 .../skills/package-development/SKILL.md | 0 .../package-development/dependencies.md | 0 .../skills/package-development/publishing.md | 0 .../package-development/registration.md | 0 .../skills/performance-analysis/SKILL.md | 0 .../skills/performance-analysis/runtime.md | 0 .../skills/performance-analysis/types.md | 0 .agents/skills/pull-requests/SKILL.md | 50 + .../skills/test-development/SKILL.md | 0 .../test-development/displayed-types.md | 0 .../skills/test-development/runtime.md | 0 .../skills/test-development/types.md | 0 .../skills/vendored-assets/SKILL.md | 0 .../skills/vendored-assets/browser-assets.md | 0 .changeset/README.md | 8 - .changeset/config.json | 59 +- .changeset/update-effect-4.0.2.md | 6 - .claude/settings.json | 18 - .devcontainer/Dockerfile | 42 - .devcontainer/devcontainer.json | 14 - .devcontainer/humanlog.sh | 2 - .devcontainer/usepnpm.sh | 26 - .envrc | 4 - .githooks/README.md | 49 - .githooks/post-checkout | 98 - .githooks/pre-commit | 12 - .githooks/pre-push | 17 - .github/FUNDING.yml | 3 - .../actions/setup/action.yaml | 0 .github/npm-trusted-publishing.md | 85 - .../workflows/ai-codegen.yml | 0 .../workflows/bundle-comment.yml | 0 .../.github => .github}/workflows/check.yml | 37 +- .github/workflows/ci.yml | 179 - .github/workflows/claude.yml | 49 - .../.github => .github}/workflows/cluster.yml | 0 .../workflows/effect-bot.yml | 0 .github/workflows/npm-promote.yml | 67 + .github/workflows/publish-snapshot.yml | 105 - .github/workflows/publish-wiki.yml | 18 - .../workflows/release-queue.yml | 18 +- .github/workflows/release.yaml | 94 - .../.github => .github}/workflows/release.yml | 10 +- .../workflows/snapshot.yml | 0 .gitignore | 89 +- .ncurc.json | 20 - .npmrc | 1 - .nvmrc | 1 - .oxlintrc.json | 69 +- .specs/Update-to-latest-Effect.md | 23 - .specs/changeset-publishing.md | 138 - .vscode/extensions.json | 5 +- .vscode/launch.json | 22 - .vscode/model.code-snippets | 77 - .vscode/service.code-snippets | 14 - .vscode/settings.json | 84 +- .vscode/tasks.json | 38 - AGENTS.md | 217 - CLAUDE.md | 3 - repos/effect/LICENSE => LICENSE | 0 repos/effect/LLMS.md => LLMS.md | 24 + repos/effect/MIGRATION.md => MIGRATION.md | 5 +- README.md | 211 +- {repos/effect/ai-docs => ai-docs}/README.md | 0 .../effect/ai-docs => ai-docs}/package.json | 4 +- .../src/01_effect/01_basics/01_effect-gen.ts | 0 .../src/01_effect/01_basics/02_effect-fn.ts | 0 .../01_basics/10_creating-effects.ts | 0 .../src/01_effect/01_basics/index.md | 0 .../01_effect/02_schema/10_schema-basics.ts | 0 .../src/01_effect/02_schema/index.md | 0 .../src/01_effect/03_services/01_service.ts | 0 .../src/01_effect/03_services/10_reference.ts | 0 .../03_services/20_layer-composition.ts | 0 .../01_effect/03_services/20_layer-unwrap.ts | 0 .../src/01_effect/03_services/index.md | 0 .../01_effect/04_errors/01_error-handling.ts | 0 .../src/01_effect/04_errors/10_catch-tags.ts | 0 .../01_effect/04_errors/20_reason-errors.ts | 0 .../src/01_effect/04_errors/index.md | 0 .../05_resources/10_acquire-release.ts | 0 .../05_resources/20_layer-side-effects.ts | 0 .../01_effect/05_resources/30_layer-map.ts | 0 .../src/01_effect/05_resources/index.md | 0 .../src/01_effect/06_running/10_run-main.ts | 0 .../01_effect/06_running/20_layer-launch.ts | 0 .../src/01_effect/06_running/index.md | 0 .../src/01_effect/07_pubsub/10_pubsub.ts | 0 .../src/01_effect/07_pubsub/index.md | 0 .../src/03_stream/10_creating-streams.ts | 0 .../src/03_stream/20_consuming-streams.ts | 0 .../src/03_stream/30_encoding.ts | 0 .../src/03_stream/index.md | 0 .../src/04_integration/10_managed-runtime.ts | 0 .../src/04_integration/index.md | 0 .../src/05_batching/10_request-resolver.ts | 0 .../src/05_batching/index.md | 0 .../src/06_schedule/10_schedules.ts | 0 .../src/06_schedule/index.md | 0 .../07_datetime/10_creating-and-formatting.ts | 0 .../src/07_datetime/20_time-zones.ts | 0 .../src/07_datetime/index.md | 0 .../src/08_observability/10_logging.ts | 0 .../src/08_observability/20_otlp-tracing.ts | 0 .../src/08_observability/index.md | 0 .../src/09_testing/10_effect-tests.ts | 0 .../src/09_testing/20_layer-tests.ts | 0 .../src/09_testing/index.md | 0 .../src/10_predicate/01_basics.ts | 0 .../src/10_predicate/index.md | 0 .../src/40_sql/10_basics.ts | 0 .../ai-docs => ai-docs}/src/40_sql/index.md | 0 .../src/50_http-client/10_basics.ts | 0 .../src/50_http-client/index.md | 0 .../src/51_http-server/10_basics.ts | 0 .../src/51_http-server/20_testing.ts | 0 .../src/51_http-server/fixtures/api/Api.ts | 0 .../fixtures/api/Authorization.ts | 0 .../src/51_http-server/fixtures/api/System.ts | 0 .../src/51_http-server/fixtures/api/Users.ts | 0 .../51_http-server/fixtures/domain/User.ts | 0 .../fixtures/domain/UserErrors.ts | 0 .../fixtures/server/Authorization.ts | 0 .../51_http-server/fixtures/server/Users.ts | 0 .../fixtures/server/Users/http.ts | 0 .../src/51_http-server/index.md | 0 ai-docs/src/52_graphql/10_basics.ts | 117 + ai-docs/src/52_graphql/20_testing.ts | 45 + ai-docs/src/52_graphql/30_batching.ts | 118 + ai-docs/src/52_graphql/40_aliased-batching.ts | 124 + .../src/52_graphql/50_errors-and-retries.ts | 74 + ai-docs/src/52_graphql/index.md | 6 + .../10_working-with-child-processes.ts | 0 .../src/60_child-process/index.md | 0 .../src/70_cli/10_basics.ts | 0 .../ai-docs => ai-docs}/src/70_cli/index.md | 0 .../src/71_ai/10_language-model.ts | 0 .../ai-docs => ai-docs}/src/71_ai/20_tools.ts | 0 .../ai-docs => ai-docs}/src/71_ai/30_chat.ts | 0 .../src/71_ai/fixtures/domain/LaunchPlan.ts | 0 .../ai-docs => ai-docs}/src/71_ai/index.md | 0 .../src/80_cluster/10_entities.ts | 0 .../src/80_cluster/index.md | 0 .../effect/ai-docs => ai-docs}/src/index.md | 0 .../effect/ai-docs => ai-docs}/tsconfig.json | 0 assets/dev/effect-app-apple-touch-180.png | Bin 11603 -> 0 bytes repos/effect/deno.json => deno.json | 2 +- repos/effect/dprint.json => dprint.json | 1 + dprint.jsonc | 6 - flake.lock | 46 +- flake.nix | 47 +- .../jsdocs.config.json => jsdocs.config.json | 0 lint-staged.config.js | 29 - .../annotations/README.md | 0 .../annotations/effect__Arbitrary.yaml | 0 .../annotations/effect__Array.yaml | 0 .../annotations/effect__BigDecimal.yaml | 0 .../annotations/effect__BigInt.yaml | 0 .../annotations/effect__Brand.yaml | 0 .../annotations/effect__Cache.yaml | 0 .../annotations/effect__Cause.yaml | 0 .../annotations/effect__Channel.yaml | 2 +- .../effect__ChildExecutorDecision.yaml | 0 .../annotations/effect__Chunk.yaml | 0 .../annotations/effect__Clock.yaml | 0 .../annotations/effect__Config.yaml | 0 .../annotations/effect__ConfigError.yaml | 0 .../annotations/effect__ConfigProvider.yaml | 0 .../effect__ConfigProviderPathPatch.yaml | 0 .../annotations/effect__Console.yaml | 0 .../annotations/effect__Context.yaml | 0 .../annotations/effect__Cron.yaml | 0 .../annotations/effect__Data.yaml | 0 .../annotations/effect__DateTime.yaml | 10 +- .../annotations/effect__DefaultServices.yaml | 0 .../annotations/effect__Deferred.yaml | 0 .../annotations/effect__Differ.yaml | 0 .../annotations/effect__Duration.yaml | 0 .../annotations/effect__Effect.yaml | 0 .../annotations/effect__Effectable.yaml | 0 .../annotations/effect__Either.yaml | 0 .../annotations/effect__Encoding.yaml | 0 .../annotations/effect__Equal.yaml | 0 .../annotations/effect__Equivalence.yaml | 0 .../annotations/effect__ExecutionPlan.yaml | 9 + .../effect__ExecutionStrategy.yaml | 0 .../annotations/effect__Exit.yaml | 0 .../annotations/effect__FastCheck.yaml | 0 .../annotations/effect__Fiber.yaml | 0 .../annotations/effect__FiberHandle.yaml | 0 .../annotations/effect__FiberId.yaml | 0 .../annotations/effect__FiberMap.yaml | 0 .../annotations/effect__FiberRef.yaml | 0 .../annotations/effect__FiberRefs.yaml | 0 .../annotations/effect__FiberRefsPatch.yaml | 0 .../annotations/effect__FiberSet.yaml | 0 .../annotations/effect__FiberStatus.yaml | 0 .../annotations/effect__Function.yaml | 0 .../annotations/effect__GlobalValue.yaml | 0 .../annotations/effect__Graph.yaml | 0 .../annotations/effect__GroupBy.yaml | 0 .../annotations/effect__Hash.yaml | 0 .../annotations/effect__HashMap.yaml | 0 .../annotations/effect__HashSet.yaml | 0 .../annotations/effect__Inspectable.yaml | 0 .../annotations/effect__Iterable.yaml | 0 .../annotations/effect__JSONSchema.yaml | 0 .../annotations/effect__KeyedPool.yaml | 0 .../annotations/effect__Layer.yaml | 0 .../annotations/effect__LayerMap.yaml | 0 .../annotations/effect__List.yaml | 0 .../annotations/effect__LogLevel.yaml | 0 .../annotations/effect__LogSpan.yaml | 0 .../annotations/effect__Logger.yaml | 0 .../annotations/effect__Mailbox.yaml | 0 .../annotations/effect__ManagedRuntime.yaml | 0 .../annotations/effect__Match.yaml | 0 .../annotations/effect__MergeDecision.yaml | 0 .../annotations/effect__MergeState.yaml | 0 .../annotations/effect__MergeStrategy.yaml | 0 .../annotations/effect__Metric.yaml | 0 .../annotations/effect__MetricBoundaries.yaml | 0 .../annotations/effect__MetricHook.yaml | 0 .../annotations/effect__MetricKey.yaml | 0 .../annotations/effect__MetricKeyType.yaml | 0 .../annotations/effect__MetricLabel.yaml | 0 .../annotations/effect__MetricPair.yaml | 0 .../annotations/effect__MetricPolling.yaml | 0 .../annotations/effect__MetricRegistry.yaml | 0 .../annotations/effect__MetricState.yaml | 0 .../annotations/effect__Micro.yaml | 0 .../annotations/effect__ModuleVersion.yaml | 0 .../annotations/effect__MutableHashMap.yaml | 0 .../annotations/effect__MutableHashSet.yaml | 0 .../annotations/effect__MutableList.yaml | 0 .../annotations/effect__MutableQueue.yaml | 0 .../annotations/effect__MutableRef.yaml | 0 .../annotations/effect__Number.yaml | 0 .../annotations/effect__Option.yaml | 0 .../annotations/effect__Order.yaml | 0 .../annotations/effect__Ordering.yaml | 0 .../annotations/effect__ParseResult.yaml | 0 .../effect__PartitionedSemaphore.yaml | 0 .../annotations/effect__Pipeable.yaml | 0 .../annotations/effect__Pool.yaml | 0 .../annotations/effect__Predicate.yaml | 0 .../annotations/effect__Pretty.yaml | 0 .../annotations/effect__PubSub.yaml | 0 .../annotations/effect__Queue.yaml | 0 .../annotations/effect__Random.yaml | 0 .../annotations/effect__RateLimiter.yaml | 0 .../annotations/effect__RcMap.yaml | 0 .../annotations/effect__RcRef.yaml | 0 .../annotations/effect__Readable.yaml | 0 .../annotations/effect__Record.yaml | 0 .../annotations/effect__RedBlackTree.yaml | 0 .../annotations/effect__Redacted.yaml | 0 .../annotations/effect__Ref.yaml | 0 .../annotations/effect__Reloadable.yaml | 0 .../annotations/effect__Request.yaml | 0 .../annotations/effect__RequestBlock.yaml | 0 .../annotations/effect__RequestResolver.yaml | 0 .../annotations/effect__Resource.yaml | 0 .../annotations/effect__Runtime.yaml | 0 .../annotations/effect__RuntimeFlags.yaml | 0 .../effect__RuntimeFlagsPatch.yaml | 0 .../annotations/effect__STM.yaml | 0 .../annotations/effect__Schedule.yaml | 0 .../annotations/effect__ScheduleDecision.yaml | 0 .../annotations/effect__ScheduleInterval.yaml | 0 .../effect__ScheduleIntervals.yaml | 0 .../annotations/effect__Scheduler.yaml | 2 +- .../annotations/effect__Schema.yaml | 0 .../annotations/effect__SchemaAST.yaml | 0 .../annotations/effect__Scope.yaml | 0 .../annotations/effect__ScopedCache.yaml | 0 .../annotations/effect__ScopedRef.yaml | 0 .../annotations/effect__Secret.yaml | 0 .../effect__SingleProducerAsyncInput.yaml | 0 .../annotations/effect__Sink.yaml | 0 .../annotations/effect__SortedMap.yaml | 0 .../annotations/effect__SortedSet.yaml | 0 .../annotations/effect__Stream.yaml | 0 .../annotations/effect__StreamEmit.yaml | 0 .../effect__StreamHaltStrategy.yaml | 0 .../annotations/effect__Streamable.yaml | 0 .../annotations/effect__Struct.yaml | 0 .../annotations/effect__Subscribable.yaml | 0 .../annotations/effect__SubscriptionRef.yaml | 0 .../annotations/effect__Supervisor.yaml | 0 .../annotations/effect__Symbol.yaml | 0 .../annotations/effect__SynchronizedRef.yaml | 0 .../annotations/effect__TArray.yaml | 0 .../annotations/effect__TDeferred.yaml | 0 .../annotations/effect__TMap.yaml | 0 .../annotations/effect__TPriorityQueue.yaml | 0 .../annotations/effect__TPubSub.yaml | 0 .../annotations/effect__TQueue.yaml | 0 .../annotations/effect__TRandom.yaml | 0 .../annotations/effect__TReentrantLock.yaml | 0 .../annotations/effect__TRef.yaml | 0 .../annotations/effect__TSemaphore.yaml | 0 .../annotations/effect__TSet.yaml | 0 .../annotations/effect__TSubscriptionRef.yaml | 0 .../annotations/effect__Take.yaml | 0 .../annotations/effect__TestAnnotation.yaml | 0 .../effect__TestAnnotationMap.yaml | 0 .../annotations/effect__TestAnnotations.yaml | 0 .../annotations/effect__TestClock.yaml | 0 .../annotations/effect__TestConfig.yaml | 0 .../annotations/effect__TestContext.yaml | 0 .../annotations/effect__TestLive.yaml | 0 .../annotations/effect__TestServices.yaml | 0 .../annotations/effect__TestSized.yaml | 0 .../annotations/effect__Tracer.yaml | 0 .../annotations/effect__Trie.yaml | 0 .../annotations/effect__Tuple.yaml | 0 .../annotations/effect__Types.yaml | 0 .../effect__UpstreamPullRequest.yaml | 0 .../effect__UpstreamPullStrategy.yaml | 0 .../annotations/effect__Utils.yaml | 0 .../effect__ai-amazon-bedrock.yaml | 0 ...i-amazon-bedrock__AmazonBedrockClient.yaml | 0 ...i-amazon-bedrock__AmazonBedrockConfig.yaml | 0 ...n-bedrock__AmazonBedrockLanguageModel.yaml | 0 ...i-amazon-bedrock__AmazonBedrockSchema.yaml | 0 ..._ai-amazon-bedrock__AmazonBedrockTool.yaml | 0 ...i-amazon-bedrock__EventStreamEncoding.yaml | 0 .../effect__ai-amazon-bedrock__index.yaml | 0 ...effect__ai-anthropic__AnthropicClient.yaml | 0 ...effect__ai-anthropic__AnthropicConfig.yaml | 0 ..._ai-anthropic__AnthropicLanguageModel.yaml | 0 ...ect__ai-anthropic__AnthropicTokenizer.yaml | 0 .../effect__ai-anthropic__AnthropicTool.yaml | 0 .../effect__ai-anthropic__Generated.yaml | 0 .../effect__ai-anthropic__index.yaml | 0 .../annotations/effect__ai-google.yaml | 0 .../effect__ai-google__Generated.yaml | 0 .../effect__ai-google__GoogleClient.yaml | 0 .../effect__ai-google__GoogleConfig.yaml | 0 ...ffect__ai-google__GoogleLanguageModel.yaml | 0 .../effect__ai-google__GoogleTool.yaml | 0 .../annotations/effect__ai-google__index.yaml | 0 .../effect__ai-openai__Generated.yaml | 0 .../effect__ai-openai__OpenAiClient.yaml | 0 .../effect__ai-openai__OpenAiConfig.yaml | 0 ...fect__ai-openai__OpenAiEmbeddingModel.yaml | 0 ...ffect__ai-openai__OpenAiLanguageModel.yaml | 4 +- .../effect__ai-openai__OpenAiTelemetry.yaml | 4 + .../effect__ai-openai__OpenAiTokenizer.yaml | 0 .../effect__ai-openai__OpenAiTool.yaml | 0 .../annotations/effect__ai-openai__index.yaml | 0 .../effect__ai-openrouter__Generated.yaml | 0 ...fect__ai-openrouter__OpenRouterClient.yaml | 0 ...fect__ai-openrouter__OpenRouterConfig.yaml | 0 ...i-openrouter__OpenRouterLanguageModel.yaml | 0 .../effect__ai-openrouter__index.yaml | 0 .../annotations/effect__ai.yaml | 0 .../annotations/effect__ai__AiError.yaml | 0 .../annotations/effect__ai__Chat.yaml | 0 .../effect__ai__EmbeddingModel.yaml | 0 .../annotations/effect__ai__IdGenerator.yaml | 0 .../effect__ai__LanguageModel.yaml | 0 .../annotations/effect__ai__McpSchema.yaml | 0 .../annotations/effect__ai__McpServer.yaml | 8 +- .../annotations/effect__ai__Model.yaml | 0 .../annotations/effect__ai__Prompt.yaml | 0 .../annotations/effect__ai__Response.yaml | 0 .../annotations/effect__ai__Telemetry.yaml | 4 + .../annotations/effect__ai__Tool.yaml | 0 .../annotations/effect__ai__Toolkit.yaml | 0 .../annotations/effect__ai__index.yaml | 0 .../annotations/effect__cli.yaml | 0 .../annotations/effect__cli__Args.yaml | 0 .../annotations/effect__cli__AutoCorrect.yaml | 0 .../effect__cli__BuiltInOptions.yaml | 0 .../annotations/effect__cli__CliApp.yaml | 0 .../annotations/effect__cli__CliConfig.yaml | 0 .../annotations/effect__cli__Command.yaml | 0 .../effect__cli__CommandDescriptor.yaml | 0 .../effect__cli__CommandDirective.yaml | 0 .../annotations/effect__cli__ConfigFile.yaml | 0 .../annotations/effect__cli__HelpDoc.yaml | 0 .../effect__cli__HelpDoc__Span.yaml | 0 .../annotations/effect__cli__Options.yaml | 0 .../annotations/effect__cli__Primitive.yaml | 0 .../annotations/effect__cli__Prompt.yaml | 0 .../annotations/effect__cli__Usage.yaml | 0 .../effect__cli__ValidationError.yaml | 0 .../annotations/effect__cli__index.yaml | 0 .../annotations/effect__cluster.yaml | 0 .../effect__cluster__ClusterCron.yaml | 0 .../effect__cluster__ClusterError.yaml | 0 .../effect__cluster__ClusterSchema.yaml | 0 ...ffect__cluster__ClusterWorkflowEngine.yaml | 0 .../effect__cluster__DeliverAt.yaml | 0 .../annotations/effect__cluster__Entity.yaml | 0 .../effect__cluster__EntityAddress.yaml | 0 .../effect__cluster__EntityId.yaml | 0 .../effect__cluster__EntityProxy.yaml | 0 .../effect__cluster__EntityProxyServer.yaml | 0 .../effect__cluster__EntityResource.yaml | 0 .../effect__cluster__Envelope.yaml | 0 .../effect__cluster__HttpRunner.yaml | 0 .../effect__cluster__K8sHttpClient.yaml | 0 .../effect__cluster__MachineId.yaml | 0 .../annotations/effect__cluster__Message.yaml | 0 .../effect__cluster__MessageStorage.yaml | 0 .../annotations/effect__cluster__Reply.yaml | 0 .../annotations/effect__cluster__Runner.yaml | 0 .../effect__cluster__RunnerAddress.yaml | 0 .../effect__cluster__RunnerHealth.yaml | 0 .../effect__cluster__RunnerServer.yaml | 0 .../effect__cluster__RunnerStorage.yaml | 0 .../annotations/effect__cluster__Runners.yaml | 0 .../annotations/effect__cluster__ShardId.yaml | 0 .../effect__cluster__Sharding.yaml | 0 .../effect__cluster__ShardingConfig.yaml | 0 ...t__cluster__ShardingRegistrationEvent.yaml | 0 .../effect__cluster__SingleRunner.yaml | 0 .../effect__cluster__Singleton.yaml | 0 .../effect__cluster__SingletonAddress.yaml | 0 .../effect__cluster__Snowflake.yaml | 0 .../effect__cluster__SocketRunner.yaml | 0 .../effect__cluster__SqlMessageStorage.yaml | 0 .../effect__cluster__SqlRunnerStorage.yaml | 0 .../effect__cluster__TestRunner.yaml | 0 .../annotations/effect__cluster__index.yaml | 0 .../annotations/effect__experimental.yaml | 0 .../effect__experimental__DevTools.yaml | 0 ...ffect__experimental__DevTools__Client.yaml | 0 ...ffect__experimental__DevTools__Domain.yaml | 0 ...ffect__experimental__DevTools__Server.yaml | 0 .../effect__experimental__Event.yaml | 0 .../effect__experimental__EventGroup.yaml | 0 .../effect__experimental__EventJournal.yaml | 0 .../effect__experimental__EventLog.yaml | 0 .../effect__experimental__EventLogRemote.yaml | 0 .../effect__experimental__EventLogServer.yaml | 0 ...erimental__EventLogServer__Cloudflare.yaml | 0 .../effect__experimental__Machine.yaml | 0 ...ect__experimental__Machine__Procedure.yaml | 0 ..._experimental__Machine__ProcedureList.yaml | 0 ...l__Machine__SerializableProcedureList.yaml | 0 .../effect__experimental__PersistedCache.yaml | 0 .../effect__experimental__PersistedQueue.yaml | 0 ...__experimental__PersistedQueue__Redis.yaml | 0 .../effect__experimental__Persistence.yaml | 0 ...fect__experimental__Persistence__Lmdb.yaml | 0 ...ect__experimental__Persistence__Redis.yaml | 0 .../effect__experimental__RateLimiter.yaml | 0 ...ect__experimental__RateLimiter__Redis.yaml | 0 .../effect__experimental__Reactivity.yaml | 0 ...effect__experimental__RequestResolver.yaml | 0 .../effect__experimental__Sse.yaml | 0 .../effect__experimental__VariantSchema.yaml | 0 .../effect__experimental__index.yaml | 0 .../annotations/effect__index.yaml | 0 .../effect__opentelemetry__Logger.yaml | 0 .../effect__opentelemetry__Metrics.yaml | 0 .../effect__opentelemetry__NodeSdk.yaml | 0 .../effect__opentelemetry__Otlp.yaml | 0 .../effect__opentelemetry__OtlpLogger.yaml | 0 .../effect__opentelemetry__OtlpMetrics.yaml | 0 .../effect__opentelemetry__OtlpResource.yaml | 0 .../effect__opentelemetry__OtlpTracer.yaml | 0 .../effect__opentelemetry__Resource.yaml | 0 .../effect__opentelemetry__Tracer.yaml | 0 .../effect__opentelemetry__WebSdk.yaml | 0 .../effect__opentelemetry__index.yaml | 0 ...__platform-browser__BrowserHttpClient.yaml | 0 ...fect__platform-browser__BrowserWorker.yaml | 0 ...platform-browser__BrowserWorkerRunner.yaml | 0 .../effect__platform-browser__Clipboard.yaml | 0 ...effect__platform-browser__Geolocation.yaml | 0 ...effect__platform-browser__Permissions.yaml | 0 .../effect__platform-browser__index.yaml | 0 ...ect__platform-bun__BunCommandExecutor.yaml | 0 .../effect__platform-bun__BunContext.yaml | 0 ...orm-bun__BunFileSystem__ParcelWatcher.yaml | 0 ...effect__platform-bun__BunHttpPlatform.yaml | 0 .../effect__platform-bun__BunHttpServer.yaml | 0 ...t__platform-bun__BunHttpServerRequest.yaml | 0 ...ffect__platform-bun__BunKeyValueStore.yaml | 0 .../effect__platform-bun__BunSink.yaml | 0 .../effect__platform-bun__BunSocket.yaml | 0 ...effect__platform-bun__BunSocketServer.yaml | 0 .../effect__platform-bun__BunStream.yaml | 0 .../effect__platform-bun__BunTerminal.yaml | 8 + .../effect__platform-bun__BunWorker.yaml | 0 .../effect__platform-bun__index.yaml | 0 ...form-node-shared__NodeCommandExecutor.yaml | 0 ...shared__NodeFileSystem__ParcelWatcher.yaml | 0 ...atform-node-shared__NodeKeyValueStore.yaml | 0 ...__platform-node-shared__NodeMultipart.yaml | 0 ...ffect__platform-node-shared__NodeSink.yaml | 0 ...ect__platform-node-shared__NodeSocket.yaml | 0 ...ect__platform-node-shared__NodeStream.yaml | 0 ...t__platform-node-shared__NodeTerminal.yaml | 8 + ...t__platform-node__NodeCommandExecutor.yaml | 0 .../effect__platform-node__NodeContext.yaml | 0 ...m-node__NodeFileSystem__ParcelWatcher.yaml | 0 ...effect__platform-node__NodeHttpClient.yaml | 0 ...effect__platform-node__NodeHttpServer.yaml | 0 ...ect__platform-node__NodeKeyValueStore.yaml | 0 .../effect__platform-node__NodeTerminal.yaml | 8 + .../effect__platform-node__NodeWorker.yaml | 0 .../effect__platform-node__Undici.yaml | 0 .../effect__platform-node__index.yaml | 0 .../annotations/effect__platform.yaml | 0 .../effect__platform__ChannelSchema.yaml | 0 .../effect__platform__Command.yaml | 0 .../effect__platform__CommandExecutor.yaml | 0 .../effect__platform__Cookies.yaml | 0 .../effect__platform__Effectify.yaml | 0 .../annotations/effect__platform__Error.yaml | 0 .../annotations/effect__platform__Etag.yaml | 0 .../effect__platform__FetchHttpClient.yaml | 0 .../effect__platform__FileSystem.yaml | 0 .../effect__platform__Headers.yaml | 0 .../effect__platform__HttpApi.yaml | 0 .../effect__platform__HttpApiBuilder.yaml | 0 .../effect__platform__HttpApiClient.yaml | 2 +- .../effect__platform__HttpApiEndpoint.yaml | 0 .../effect__platform__HttpApiError.yaml | 0 .../effect__platform__HttpApiGroup.yaml | 0 .../effect__platform__HttpApiMiddleware.yaml | 0 .../effect__platform__HttpApiScalar.yaml | 0 .../effect__platform__HttpApiSchema.yaml | 0 .../effect__platform__HttpApiSecurity.yaml | 0 .../effect__platform__HttpApiSwagger.yaml | 0 .../effect__platform__HttpApp.yaml | 0 .../effect__platform__HttpBody.yaml | 0 .../effect__platform__HttpClient.yaml | 0 .../effect__platform__HttpClientError.yaml | 0 .../effect__platform__HttpClientRequest.yaml | 0 .../effect__platform__HttpClientResponse.yaml | 0 ...effect__platform__HttpIncomingMessage.yaml | 0 .../effect__platform__HttpLayerRouter.yaml | 0 .../effect__platform__HttpMethod.yaml | 0 .../effect__platform__HttpMiddleware.yaml | 0 .../effect__platform__HttpMultiplex.yaml | 0 .../effect__platform__HttpPlatform.yaml | 0 .../effect__platform__HttpRouter.yaml | 0 .../effect__platform__HttpServer.yaml | 2 +- .../effect__platform__HttpServerError.yaml | 0 .../effect__platform__HttpServerRequest.yaml | 0 ...fect__platform__HttpServerRespondable.yaml | 0 .../effect__platform__HttpServerResponse.yaml | 0 .../effect__platform__KeyValueStore.yaml | 0 .../effect__platform__MsgPack.yaml | 0 .../effect__platform__Multipart.yaml | 0 .../annotations/effect__platform__Ndjson.yaml | 0 .../effect__platform__OpenApi.yaml | 0 .../effect__platform__OpenApiJsonSchema.yaml | 0 .../annotations/effect__platform__Path.yaml | 0 ...ect__platform__PlatformConfigProvider.yaml | 0 .../effect__platform__PlatformLogger.yaml | 0 .../effect__platform__Runtime.yaml | 0 .../annotations/effect__platform__Socket.yaml | 0 .../effect__platform__SocketServer.yaml | 0 .../effect__platform__Template.yaml | 0 .../effect__platform__Terminal.yaml | 0 .../effect__platform__Transferable.yaml | 0 .../annotations/effect__platform__Url.yaml | 0 .../effect__platform__UrlParams.yaml | 0 .../annotations/effect__platform__Worker.yaml | 0 .../effect__platform__WorkerError.yaml | 0 .../effect__platform__WorkerRunner.yaml | 0 .../annotations/effect__platform__index.yaml | 0 .../annotations/effect__printer-ansi.yaml | 0 .../effect__printer-ansi__Ansi.yaml | 0 .../effect__printer-ansi__AnsiDoc.yaml | 0 .../effect__printer-ansi__Color.yaml | 0 .../effect__printer-ansi__index.yaml | 0 .../annotations/effect__printer.yaml | 0 .../annotations/effect__printer__Doc.yaml | 0 .../effect__printer__DocStream.yaml | 0 .../annotations/effect__printer__DocTree.yaml | 0 .../annotations/effect__printer__Flatten.yaml | 0 .../annotations/effect__printer__Layout.yaml | 0 .../effect__printer__Optimize.yaml | 0 .../effect__printer__PageWidth.yaml | 0 .../annotations/effect__printer__index.yaml | 0 .../annotations/effect__rpc.yaml | 0 .../annotations/effect__rpc__Rpc.yaml | 0 .../annotations/effect__rpc__RpcClient.yaml | 0 .../effect__rpc__RpcClientError.yaml | 0 .../annotations/effect__rpc__RpcGroup.yaml | 0 .../annotations/effect__rpc__RpcMessage.yaml | 0 .../effect__rpc__RpcMiddleware.yaml | 0 .../annotations/effect__rpc__RpcSchema.yaml | 0 .../effect__rpc__RpcSerialization.yaml | 0 .../annotations/effect__rpc__RpcServer.yaml | 0 .../annotations/effect__rpc__RpcTest.yaml | 0 .../annotations/effect__rpc__index.yaml | 0 ...ect__sql-clickhouse__ClickhouseClient.yaml | 0 ...t__sql-clickhouse__ClickhouseMigrator.yaml | 0 .../effect__sql-clickhouse__index.yaml | 0 .../annotations/effect__sql-d1__D1Client.yaml | 0 .../annotations/effect__sql-d1__index.yaml | 0 .../effect__sql-drizzle__Mysql.yaml | 0 .../annotations/effect__sql-drizzle__Pg.yaml | 0 .../effect__sql-drizzle__Sqlite.yaml | 0 .../effect__sql-kysely__Kysely.yaml | 0 .../effect__sql-kysely__Mssql.yaml | 0 .../effect__sql-kysely__Mysql.yaml | 0 .../annotations/effect__sql-kysely__Pg.yaml | 0 .../effect__sql-kysely__Sqlite.yaml | 0 .../effect__sql-kysely__patch.types.yaml | 0 .../effect__sql-libsql__index.yaml | 0 .../effect__sql-mssql__MssqlClient.yaml | 0 .../effect__sql-mssql__Parameter.yaml | 0 .../effect__sql-mssql__Procedure.yaml | 0 .../annotations/effect__sql-mssql__index.yaml | 0 .../effect__sql-mysql2__MysqlClient.yaml | 0 .../effect__sql-mysql2__index.yaml | 0 .../annotations/effect__sql-pg__PgClient.yaml | 0 .../annotations/effect__sql-pg__index.yaml | 0 .../effect__sql-sqlite-bun__SqliteClient.yaml | 0 .../effect__sql-sqlite-bun__index.yaml | 0 .../effect__sql-sqlite-do__SqliteClient.yaml | 0 .../effect__sql-sqlite-do__index.yaml | 0 ...effect__sql-sqlite-node__SqliteClient.yaml | 0 .../effect__sql-sqlite-node__index.yaml | 0 ...sql-sqlite-react-native__SqliteClient.yaml | 0 ...ffect__sql-sqlite-react-native__index.yaml | 0 ...effect__sql-sqlite-wasm__SqliteClient.yaml | 0 .../effect__sql-sqlite-wasm__index.yaml | 0 .../annotations/effect__sql.yaml | 0 .../annotations/effect__sql__Migrator.yaml | 0 .../effect__sql__Migrator__FileSystem.yaml | 0 .../annotations/effect__sql__Model.yaml | 0 .../annotations/effect__sql__SqlClient.yaml | 0 .../effect__sql__SqlConnection.yaml | 0 .../annotations/effect__sql__SqlError.yaml | 0 .../effect__sql__SqlEventJournal.yaml | 0 .../effect__sql__SqlEventLogServer.yaml | 0 .../effect__sql__SqlPersistedQueue.yaml | 0 .../annotations/effect__sql__SqlResolver.yaml | 0 .../annotations/effect__sql__SqlSchema.yaml | 0 .../annotations/effect__sql__Statement.yaml | 0 .../annotations/effect__sql__index.yaml | 0 .../annotations/effect__typeclass.yaml | 0 .../effect__typeclass__Alternative.yaml | 0 .../effect__typeclass__Applicative.yaml | 0 .../effect__typeclass__Bicovariant.yaml | 0 .../effect__typeclass__Bounded.yaml | 0 .../effect__typeclass__Chainable.yaml | 0 .../effect__typeclass__Contravariant.yaml | 0 .../effect__typeclass__Coproduct.yaml | 0 .../effect__typeclass__Covariant.yaml | 0 .../effect__typeclass__Filterable.yaml | 0 .../effect__typeclass__FlatMap.yaml | 0 .../effect__typeclass__Foldable.yaml | 0 .../effect__typeclass__Invariant.yaml | 0 .../annotations/effect__typeclass__Monad.yaml | 0 .../effect__typeclass__Monoid.yaml | 0 .../annotations/effect__typeclass__Of.yaml | 0 .../effect__typeclass__Pointed.yaml | 0 .../effect__typeclass__Product.yaml | 0 .../effect__typeclass__SemiAlternative.yaml | 0 .../effect__typeclass__SemiApplicative.yaml | 0 .../effect__typeclass__SemiCoproduct.yaml | 0 .../effect__typeclass__SemiProduct.yaml | 0 .../effect__typeclass__Semigroup.yaml | 0 .../effect__typeclass__Traversable.yaml | 0 ...ect__typeclass__TraversableFilterable.yaml | 0 .../effect__typeclass__data__Array.yaml | 0 .../effect__typeclass__data__BigInt.yaml | 0 .../effect__typeclass__data__Boolean.yaml | 0 .../effect__typeclass__data__Duration.yaml | 0 .../effect__typeclass__data__Effect.yaml | 0 .../effect__typeclass__data__Either.yaml | 0 .../effect__typeclass__data__Identity.yaml | 0 .../effect__typeclass__data__Micro.yaml | 0 .../effect__typeclass__data__Number.yaml | 0 .../effect__typeclass__data__Option.yaml | 0 .../effect__typeclass__data__Ordering.yaml | 0 .../effect__typeclass__data__Predicate.yaml | 0 .../effect__typeclass__data__Record.yaml | 0 .../effect__typeclass__data__String.yaml | 0 .../effect__typeclass__data__Tuple.yaml | 0 .../annotations/effect__typeclass__index.yaml | 0 .../annotations/effect__vitest__index.yaml | 0 .../annotations/effect__vitest__utils.yaml | 0 .../annotations/effect__workflow.yaml | 0 .../effect__workflow__Activity.yaml | 0 .../effect__workflow__DurableClock.yaml | 0 .../effect__workflow__DurableDeferred.yaml | 0 .../effect__workflow__DurableQueue.yaml | 0 .../effect__workflow__DurableRateLimiter.yaml | 0 .../effect__workflow__Workflow.yaml | 0 .../effect__workflow__WorkflowEngine.yaml | 0 .../effect__workflow__WorkflowProxy.yaml | 0 ...effect__workflow__WorkflowProxyServer.yaml | 0 .../effect/migration => migration}/cause.md | 0 .../migration => migration}/equality.md | 0 .../migration => migration}/error-handling.md | 0 .../fiber-keep-alive.md | 0 .../migration => migration}/fiberref.md | 0 .../effect/migration => migration}/forking.md | 0 .../migration => migration}/generators.md | 0 .../layer-memoization.md | 0 .../effect/migration => migration}/runtime.md | 0 .../effect/migration => migration}/schema.md | 0 .../effect/migration => migration}/scope.md | 0 .../migration => migration}/services.md | 0 .../migration => migration}/v3-to-v4.md | 60 +- .../migration => migration}/yieldable.md | 0 old.patch | 0 package.json | 144 +- .../ai/anthropic/CHANGELOG.md | 25 + .../ai/anthropic/README.md | 0 .../ai/anthropic/codegen.yml | 2 + .../ai/anthropic/package.json | 2 +- .../ai/anthropic/src/AnthropicClient.ts | 0 .../ai/anthropic/src/AnthropicConfig.ts | 0 .../ai/anthropic/src/AnthropicError.ts | 0 .../anthropic/src/AnthropicLanguageModel.ts | 59 +- .../ai/anthropic/src/AnthropicTelemetry.ts | 0 .../ai/anthropic/src/AnthropicTool.ts | 0 .../ai/anthropic/src/Generated.ts | 107 +- .../ai/anthropic/src/index.ts | 0 .../ai/anthropic/src/internal/errors.ts | 0 .../ai/anthropic/src/internal/utilities.ts | 0 .../ai/anthropic/test/AnthropicClient.test.ts | 0 .../test/AnthropicLanguageModel.test.ts | 217 + .../ai/anthropic/tsconfig.json | 0 .../typetest/AnthropicLanguageModel.tst.ts | 0 packages/ai/cloudflare/CHANGELOG.md | 41 + .../ai/cloudflare/README.md | 15 + .../ai/cloudflare/package.json | 2 +- .../ai/cloudflare/src/CloudflareClient.ts | 0 .../cloudflare/src/CloudflareDecisionModel.ts | 45 +- .../ai/cloudflare/src/CloudflareSchema.ts | 51 +- .../ai/cloudflare/src/index.ts | 0 .../ai/cloudflare/src/internal/errors.ts | 0 .../cloudflare/test/CloudflareClient.test.ts | 0 .../test/CloudflareDecisionModel.test.ts | 81 +- .../ai/cloudflare/tsconfig.json | 0 .../typetest/CloudflareDecisionModel.tst.ts | 24 + .../ai/openai-compat/CHANGELOG.md | 38 + .../ai/openai-compat/README.md | 0 .../ai/openai-compat/package.json | 2 +- .../ai/openai-compat/src/OpenAiClient.ts | 18 +- .../ai/openai-compat/src/OpenAiConfig.ts | 0 .../openai-compat/src/OpenAiEmbeddingModel.ts | 0 .../ai/openai-compat/src/OpenAiError.ts | 20 +- .../openai-compat/src/OpenAiLanguageModel.ts | 585 +- .../ai/openai-compat/src/OpenAiTelemetry.ts | 0 .../ai/openai-compat/src/index.ts | 0 .../ai/openai-compat/src/internal/errors.ts | 10 +- .../openai-compat/src/internal/utilities.ts | 0 .../openai-compat/test/OpenAiClient.test.ts | 5 +- .../test/OpenAiEmbeddingModel.test.ts | 0 .../test/OpenAiLanguageModel.test.ts | 469 +- .../test/OpenAiTelemetry.test.ts | 0 .../ai/openai-compat/tsconfig.json | 0 .../typetest/OpenAiConfig.tst.ts | 0 .../ai/openai/CHANGELOG.md | 47 + .../packages => packages}/ai/openai/README.md | 0 .../ai/openai/codegen.yaml | 24 +- .../ai/openai/package.json | 2 +- packages/ai/openai/src/Generated.ts | 69150 ++++++++++++++++ .../ai/openai/src/OpenAiClient.ts | 75 +- .../ai/openai/src/OpenAiClientGenerated.ts | 0 .../ai/openai/src/OpenAiConfig.ts | 0 packages/ai/openai/src/OpenAiDecisionModel.ts | 208 + .../ai/openai/src/OpenAiEmbeddingModel.ts | 0 .../ai/openai/src/OpenAiError.ts | 21 +- .../ai/openai/src/OpenAiLanguageModel.ts | 116 +- .../ai/openai/src/OpenAiSchema.ts | 194 +- .../ai/openai/src/OpenAiTelemetry.ts | 0 .../ai/openai/src/OpenAiTool.ts | 2 +- .../ai/openai/src/index.ts | 6 + .../ai/openai/src/internal/errors.ts | 0 .../ai/openai/src/internal/utilities.ts | 0 .../ai/openai/test/OpenAiClient.test.ts | 20 +- .../openai/test/OpenAiDecisionModel.test.ts | 327 + .../openai/test/OpenAiEmbeddingModel.test.ts | 1 + .../openai/test/OpenAiLanguageModel.test.ts | 239 +- .../ai/openai/test/OpenAiSchema.test.ts | 1 + .../ai/openai/test/OpenAiTelemetry.test.ts | 0 .../ai/openai/tsconfig.json | 0 .../typetest/OpenAiDecisionModel.tst.ts | 86 + .../ai/openai/typetest/OpenAiTool.tst.ts | 0 .../ai/openrouter/CHANGELOG.md | 22 + .../ai/openrouter/README.md | 0 .../ai/openrouter/codegen.yml | 0 .../ai/openrouter/package.json | 2 +- .../ai/openrouter/src/Generated.ts | 0 .../ai/openrouter/src/OpenRouterClient.ts | 0 .../ai/openrouter/src/OpenRouterConfig.ts | 0 .../openrouter/src/OpenRouterDecisionModel.ts | 8 +- .../ai/openrouter/src/OpenRouterError.ts | 0 .../openrouter/src/OpenRouterLanguageModel.ts | 0 .../ai/openrouter/src/OpenRouterSchema.ts | 19 +- .../ai/openrouter/src/index.ts | 0 .../ai/openrouter/src/internal/errors.ts | 0 .../ai/openrouter/src/internal/utilities.ts | 0 .../ai/openrouter/test/Generated.test.ts | 0 .../openrouter/test/OpenRouterClient.test.ts | 0 .../test/OpenRouterDecisionModel.test.ts | 41 +- .../test/OpenRouterLanguageModel.test.ts | 0 .../openrouter/test/OpenRouterSchema.test.ts | 0 .../ai/openrouter/tsconfig.json | 0 .../typetest/OpenRouterDecisionModel.tst.ts | 0 .../ai/typesafe/CHANGELOG.md | 22 + .../ai/typesafe/README.md | 15 + .../ai/typesafe/package.json | 2 +- .../ai/typesafe/src/TypeSafeClient.ts | 0 .../ai/typesafe/src/TypeSafeConfig.ts | 0 .../ai/typesafe/src/TypeSafeDecisionModel.ts | 8 +- .../ai/typesafe/src/TypeSafeSchema.ts | 20 +- .../ai/typesafe/src/index.ts | 0 .../ai/typesafe/src/internal/errors.ts | 0 .../ai/typesafe/test/TypeSafeClient.test.ts | 0 .../test/TypeSafeDecisionModel.test.ts | 40 +- .../ai/typesafe/test/TypeSafeSchema.test.ts | 0 .../ai/typesafe/tsconfig.json | 0 .../typetest/TypeSafeDecisionModel.tst.ts | 0 .../atom/react/CHANGELOG.md | 22 + .../packages => packages}/atom/react/LICENSE | 0 .../atom/react/README.md | 0 .../atom/react/package.json | 4 +- .../atom/react/src/Hooks.ts | 0 .../atom/react/src/ReactHydration.ts | 0 .../atom/react/src/RegistryContext.ts | 0 .../atom/react/src/ScopedAtom.ts | 0 .../atom/react/src/index.ts | 0 .../atom/react/test/index.test.tsx | 0 .../atom/react/tsconfig.json | 0 .../atom/react/vitest.setup.ts | 0 .../atom/solid/CHANGELOG.md | 21 + .../packages => packages}/atom/solid/LICENSE | 0 .../atom/solid/README.md | 0 .../atom/solid/package.json | 4 +- .../atom/solid/src/Hooks.ts | 0 .../atom/solid/src/RegistryContext.ts | 0 .../atom/solid/src/index.ts | 0 .../atom/solid/test/index.test.tsx | 0 .../atom/solid/tsconfig.json | 0 .../atom/solid/vitest.setup.ts | 0 .../atom/vue/CHANGELOG.md | 21 + .../packages => packages}/atom/vue/LICENSE | 0 .../packages => packages}/atom/vue/README.md | 0 .../atom/vue/package.json | 2 +- .../atom/vue/src/index.ts | 0 .../atom/vue/test/index.test.ts | 0 .../atom/vue/tsconfig.json | 0 packages/cli/.gitignore | 0 packages/cli/.madgerc | 7 - packages/cli/.npmignore | 14 - packages/cli/CHANGELOG.md | 1787 - packages/cli/bin.js | 2 - packages/cli/package.json | 158 - packages/cli/src/argv-patch.ts | 16 - packages/cli/src/extract.ts | 75 - packages/cli/src/gist.ts | 748 - packages/cli/src/index.ts | 904 - packages/cli/src/os-command.ts | 54 - packages/cli/src/shared.ts | 3 - packages/cli/src/sync-effect-app-subtree.ts | 307 - packages/cli/src/sync-effect-subtree.ts | 140 - packages/cli/src/sync-shared.ts | 349 - packages/cli/src/update-packages.ts | 4 - packages/cli/test.gists.yaml | 27 - packages/cli/test/argv-patch.test.ts | 198 - packages/cli/test/gist-cache.test.ts | 92 - packages/cli/test/update-packages.test.ts | 13 - packages/cli/tsconfig.json | 17 - packages/cli/vitest.config.ts | 5 - packages/e2e/CHANGELOG.md | 881 - packages/e2e/package.json | 34 - .../e2e/test/repoInvalidation.e2e.test.ts | 270 - packages/e2e/tsconfig.json | 28 - packages/e2e/vitest.config.ts | 13 - packages/effect-app/.madgerc | 7 - packages/effect-app/.npmignore | 14 - packages/effect-app/CHANGELOG.md | 9766 --- packages/effect-app/package.json | 97 - packages/effect-app/src/Array.ts | 78 - packages/effect-app/src/Chunk.ts | 32 - packages/effect-app/src/Config.ts | 7 - packages/effect-app/src/Config/SecretURL.ts | 80 - .../src/Config/internal/configSecretURL.ts | 68 - packages/effect-app/src/ConfigProvider.ts | 5 - packages/effect-app/src/Context.ts | 181 - packages/effect-app/src/DataDependencies.ts | 171 - packages/effect-app/src/DateTime.ts | 473 - packages/effect-app/src/Effect.ts | 183 - packages/effect-app/src/Emailer.ts | 51 - packages/effect-app/src/Function.ts | 168 - packages/effect-app/src/Inputify.type.ts | 12 - packages/effect-app/src/Layer.ts | 69 - packages/effect-app/src/Model.ts | 5 - packages/effect-app/src/Model/Repository.ts | 7 - .../src/Model/Repository/Registry.ts | 35 - .../src/Model/Repository/dependency.ts | 32 - .../effect-app/src/Model/Repository/ext.ts | 374 - .../src/Model/Repository/internal/internal.ts | 987 - .../effect-app/src/Model/Repository/legacy.ts | 29 - .../src/Model/Repository/makeRepo.ts | 168 - .../src/Model/Repository/service.ts | 676 - .../src/Model/Repository/validation.ts | 38 - packages/effect-app/src/Model/dsl.ts | 130 - .../effect-app/src/Model/filter/filterApi.ts | 78 - packages/effect-app/src/Model/filter/types.ts | 6 - .../src/Model/filter/types/errors.ts | 47 - .../src/Model/filter/types/fields.ts | 50 - .../src/Model/filter/types/path/common.ts | 404 - .../src/Model/filter/types/path/eager.ts | 329 - .../src/Model/filter/types/path/index.ts | 4 - .../src/Model/filter/types/utils.ts | 128 - .../src/Model/filter/types/validator.ts | 46 - packages/effect-app/src/Model/query.ts | 13 - packages/effect-app/src/Model/query/dsl.ts | 3092 - .../src/Model/query/new-kid-interpreter.ts | 531 - packages/effect-app/src/NonEmptySet.ts | 92 - packages/effect-app/src/Option.ts | 63 - packages/effect-app/src/Pure.ts | 450 - packages/effect-app/src/QueueMaker.ts | 11 - packages/effect-app/src/RequestContext.ts | 48 - .../src/RequestScopedDependencies.ts | 45 - packages/effect-app/src/Schema.ts | 499 - packages/effect-app/src/Schema/Class.ts | 683 - packages/effect-app/src/Schema/FastCheck.ts | 23 - packages/effect-app/src/Schema/Methods.ts | 8 - .../effect-app/src/Schema/SchemaParser.ts | 12 - .../src/Schema/SpecialJsonSchema.ts | 137 - .../effect-app/src/Schema/SpecialOpenApi.ts | 130 - packages/effect-app/src/Schema/brand.ts | 71 - packages/effect-app/src/Schema/email.ts | 32 - packages/effect-app/src/Schema/ext.ts | 681 - packages/effect-app/src/Schema/moreStrings.ts | 390 - packages/effect-app/src/Schema/numbers.ts | 162 - packages/effect-app/src/Schema/phoneNumber.ts | 30 - packages/effect-app/src/Schema/schema.ts | 5 - packages/effect-app/src/Schema/strings.ts | 69 - packages/effect-app/src/SchemaAST.ts | 1 - packages/effect-app/src/Set.ts | 666 - packages/effect-app/src/Store.ts | 325 - packages/effect-app/src/TypeTest.ts | 7 - packages/effect-app/src/Types.ts | 16 - packages/effect-app/src/Widen.type.ts | 26 - packages/effect-app/src/_ext/Array.ts | 36 - packages/effect-app/src/_ext/ord.ext.ts | 40 - packages/effect-app/src/builtin-json.d.ts | 24 - packages/effect-app/src/builtin.ts | 53 - packages/effect-app/src/client.ts | 8 - .../effect-app/src/client/InvalidationKeys.ts | 55 - .../effect-app/src/client/apiClientFactory.ts | 465 - packages/effect-app/src/client/clientFor.ts | 186 - packages/effect-app/src/client/errors.ts | 310 - packages/effect-app/src/client/makeClient.ts | 629 - packages/effect-app/src/faker.ts | 32 - packages/effect-app/src/http.ts | 1 - packages/effect-app/src/http/Request.ts | 42 - packages/effect-app/src/http/internal/lib.ts | 13 - packages/effect-app/src/ids.ts | 66 - packages/effect-app/src/index.ts | 27 - packages/effect-app/src/logger.ts | 3 - packages/effect-app/src/middleware.ts | 28 - .../effect-app/src/migration/nativeEffect.ts | 645 - packages/effect-app/src/rpc.ts | 4 - packages/effect-app/src/rpc/Invalidation.ts | 278 - .../effect-app/src/rpc/MiddlewareMaker.ts | 492 - packages/effect-app/src/rpc/README.md | 41 - packages/effect-app/src/rpc/RpcContextMap.ts | 156 - packages/effect-app/src/rpc/RpcMiddleware.ts | 244 - packages/effect-app/src/runtime.ts | 57 - packages/effect-app/src/setupRequest.ts | 138 - packages/effect-app/src/toast.ts | 54 - packages/effect-app/src/transform.ts | 80 - packages/effect-app/src/utils.ts | 884 - packages/effect-app/src/utils/effectify.ts | 260 - packages/effect-app/src/utils/extend.ts | 28 - packages/effect-app/src/utils/gen.ts | 24 - packages/effect-app/src/utils/logLevel.ts | 17 - packages/effect-app/src/utils/logger.ts | 36 - packages/effect-app/src/validation.ts | 18 - .../effect-app/src/validation/validators.ts | 31 - packages/effect-app/src/withToast.ts | 133 - .../effect-app/test/dateTimeLocal.test.ts | 55 - packages/effect-app/test/moreStrings.test.ts | 62 - .../test/nativeEffectMigration.test.ts | 77 - packages/effect-app/test/rpc.test.ts | 62 - .../test/rpcMiddlewareContext.test.ts | 63 - packages/effect-app/test/schema.test.ts | 681 - packages/effect-app/test/secretURL.test.ts | 160 - packages/effect-app/test/special.test.ts | 1261 - packages/effect-app/test/utils.test.ts | 209 - packages/effect-app/tsconfig.base.json | 61 - packages/effect-app/tsconfig.json | 12 - packages/effect-app/tsconfig.src.json | 31 - packages/effect-app/tsconfig.test.json | 33 - packages/effect-app/vitest.config.ts | 5 - .../packages => packages}/effect/ARBITRARY.md | 0 .../packages => packages}/effect/CHANGELOG.md | 252 + .../packages => packages}/effect/CONFIG.md | 0 .../packages => packages}/effect/HTTPAPI.md | 0 .../packages => packages}/effect/LICENSE | 0 .../packages => packages}/effect/MCP.md | 0 .../packages => packages}/effect/OPTIC.md | 0 .../packages => packages}/effect/README.md | 0 .../packages => packages}/effect/SCHEMA.md | 18 +- .../effect/benchmark/Pool.ts | 0 .../effect/benchmark/http/multipart.ts | 0 .../benchmark/http/serverAllocations.ts | 0 .../effect/benchmark/http/tracer.ts | 0 .../benchmark/httpapi/sseClientDecoder.ts | 0 .../effect/benchmark/net/NetAddress.ts | 0 .../effect/benchmark/rpc/RpcSerialization.ts | 0 .../effect/benchmark/schema/Optic.ts | 0 .../effect/benchmark/schema/SchemaBinary.md | 0 .../effect/benchmark/schema/SchemaBinary.ts | 0 .../effect/benchmark/schema/SchemaError.ts | 0 .../effect/benchmark/stream/splitLines.ts | 0 .../packages => packages}/effect/package.json | 10 +- .../effect/runtimeperf/README.md | 0 .../effect/runtimeperf/compare.mts | 0 .../effect/runtimeperf/config.json | 0 .../effect/runtimeperf/materialize.mts | 0 .../effect/runtimeperf/run.mts | 0 .../effect/runtimeperf/stats.mts | 0 .../arbitrary/fixtures/fast-check-v4.ts | 0 .../suites/arbitrary/fixtures/native.ts | 0 .../suites/arbitrary/fixtures/schema.ts | 0 .../suites/compiler-rebuild/README.md | 0 .../suites/compiler-rebuild/costs.mts | 0 .../suites/compiler-rebuild/fixtures/aot.ts | 0 .../suites/compiler-rebuild/fixtures/cases.ts | 0 .../compiler-rebuild/fixtures/generate.mts | 0 .../compiler-rebuild/fixtures/interpreted.ts | 0 .../suites/compiler-rebuild/fixtures/jit.ts | 0 .../compiler-rebuild/fixtures/valibot.ts | 0 .../compiler-rebuild/fixtures/zod-cases.ts | 0 .../compiler-rebuild/fixtures/zod-compiled.ts | 0 .../compiler-rebuild/fixtures/zod-jitless.ts | 0 .../compiler-rebuild/report-resources.mts | 0 .../suites/compiler-rebuild/resources.mts | 0 .../suites/compiler-rebuild/run-resources.mts | 0 .../runtimeperf/suites/moltar/fixtures/aot.ts | 0 .../suites/moltar/fixtures/cases.ts | 0 .../suites/moltar/fixtures/data.ts | 0 .../suites/moltar/fixtures/generate.mts | 0 .../suites/moltar/fixtures/interpreted.ts | 0 .../runtimeperf/suites/moltar/fixtures/jit.ts | 0 .../suites/moltar/fixtures/valibot.ts | 0 .../runtimeperf/suites/moltar/fixtures/zod.ts | 0 .../suites/schema-benchmarks/fixtures/data.ts | 0 .../schema-benchmarks/fixtures/effect-beta.ts | 0 .../schema-benchmarks/fixtures/valibot.ts | 0 .../suites/schema-benchmarks/fixtures/zod.ts | 0 .../suites/schema-checked-sources/README.md | 0 .../schema-checked-sources/fixtures/cases.ts | 0 .../suites/schema-construction/AST-LAYOUT.md | 0 .../suites/schema-construction/README.md | 0 .../suites/schema-construction/RESULTS.md | 0 .../suites/schema-construction/TO-TYPE.md | 0 .../suites/schema-construction/fixtures.mts | 0 .../suites/schema-construction/profile.mts | 0 .../suites/schema-construction/run.mts | 0 .../suites/schema-construction/worker.mts | 0 .../fixtures/cases.ts | 0 .../suites/schema/fixtures/adapters.ts | 0 .../suites/schema/fixtures/behavior.ts | 0 .../suites/schema/fixtures/cold.ts | 0 .../suites/schema/fixtures/comparison.ts | 0 .../runtimeperf/test/materialize.test.mts | 0 .../effect/runtimeperf/test/registry.test.mts | 0 .../test/schema-construction.test.mts | 0 .../effect/runtimeperf/test/stats.test.mts | 0 .../effect/runtimeperf/test/worker.test.mts | 0 .../runtimeperf/test/worktree-diff.test.mts | 0 .../effect/runtimeperf/utils.mts | 0 .../effect/runtimeperf/worker.mts | 0 .../effect/runtimeperf/worktree-diff.mts | 0 .../effect/src/Arbitrary.ts | 0 .../packages => packages}/effect/src/Array.ts | 19 +- .../effect/src/BigDecimal.ts | 0 .../effect/src/BigInt.ts | 0 .../effect/src/Boolean.ts | 0 .../packages => packages}/effect/src/Brand.ts | 0 .../effect/src/ByteSize.ts | 0 .../packages => packages}/effect/src/Cache.ts | 0 .../packages => packages}/effect/src/Cause.ts | 0 .../effect/src/Channel.ts | 27 +- .../effect/src/ChannelSchema.ts | 0 .../packages => packages}/effect/src/Chunk.ts | 0 .../packages => packages}/effect/src/Clock.ts | 0 .../effect/src/Combiner.ts | 0 .../effect/src/Config.ts | 0 .../effect/src/ConfigProvider.ts | 84 +- .../effect/src/Console.ts | 0 .../effect/src/Context.ts | 15 +- .../packages => packages}/effect/src/Cron.ts | 0 .../effect/src/Crypto.ts | 52 +- .../packages => packages}/effect/src/Data.ts | 0 .../effect/src/DateTime.ts | 15 +- .../effect/src/Deferred.ts | 0 .../effect/src/Differ.ts | 0 .../effect/src/Duration.ts | 53 +- .../effect/src/Effect.ts | 193 +- .../effect/src/Effectable.ts | 0 .../packages => packages}/effect/src/Equal.ts | 0 .../effect/src/Equivalence.ts | 0 .../effect/src/ErrorReporter.ts | 0 .../effect/src/ExecutionPlan.ts | 0 .../packages => packages}/effect/src/Exit.ts | 0 .../packages => packages}/effect/src/Fiber.ts | 0 .../effect/src/FiberHandle.ts | 0 .../effect/src/FiberMap.ts | 0 .../effect/src/FiberSet.ts | 0 .../effect/src/FileSystem.ts | 82 +- .../effect/src/Filter.ts | 0 .../effect/src/Formatter.ts | 0 .../effect/src/Function.ts | 0 .../packages => packages}/effect/src/Graph.ts | 0 .../packages => packages}/effect/src/HKT.ts | 0 .../packages => packages}/effect/src/Hash.ts | 0 .../effect/src/HashMap.ts | 0 .../effect/src/HashRing.ts | 0 .../effect/src/HashSet.ts | 0 .../effect/src/Inspectable.ts | 0 .../effect/src/Iterable.ts | 0 .../effect/src/JsonPatch.ts | 0 .../effect/src/JsonPointer.ts | 0 .../effect/src/JsonSchema.ts | 0 .../packages => packages}/effect/src/Latch.ts | 0 .../packages => packages}/effect/src/Layer.ts | 0 .../effect/src/LayerMap.ts | 0 .../effect/src/LayerRef.ts | 0 .../effect/src/LogLevel.ts | 0 .../effect/src/Logger.ts | 0 .../effect/src/ManagedRuntime.ts | 0 .../packages => packages}/effect/src/Match.ts | 77 +- .../effect/src/Metric.ts | 0 .../effect/src/MutableHashMap.ts | 0 .../effect/src/MutableHashSet.ts | 0 .../effect/src/MutableList.ts | 0 .../effect/src/MutableRef.ts | 0 .../effect/src/Newtype.ts | 0 .../effect/src/NonEmptyIterable.ts | 0 .../effect/src/Number.ts | 0 .../packages => packages}/effect/src/Optic.ts | 0 .../effect/src/Option.ts | 0 .../packages => packages}/effect/src/Order.ts | 0 .../effect/src/Ordering.ts | 0 .../effect/src/PartitionedSemaphore.ts | 0 .../packages => packages}/effect/src/Path.ts | 59 +- .../effect/src/Pipeable.ts | 0 .../effect/src/PlatformError.ts | 0 .../packages => packages}/effect/src/Pool.ts | 0 .../effect/src/Predicate.ts | 0 .../effect/src/PrimaryKey.ts | 0 .../effect/src/PubSub.ts | 0 .../packages => packages}/effect/src/Pull.ts | 0 .../packages => packages}/effect/src/Queue.ts | 0 .../effect/src/Random.ts | 0 .../packages => packages}/effect/src/RcMap.ts | 0 .../packages => packages}/effect/src/RcRef.ts | 0 .../effect/src/Record.ts | 0 .../effect/src/Redactable.ts | 0 .../effect/src/Redacted.ts | 0 .../effect/src/Reducer.ts | 0 .../packages => packages}/effect/src/Ref.ts | 0 .../effect/src/References.ts | 0 .../effect/src/RegExp.ts | 0 .../effect/src/Request.ts | 0 .../effect/src/RequestResolver.ts | 14 +- .../effect/src/Resource.ts | 0 .../effect/src/Result.ts | 0 .../effect/src/Runtime.ts | 0 .../effect/src/Schedule.ts | 0 .../effect/src/Scheduler.ts | 9 + .../effect/src/Schema.ts | 703 +- .../effect/src/SchemaAST.ts | 0 .../effect/src/SchemaGetter.ts | 82 +- .../effect/src/SchemaIssue.ts | 0 .../effect/src/SchemaParser.ts | 0 .../effect/src/SchemaRepresentation.ts | 0 .../effect/src/SchemaTransformation.ts | 0 .../packages => packages}/effect/src/Scope.ts | 0 .../effect/src/ScopedCache.ts | 0 .../effect/src/ScopedRef.ts | 0 .../effect/src/Semaphore.ts | 0 .../packages => packages}/effect/src/Sink.ts | 63 +- .../effect/src/StandardSchema.ts | 0 .../packages => packages}/effect/src/Stdio.ts | 11 +- .../effect/src/Stream.ts | 41 +- .../effect/src/String.ts | 21 +- .../effect/src/Struct.ts | 0 .../effect/src/SubscriptionRef.ts | 0 .../effect/src/Symbol.ts | 0 .../effect/src/SynchronizedRef.ts | 0 .../packages => packages}/effect/src/Take.ts | 0 .../effect/src/Terminal.ts | 0 .../effect/src/Tracer.ts | 0 .../packages => packages}/effect/src/Trie.ts | 0 .../packages => packages}/effect/src/Tuple.ts | 0 .../effect/src/TxChunk.ts | 0 .../effect/src/TxDeferred.ts | 0 .../effect/src/TxHashMap.ts | 0 .../effect/src/TxHashSet.ts | 0 .../effect/src/TxPriorityQueue.ts | 0 .../effect/src/TxPubSub.ts | 0 .../effect/src/TxQueue.ts | 0 .../effect/src/TxReentrantLock.ts | 0 .../packages => packages}/effect/src/TxRef.ts | 6 +- .../effect/src/TxSemaphore.ts | 0 .../effect/src/TxSubscriptionRef.ts | 0 .../packages => packages}/effect/src/Types.ts | 0 .../effect/src/UndefinedOr.ts | 0 .../packages => packages}/effect/src/Unify.ts | 0 .../packages => packages}/effect/src/Utils.ts | 13 +- .../effect/src/Version.ts | 0 .../effect/src/ai/AiError.ts | 4 +- .../src/ai/AnthropicStructuredOutput.ts | 4 +- .../effect/src/ai/Chat.ts | 4 +- .../effect/src/ai/Decision.ts | 91 +- .../effect/src/ai/DecisionModel.ts | 2 +- .../effect/src/ai/EmbeddingModel.ts | 0 .../effect/src/ai/IdGenerator.ts | 0 .../effect/src/ai/LanguageModel.ts | 204 +- .../effect/src/ai/McpProtocol.ts | 0 .../effect/src/ai/McpSchema.ts | 0 .../effect/src/ai/McpServer.ts | 432 +- .../effect/src/ai/Model.ts | 0 .../effect/src/ai/OpenAiStructuredOutput.ts | 0 .../effect/src/ai/Prompt.ts | 34 +- .../effect/src/ai/Response.ts | 9 +- .../effect/src/ai/ResponseIdTracker.ts | 0 .../effect/src/ai/Telemetry.ts | 0 .../effect/src/ai/Tokenizer.ts | 0 .../effect/src/ai/Tool.ts | 0 .../effect/src/ai/Toolkit.ts | 0 .../effect/src/ai/index.ts | 0 .../src/ai/internal/codec-transformer.ts | 0 .../effect/src/ai/internal/mcpCore.ts | 0 .../effect/src/ai/internal/mcpProtocol.ts | 3 + .../ai/internal/mcpProtocol/v2024_11_05.ts | 0 .../ai/internal/mcpProtocol/v2025_03_26.ts | 0 .../ai/internal/mcpProtocol/v2025_06_18.ts | 0 .../ai/internal/mcpProtocol/v2025_11_25.ts | 0 .../ai/internal/mcpProtocol/v2026_07_28.ts | 5 + .../src/ai/internal/mcpProtocolRegistry.ts | 0 .../effect/src/ai/internal/mcpRuntime.ts | 2 + .../src/ai/internal/mcpSchema/v2024_11_05.ts | 0 .../src/ai/internal/mcpSchema/v2025_03_26.ts | 0 .../src/ai/internal/mcpSchema/v2025_06_18.ts | 0 .../src/ai/internal/mcpSchema/v2025_11_25.ts | 0 .../src/ai/internal/mcpSchema/v2026_07_28.ts | 0 .../src/ai/internal/mcpStatefulRuntime.ts | 0 .../src/ai/internal/structured-output.ts | 0 .../effect/src/cli/Argument.ts | 0 .../effect/src/cli/CliConfig.ts | 0 .../effect/src/cli/CliError.ts | 0 .../effect/src/cli/CliOutput.ts | 49 +- .../effect/src/cli/Command.ts | 7 +- .../effect/src/cli/Completions.ts | 20 +- .../effect/src/cli/Flag.ts | 37 + .../effect/src/cli/GlobalFlag.ts | 30 +- .../effect/src/cli/HelpDoc.ts | 0 .../effect/src/cli/Param.ts | 44 + .../effect/src/cli/Primitive.ts | 0 .../effect/src/cli/Prompt.ts | 308 +- .../effect/src/cli/index.ts | 0 .../effect/src/cli/internal/ansi.ts | 29 +- .../effect/src/cli/internal/auto-suggest.ts | 0 .../effect/src/cli/internal/command.ts | 3 +- .../src/cli/internal/completions/bash.ts | 7 +- .../cli/internal/completions/descriptor.ts | 11 +- .../src/cli/internal/completions/fish.ts | 8 +- .../cli/internal/completions/powershell.ts | 325 + .../src/cli/internal/completions/zsh.ts | 16 +- .../effect/src/cli/internal/config.ts | 0 .../effect/src/cli/internal/help.ts | 0 .../effect/src/cli/internal/lexer.ts | 0 .../effect/src/cli/internal/parser.ts | 15 +- .../effect/src/cli/internal/wizard.ts | 63 +- .../effect/src/cluster/ClusterCron.ts | 0 .../effect/src/cluster/ClusterError.ts | 0 .../effect/src/cluster/ClusterMetrics.ts | 0 .../effect/src/cluster/ClusterSchema.ts | 32 + .../src/cluster/ClusterWorkflowEngine.ts | 216 +- .../effect/src/cluster/DeliverAt.ts | 0 .../effect/src/cluster/Entity.ts | 0 .../effect/src/cluster/EntityAddress.ts | 0 .../effect/src/cluster/EntityId.ts | 0 .../effect/src/cluster/EntityProxy.ts | 0 .../effect/src/cluster/EntityProxyServer.ts | 0 .../effect/src/cluster/EntityResource.ts | 0 .../effect/src/cluster/EntityType.ts | 0 .../effect/src/cluster/Envelope.ts | 0 .../effect/src/cluster/HttpRunner.ts | 0 .../effect/src/cluster/K8sHttpClient.ts | 0 .../effect/src/cluster/K8sTypes.ts | 0 .../effect/src/cluster/MachineId.ts | 0 .../effect/src/cluster/Message.ts | 0 .../effect/src/cluster/MessageStorage.ts | 76 +- .../effect/src/cluster/Reply.ts | 0 .../effect/src/cluster/Runner.ts | 0 .../effect/src/cluster/RunnerAddress.ts | 0 .../effect/src/cluster/RunnerHealth.ts | 0 .../effect/src/cluster/RunnerServer.ts | 0 .../effect/src/cluster/RunnerStorage.ts | 0 .../effect/src/cluster/Runners.ts | 148 +- .../effect/src/cluster/ShardId.ts | 0 .../effect/src/cluster/Sharding.ts | 277 +- .../effect/src/cluster/ShardingConfig.ts | 0 .../src/cluster/ShardingRegistrationEvent.ts | 0 .../effect/src/cluster/SingleRunner.ts | 0 .../effect/src/cluster/Singleton.ts | 0 .../effect/src/cluster/SingletonAddress.ts | 0 .../effect/src/cluster/Snowflake.ts | 0 .../effect/src/cluster/SocketRunner.ts | 0 .../effect/src/cluster/SqlMessageStorage.ts | 202 +- .../effect/src/cluster/SqlRunnerStorage.ts | 352 +- .../effect/src/cluster/TestRunner.ts | 0 .../effect/src/cluster/index.ts | 0 .../src/cluster/internal/clusterAbandon.ts | 0 .../src/cluster/internal/entityActivation.ts | 0 .../src/cluster/internal/entityManager.ts | 208 +- .../src/cluster/internal/entityReaper.ts | 0 .../effect/src/cluster/internal/hash.ts | 0 .../src/cluster/internal/interruptors.ts | 0 .../src/cluster/internal/resourceMap.ts | 0 .../src/cluster/internal/resourceRef.ts | 0 .../effect/src/cluster/internal/shardLock.ts | 0 .../effect/src/devtools/DevTools.ts | 0 .../effect/src/devtools/DevToolsClient.ts | 0 .../effect/src/devtools/DevToolsSchema.ts | 0 .../effect/src/devtools/DevToolsServer.ts | 0 .../effect/src/devtools/index.ts | 0 .../effect/src/encoding/Base64.ts | 0 .../effect/src/encoding/Base64Url.ts | 0 .../effect/src/encoding/EncodingError.ts | 0 .../effect/src/encoding/Hex.ts | 0 .../effect/src/encoding/Ini.ts | 0 .../effect/src/encoding/Ndjson.ts | 0 .../effect/src/encoding/SchemaBinary.ts | 0 .../effect/src/encoding/Sse.ts | 150 +- .../effect/src/encoding/Toml.ts | 0 .../effect/src/encoding/Yaml.ts | 0 .../effect/src/encoding/index.ts | 0 .../effect/src/eventlog/Event.ts | 0 .../effect/src/eventlog/EventGroup.ts | 0 .../effect/src/eventlog/EventJournal.ts | 0 .../effect/src/eventlog/EventLog.ts | 0 .../effect/src/eventlog/EventLogEncryption.ts | 0 .../effect/src/eventlog/EventLogMessage.ts | 0 .../effect/src/eventlog/EventLogRemote.ts | 0 .../effect/src/eventlog/EventLogServer.ts | 0 .../src/eventlog/EventLogServerEncrypted.ts | 0 .../src/eventlog/EventLogServerUnencrypted.ts | 0 .../src/eventlog/EventLogSessionAuth.ts | 0 .../effect/src/eventlog/SqlEventJournal.ts | 0 .../eventlog/SqlEventLogServerEncrypted.ts | 0 .../eventlog/SqlEventLogServerUnencrypted.ts | 0 .../effect/src/eventlog/index.ts | 0 .../internal/identityRootSecretDerivation.ts | 0 packages/effect/src/graphql/GraphQL.ts | 494 + packages/effect/src/graphql/GraphQLClient.ts | 924 + .../effect/src/graphql/GraphQLClientError.ts | 542 + packages/effect/src/graphql/GraphQLGroup.ts | 167 + .../effect/src/graphql/GraphQLMiddleware.ts | 253 + .../effect/src/graphql/GraphQLProtocol.ts | 1072 + .../effect/src/graphql/GraphQLResolver.ts | 728 + packages/effect/src/graphql/index.ts | 48 + .../effect/src/http-api/HttpApi.ts | 0 .../effect/src/http-api/HttpApiBuilder.ts | 0 .../effect/src/http-api/HttpApiClient.ts | 3 +- .../effect/src/http-api/HttpApiEndpoint.ts | 0 .../effect/src/http-api/HttpApiError.ts | 0 .../effect/src/http-api/HttpApiGroup.ts | 0 .../effect/src/http-api/HttpApiMiddleware.ts | 0 .../effect/src/http-api/HttpApiScalar.ts | 5 + .../effect/src/http-api/HttpApiSchema.ts | 0 .../effect/src/http-api/HttpApiSecurity.ts | 0 .../effect/src/http-api/HttpApiSwagger.ts | 0 .../effect/src/http-api/HttpApiTest.ts | 0 .../effect/src/http-api/OpenApi.ts | 0 .../effect/src/http-api/index.ts | 0 .../effect/src/http-api/internal/html.ts | 0 .../src/http-api/internal/httpApiScalar.ts | 0 .../src/http-api/internal/httpApiSwagger.ts | 0 .../effect/src/http-api/internal/mediaType.ts | 0 .../effect/src/http-api/internal/path.ts | 0 .../effect/src/http/Cookies.ts | 0 .../effect/src/http/Etag.ts | 0 .../effect/src/http/FetchHttpClient.ts | 9 +- .../effect/src/http/FindMyWay.ts | 0 .../http/FindMyWay/internal/queryString.ts | 0 .../src/http/FindMyWay/internal/router.ts | 14 +- .../effect/src/http/Headers.ts | 15 +- .../effect/src/http/HttpBody.ts | 0 .../effect/src/http/HttpClient.ts | 119 +- .../effect/src/http/HttpClientError.ts | 16 +- .../effect/src/http/HttpClientRequest.ts | 105 +- .../effect/src/http/HttpClientResponse.ts | 0 .../effect/src/http/HttpEffect.ts | 16 +- .../effect/src/http/HttpIncomingMessage.ts | 0 .../effect/src/http/HttpMethod.ts | 0 .../effect/src/http/HttpMiddleware.ts | 147 +- .../effect/src/http/HttpPlatform.ts | 0 .../effect/src/http/HttpRouter.ts | 108 +- .../effect/src/http/HttpServer.ts | 0 .../effect/src/http/HttpServerError.ts | 12 +- .../effect/src/http/HttpServerRequest.ts | 4 +- .../effect/src/http/HttpServerRespondable.ts | 0 .../effect/src/http/HttpServerResponse.ts | 44 +- .../effect/src/http/HttpStaticServer.ts | 14 +- .../effect/src/http/HttpStatus.ts | 0 .../effect/src/http/HttpTraceContext.ts | 12 +- .../effect/src/http/Mime.ts | 0 .../effect/src/http/Multipart.ts | 18 +- .../effect/src/http/MultipartParser.ts | 0 .../src/http/MultipartParser/HeadersParser.ts | 0 .../effect/src/http/MultipartParser/Search.ts | 0 .../MultipartParser/internal/contentType.ts | 0 .../http/MultipartParser/internal/headers.ts | 0 .../MultipartParser/internal/multipart.ts | 0 .../http/MultipartParser/internal/search.ts | 0 .../effect/src/http/Template.ts | 0 .../effect/src/http/Url.ts | 3 +- .../effect/src/http/UrlParams.ts | 232 +- .../effect/src/http/index.ts | 0 .../effect/src/http/internal/compression.ts | 20 +- .../src/http/internal/compressionBody.ts | 56 + .../effect/src/http/internal/decompression.ts | 226 + .../effect/src/http/internal/headers.ts | 0 .../effect/src/http/internal/httpBody.ts | 0 .../effect/src/http/internal/mimeTypes.ts | 0 .../src/http/internal/preResponseHandler.ts | 0 .../effect/src/http/internal/tracing.ts | 25 +- .../effect/src/http/internal/urlParams.ts | 5 + .../packages => packages}/effect/src/index.ts | 0 .../effect/src/internal/arbitrary/array.ts | 0 .../src/internal/arbitrary/bigDecimal.ts | 0 .../effect/src/internal/arbitrary/bigInt.ts | 0 .../effect/src/internal/arbitrary/model.ts | 0 .../effect/src/internal/arbitrary/regexp.ts | 0 .../effect/src/internal/arbitrary/runner.ts | 0 .../effect/src/internal/arbitrary/schema.ts | 0 .../effect/src/internal/array.ts | 0 .../effect/src/internal/core.ts | 22 +- .../effect/src/internal/count.ts | 0 .../effect/src/internal/dateTime.ts | 23 +- .../effect/src/internal/doNotation.ts | 0 .../effect/src/internal/effect.ts | 46 +- .../effect/src/internal/equal.ts | 0 .../effect/src/internal/errors.ts | 0 .../effect/src/internal/executionPlan.ts | 0 .../effect/src/internal/graph.ts | 0 .../effect/src/internal/graphCsr.ts | 0 packages/effect/src/internal/graphql.ts | 286 + .../effect/src/internal/graphqlHeaders.ts | 11 + .../effect/src/internal/hash.ts | 0 .../effect/src/internal/hashMap.ts | 0 .../effect/src/internal/hashSet.ts | 0 .../effect/src/internal/layer.ts | 0 .../effect/src/internal/matcher.ts | 0 .../effect/src/internal/metric.ts | 0 .../effect/src/internal/option.ts | 0 .../effect/src/internal/persistence.ts | 0 .../effect/src/internal/random.ts | 0 .../effect/src/internal/rcRef.ts | 0 .../effect/src/internal/record.ts | 0 .../effect/src/internal/redacted.ts | 0 .../effect/src/internal/references.ts | 0 .../effect/src/internal/request.ts | 34 +- .../effect/src/internal/result.ts | 0 .../effect/src/internal/schedule.ts | 0 .../effect/src/internal/schema/annotations.ts | 0 .../effect/src/internal/schema/cause.ts | 0 .../effect/src/internal/schema/codegen.ts | 0 .../src/internal/schema/compilerRegistry.ts | 0 .../internal/schema/fromJsonSchemaDocument.ts | 0 .../src/internal/schema/fromRepresentation.ts | 0 .../effect/src/internal/schema/interpreter.ts | 0 .../effect/src/internal/schema/make.ts | 0 .../effect/src/internal/schema/parser.ts | 0 .../src/internal/schema/standardSchema.ts | 0 .../src/internal/schema/toCodeDocument.ts | 8 +- .../effect/src/internal/schema/toCodec.ts | 0 .../src/internal/schema/toDifferJsonPatch.ts | 0 .../src/internal/schema/toEncoderXml.ts | 0 .../src/internal/schema/toEquivalence.ts | 0 .../effect/src/internal/schema/toFormatter.ts | 0 .../effect/src/internal/schema/toIso.ts | 0 .../internal/schema/toJsonSchemaDocument.ts | 36 +- .../src/internal/schema/toRepresentation.ts | 0 .../effect/src/internal/schemaError.ts | 0 .../effect/src/internal/stackTraceLimit.ts | 0 .../effect/src/internal/stream.ts | 0 .../effect/src/internal/tracer.ts | 0 .../effect/src/internal/trie.ts | 0 .../effect/src/internal/ulid.ts | 0 .../effect/src/internal/uuid.ts | 0 packages/effect/src/internal/version.ts | 2 + packages/effect/src/net/AddressResolver.ts | 222 + packages/effect/src/net/Dns.ts | 1035 + packages/effect/src/net/Host.ts | 544 + .../effect/src/net/IpInterface.ts | 124 +- .../effect/src/net/IpNetwork.ts | 133 +- .../effect/src/net/NetAddress.ts | 652 +- .../effect/src/net/index.ts | 18 + .../effect/src/observability/Otlp.ts | 18 + .../effect/src/observability/OtlpExporter.ts | 17 +- .../effect/src/observability/OtlpLogger.ts | 33 +- .../effect/src/observability/OtlpMetrics.ts | 66 +- .../effect/src/observability/OtlpResource.ts | 0 .../src/observability/OtlpSerialization.ts | 0 .../effect/src/observability/OtlpTracer.ts | 35 +- .../src/observability/PrometheusMetrics.ts | 1 + .../effect/src/observability/index.ts | 0 .../src/observability/internal/otlpEnv.ts | 0 .../observability/internal/otlpProtobuf.ts | 0 .../src/observability/internal/protobuf.ts | 0 .../effect/src/persistence/KeyValueStore.ts | 0 .../effect/src/persistence/Persistable.ts | 0 .../effect/src/persistence/PersistedCache.ts | 0 .../effect/src/persistence/PersistedQueue.ts | 199 +- .../effect/src/persistence/Persistence.ts | 0 .../effect/src/persistence/RateLimiter.ts | 0 .../effect/src/persistence/Redis.ts | 0 .../effect/src/persistence/index.ts | 0 .../effect/src/process/ChildProcess.ts | 0 .../effect/src/process/ChildProcessSpawner.ts | 0 .../effect/src/process/index.ts | 0 .../effect/src/reactivity/AsyncResult.ts | 31 + .../effect/src/reactivity/Atom.ts | 0 .../effect/src/reactivity/AtomHttpApi.ts | 0 .../effect/src/reactivity/AtomRef.ts | 0 .../effect/src/reactivity/AtomRegistry.ts | 4 +- .../effect/src/reactivity/AtomRpc.ts | 0 .../effect/src/reactivity/Hydration.ts | 0 .../effect/src/reactivity/Reactivity.ts | 0 .../effect/src/reactivity/index.ts | 0 .../effect/src/rpc/Rpc.ts | 0 .../effect/src/rpc/RpcClient.ts | 67 +- .../effect/src/rpc/RpcClientError.ts | 0 .../effect/src/rpc/RpcGroup.ts | 0 .../effect/src/rpc/RpcMessage.ts | 0 .../effect/src/rpc/RpcMiddleware.ts | 0 .../effect/src/rpc/RpcSchema.ts | 0 .../effect/src/rpc/RpcSerialization.ts | 0 .../effect/src/rpc/RpcServer.ts | 155 +- .../effect/src/rpc/RpcTest.ts | 0 .../effect/src/rpc/RpcWorker.ts | 0 .../effect/src/rpc/Utils.ts | 0 .../effect/src/rpc/index.ts | 0 .../effect/src/rpc/internal/tracing.ts | 0 .../effect/src/schema/Model.ts | 0 .../effect/src/schema/SchemaAOTCompiler.ts | 0 .../src/schema/SchemaAOTCompiler/Build.ts | 0 .../effect/src/schema/SchemaCompiler.ts | 0 .../src/schema/SchemaCompiler/runtime.ts | 0 .../effect/src/schema/SchemaJITCompiler.ts | 0 .../src/schema/SchemaJITCompiler/enable.ts | 0 .../effect/src/schema/VariantSchema.ts | 0 .../effect/src/schema/index.ts | 0 packages/effect/src/socket/DatagramSocket.ts | 919 + .../effect/src/socket/Socket.ts | 30 +- .../effect/src/socket/SocketServer.ts | 0 .../effect/src/socket/index.ts | 6 + .../effect/src/sql/Migrator.ts | 26 +- .../effect/src/sql/SqlClient.ts | 62 +- .../effect/src/sql/SqlConnection.ts | 0 .../effect/src/sql/SqlError.ts | 0 .../effect/src/sql/SqlModel.ts | 0 .../effect/src/sql/SqlResolver.ts | 0 .../effect/src/sql/SqlSchema.ts | 0 .../effect/src/sql/SqlStream.ts | 0 .../effect/src/sql/Statement.ts | 0 .../effect/src/sql/index.ts | 0 .../effect/src/testing/TestClock.ts | 173 +- .../effect/src/testing/TestConsole.ts | 0 packages/effect/src/testing/TestCrypto.ts | 94 + .../effect/src/testing/TestSchema.ts | 0 .../effect/src/testing/index.ts | 6 + .../effect/src/workers/Transferable.ts | 0 .../effect/src/workers/Worker.ts | 0 .../effect/src/workers/WorkerError.ts | 0 .../effect/src/workers/WorkerRunner.ts | 0 .../effect/src/workers/index.ts | 0 .../effect/src/workflow/Activity.ts | 1 + .../effect/src/workflow/DurableClock.ts | 0 .../effect/src/workflow/DurableDeferred.ts | 179 +- .../effect/src/workflow/DurableQueue.ts | 0 .../effect/src/workflow/Workflow.ts | 36 +- .../effect/src/workflow/WorkflowEngine.ts | 135 +- .../effect/src/workflow/WorkflowProxy.ts | 0 .../src/workflow/WorkflowProxyServer.ts | 0 .../effect/src/workflow/index.ts | 0 .../effect/src/workflow/internal/crypto.ts | 0 .../effect/test/Array.test.ts | 0 .../effect/test/ArrayNaNIndex.test.ts | 0 .../effect/test/AtomRef.test.ts | 0 .../effect/test/BigDecimal.test.ts | 0 .../effect/test/BigInt.test.ts | 0 .../effect/test/Boolean.test.ts | 0 .../effect/test/Brand.test.ts | 0 .../effect/test/ByteSize.test.ts | 0 .../effect/test/Cache.test.ts | 0 .../effect/test/Cause.test.ts | 0 .../effect/test/CauseMapAnnotations.test.ts | 0 .../effect/test/Channel.test.ts | 20 + .../effect/test/Chunk.test.ts | 0 .../effect/test/Clock.test.ts | 0 .../effect/test/Combiner.test.ts | 0 .../effect/test/Config.test.ts | 0 .../effect/test/ConfigProvider.test.ts | 47 + .../effect/test/Context.test.ts | 70 +- .../effect/test/Cron.test.ts | 0 .../effect/test/Crypto.test.ts | 26 +- .../effect/test/Data.test.ts | 0 .../effect/test/DateTime.test.ts | 38 + .../effect/test/Deferred.test.ts | 0 .../effect/test/Duration.test.ts | 20 + .../effect/test/Effect.test.ts | 196 + .../effect/test/EffectEager.test.ts | 0 .../effect/test/EffectKeepAlive.test.ts | 0 .../effect/test/Effectable.test.ts | 0 .../effect/test/Equal.test.ts | 0 .../effect/test/Equivalence.test.ts | 0 .../effect/test/ExecutionPlan.test.ts | 0 .../effect/test/Exit.test.ts | 0 .../effect/test/Fiber.test.ts | 0 .../effect/test/FiberHandle.test.ts | 0 .../effect/test/FiberMap.test.ts | 0 .../effect/test/FiberSet.test.ts | 0 .../test/FiberSetRuntimePropagation.test.ts | 0 .../effect/test/FileSystem.test-utils.ts | 63 + .../effect/test/Formatter.test.ts | 0 .../effect/test/Function.test.ts | 0 .../effect/test/Graph.test.ts | 0 .../effect/test/Hash.test.ts | 0 .../effect/test/HashMap.test.ts | 0 .../effect/test/HashRing.test.ts | 0 .../effect/test/HashSet.test.ts | 0 .../effect/test/HttpClient.test.ts | 0 .../effect/test/Iterable.test.ts | 0 .../effect/test/JsonPatch.test.ts | 0 .../effect/test/JsonPointer.test.ts | 0 .../effect/test/JsonSchema.test.ts | 0 .../effect/test/Latch.test.ts | 0 .../effect/test/Layer.test.ts | 0 .../effect/test/LayerMap.test.ts | 0 .../effect/test/LayerRef.test.ts | 0 .../effect/test/LogLevel.test.ts | 0 .../effect/test/Logger.test.ts | 0 .../effect/test/ManagedRuntime.test.ts | 0 .../effect/test/Match.test.ts | 0 .../effect/test/Metric.test.ts | 0 .../effect/test/Migrator.test.ts | 0 .../effect/test/MutableHashMap.test.ts | 0 .../effect/test/MutableHashSet.test.ts | 0 .../effect/test/MutableList.test.ts | 0 .../effect/test/Newtype.test.ts | 0 .../effect/test/Number.test.ts | 0 .../effect/test/Optic.test.ts | 0 .../effect/test/Option.test.ts | 0 .../effect/test/Order.test.ts | 0 .../effect/test/Ordering.test.ts | 0 .../effect/test/PartitionedSemaphore.test.ts | 0 packages/effect/test/Path.test.ts | 22 + .../effect/test/Pathfinding.test.ts | 0 .../effect/test/Pool.test.ts | 0 .../effect/test/Predicate.test.ts | 0 .../effect/test/PubSub.test.ts | 0 .../effect/test/Pull.test.ts | 0 .../effect/test/Queue.test.ts | 0 .../effect/test/Random.test.ts | 0 .../effect/test/RcMap.test.ts | 0 .../effect/test/RcRef.test.ts | 0 .../effect/test/Record.test.ts | 0 .../effect/test/Redacted.test.ts | 0 .../effect/test/Reducer.test.ts | 0 .../effect/test/Ref.test.ts | 0 .../effect/test/Request.test.ts | 0 packages/effect/test/RequestResolver.test.ts | 63 + .../effect/test/Resource.test.ts | 0 .../effect/test/Result.test.ts | 0 .../effect/test/Schedule.test.ts | 0 .../effect/test/Scheduler.test.ts | 0 .../effect/test/Scope.test.ts | 0 .../effect/test/ScopedCache.test.ts | 0 .../effect/test/ScopedRef.test.ts | 0 .../effect/test/Semaphore.test.ts | 0 .../effect/test/Sink.test.ts | 34 + .../effect/test/StackCapture.test.ts | 56 + .../effect/test/StackTraceLimit.test.ts | 0 .../effect/test/Stdio.test.ts | 19 +- .../effect/test/Stream.test.ts | 19 + .../effect/test/String.test.ts | 21 + .../effect/test/Struct.test.ts | 0 .../effect/test/SubscriptionRef.test.ts | 0 .../effect/test/Symbol.test.ts | 0 .../SynchronizedRef.modifySomeEffect.test.ts | 0 .../effect/test/SynchronizedRef.test.ts | 0 .../effect/test/TestClock.test.ts | 97 + .../effect/test/Tracer.test.ts | 0 .../effect/test/Trie.test.ts | 0 .../effect/test/Tuple.test.ts | 0 .../effect/test/TxChunk.test.ts | 0 .../effect/test/TxDeferred.test.ts | 0 .../effect/test/TxHashMap.test.ts | 0 .../effect/test/TxHashSet.test.ts | 0 .../effect/test/TxPriorityQueue.test.ts | 0 .../effect/test/TxPubSub.test.ts | 0 .../effect/test/TxQueue.test.ts | 0 .../effect/test/TxReentrantLock.test.ts | 0 .../effect/test/TxSemaphore.test.ts | 0 .../effect/test/TxSubscriptionRef.test.ts | 0 .../effect/test/UndefinedOr.test.ts | 0 .../effect/test/Version.test.ts | 0 .../effect/test/ai/AiError.test.ts | 15 +- .../test/ai/AnthropicStructuredOutput.test.ts | 0 ...opicStructuredOutputRepresentation.test.ts | 0 .../effect/test/ai/Chat.test.ts | 30 +- .../effect/test/ai/DecisionModel.test.ts | 30 +- .../effect/test/ai/EmbeddingModel.test.ts | 0 .../effect/test/ai/LanguageModel.test.ts | 192 + .../ai/LanguageModelRepresentation.test.ts | 0 .../ai/LanguageModelTrackerLifecycle.test.ts | 0 .../effect/test/ai/McpSchema.test.ts | 0 .../McpServer/LayerHttpRequestScoped.test.ts | 364 + .../McpConformance/BaseProtocolTest.ts | 0 .../McpConformance/CompletionTest.ts | 0 .../McpConformance/ElicitationTest.ts | 58 + .../McpServer/McpConformance/LifecycleTest.ts | 0 .../McpServer/McpConformance/LoggingTest.ts | 0 .../McpConformance/McpConformance.ts | 10 +- .../McpConformance/McpConformanceFixtures.ts | 6 +- .../McpServer/McpConformance/McpTestPeer.ts | 0 .../McpConformance/MultiRoundTripTest.ts | 0 .../McpServer/McpConformance/PromptsTest.ts | 0 .../McpServer/McpConformance/ResourcesTest.ts | 0 .../ai/McpServer/McpConformance/RootsTest.ts | 0 .../McpServer/McpConformance/SamplingTest.ts | 0 .../McpConformance/SubscriptionsTest.ts | 5 +- .../ai/McpServer/McpConformance/ToolsTest.ts | 0 .../McpConformance/TransportsTest.ts | 0 .../McpServer/McpConformance/UtilitiesTest.ts | 36 + .../test/ai/McpServer/McpProtocol.test.ts | 0 .../test/ai/McpServer/McpServer.test.ts | 111 +- .../ai/McpServer/ProtocolAdapters.test.ts | 0 .../ai/McpServer/TestUtils/McpHttpHarness.ts | 0 .../ai/McpServer/TestUtils/McpHttpResponse.ts | 0 .../ai/McpServer/TestUtils/McpServerLayer.ts | 21 +- .../ai/McpServer/TestUtils/McpStdioHarness.ts | 0 .../McpServer/TestUtils/McpWorkerdHarness.ts | 58 + .../effect/test/ai/McpServer/Workerd.test.ts | 95 + .../test/ai/McpServer/fixtures/workerd.ts | 36 + .../test/ai/McpServer/v2024_11_05.test.ts | 0 .../test/ai/McpServer/v2025_03_26.test.ts | 0 .../test/ai/McpServer/v2025_06_18.test.ts | 0 .../test/ai/McpServer/v2025_11_25.test.ts | 0 .../test/ai/McpServer/v2026_07_28.test.ts | 0 .../effect/test/ai/Model.test.ts | 0 .../test/ai/OpenAiStructuredOutput.test.ts | 17 +- ...enAiStructuredOutputRepresentation.test.ts | 0 .../effect/test/ai/Prompt.test.ts | 34 + .../effect/test/ai/Response.test.ts | 0 .../effect/test/ai/ResponseIdTracker.test.ts | 0 .../effect/test/ai/Tokenizer.test.ts | 0 .../effect/test/ai/Tool.test.ts | 0 .../effect/test/ai/ToolRepresentation.test.ts | 0 .../effect/test/ai/utils.ts | 0 .../test/arbitrary/Arbitrary.array.test.ts | 0 .../test/arbitrary/Arbitrary.causes.test.ts | 0 .../arbitrary/Arbitrary.composition.test.ts | 0 .../arbitrary/Arbitrary.configuration.test.ts | 0 .../effect/test/arbitrary/Arbitrary.test.ts | 0 .../effect/test/cli/Ansi.test.ts | 0 .../effect/test/cli/Arguments.test.ts | 0 .../effect/test/cli/Command.test.ts | 87 +- .../effect/test/cli/Errors.test.ts | 0 .../effect/test/cli/Help.test.ts | 39 +- .../effect/test/cli/Lexer.test.ts | 0 .../effect/test/cli/LogLevel.test.ts | 0 .../effect/test/cli/Param.test.ts | 0 .../effect/test/cli/Primitive.test.ts | 0 .../effect/test/cli/Prompt.test.ts | 87 +- .../test/cli/completions/completions.test.ts | 150 +- .../test/cli/completions/descriptor.test.ts | 0 .../test/cli/fixtures/ComprehensiveCli.ts | 0 .../effect/test/cli/services/MockTerminal.ts | 0 .../effect/test/cli/services/TestActions.ts | 0 .../cluster/ClusterWorkflowEngine.test.ts | 671 +- .../effect/test/cluster/Entity.test.ts | 0 .../effect/test/cluster/EntityProxy.test.ts | 0 .../effect/test/cluster/Envelope.test.ts | 0 .../effect/test/cluster/K8sHttpClient.test.ts | 0 .../test/cluster/MessageStorage.test.ts | 0 .../effect/test/cluster/ResourceMap.test.ts | 0 .../effect/test/cluster/ResourceRef.test.ts | 0 .../effect/test/cluster/RunnerServer.test.ts | 0 .../effect/test/cluster/RunnerStorage.test.ts | 0 .../effect/test/cluster/Runners.test.ts | 87 +- .../effect/test/cluster/Sharding.test.ts | 706 +- .../test/cluster/ShardingConfig.test.ts | 0 .../effect/test/cluster/TestEntity.ts | 29 +- .../effect/test/cluster/interruptors.test.ts | 0 .../test/devtools/DevToolsClient.test.ts | 0 .../effect/test/encoding/Base64.test.ts | 12 + .../effect/test/encoding/Base64Url.test.ts | 0 .../test/encoding/EncodingError.test.ts | 0 .../effect/test/encoding/Hex.test.ts | 0 .../effect/test/encoding/Ini.test.ts | 0 .../effect/test/encoding/Ndjson.test.ts | 0 .../effect/test/encoding/SchemaBinary.test.ts | 0 .../effect/test/encoding/Sse.test.ts | 76 +- .../effect/test/encoding/Toml.test.ts | 0 .../effect/test/encoding/Yaml.test.ts | 0 .../effect/test/eventlog/EventJournal.test.ts | 0 .../effect/test/eventlog/EventLog.test.ts | 0 .../EventLogIdentityDerivation.test.ts | 0 .../test/eventlog/EventLogMessage.test.ts | 0 .../test/eventlog/EventLogRemote.test.ts | 0 .../EventLogServerUnencrypted.test.ts | 0 .../test/eventlog/EventLogSessionAuth.test.ts | 0 ...SqlEventLogServerUnencryptedStorageTest.ts | 0 .../test/fixtures/migrator/0001_first.js | 0 .../effect/test/fixtures/text.txt | 0 packages/effect/test/graphql/GraphQL.test.ts | 68 + .../graphql/GraphQLClient.makeTest.test.ts | 118 + .../test/graphql/GraphQLClient.pages.test.ts | 111 + .../GraphQLClient.subscriptions.test.ts | 138 + .../effect/test/graphql/GraphQLClient.test.ts | 195 + .../test/graphql/GraphQLClientError.test.ts | 85 + .../test/graphql/GraphQLProtocol.sse.test.ts | 167 + .../test/graphql/GraphQLProtocol.test.ts | 286 + .../graphql/GraphQLProtocol.webSocket.test.ts | 250 + .../test/graphql/GraphQLResolver.test.ts | 532 + packages/effect/test/graphql/fixtures.ts | 233 + .../effect/test/http/Cookies.test.ts | 0 .../effect/test/http/FetchHttpClient.test.ts | 0 .../http/FindMyWay/case-insensitive.test.ts | 0 .../http/FindMyWay/matching-order.test.ts | 0 .../test/http/FindMyWay/methods.test.ts | 0 .../http/FindMyWay/optional-params.test.ts | 0 .../http/FindMyWay/params-collisions.test.ts | 0 .../http/FindMyWay/path-params-match.test.ts | 0 .../test/http/FindMyWay/querystring.test.ts | 8 + .../effect/test/http/FindMyWay/regex.test.ts | 0 .../effect/test/http/Headers.test.ts | 0 .../effect/test/http/HttpBody.test.ts | 0 .../effect/test/http/HttpClient.test.ts | 483 +- .../test/http/HttpClientRequest.test.ts | 128 +- .../effect/test/http/HttpCompression.test.ts | 0 .../test/http/HttpDecompression.test.ts | 228 + .../effect/test/http/HttpEffect.test.ts | 69 + .../effect/test/http/HttpMiddleware.test.ts | 29 + .../effect/test/http/HttpPlatform.test.ts | 0 .../effect/test/http/HttpRouter.test.ts | 32 + .../effect/test/http/HttpServer.test.ts | 0 .../effect/test/http/HttpServerError.test.ts | 11 + .../test/http/HttpServerRequest.test.ts | 0 .../test/http/HttpServerRespondable.test.ts | 0 .../test/http/HttpServerResponse.test.ts | 58 +- .../effect/test/http/HttpStaticServer.test.ts | 15 + .../effect/test/http/HttpStatus.test.ts | 0 .../effect/test/http/HttpTraceContext.test.ts | 88 + .../effect/test/http/Mime.test.ts | 0 .../effect/test/http/Multipart.test.ts | 32 + .../test/http/SchemaRepresentation.test.ts | 0 .../effect/test/http/Template.test.ts | 0 .../effect/test/http/Url.test.ts | 0 packages/effect/test/http/UrlParams.test.ts | 144 + .../effect/test/httpapi/HttpApi.test.ts | 0 .../test/httpapi/HttpApiBuilder.test.ts | 0 .../effect/test/httpapi/HttpApiClient.test.ts | 0 .../test/httpapi/HttpApiDocumentation.test.ts | 0 .../test/httpapi/HttpApiEndpoint.test.ts | 0 .../effect/test/httpapi/HttpApiError.test.ts | 0 .../effect/test/httpapi/HttpApiSchema.test.ts | 0 .../test/httpapi/HttpApiSecurity.test.ts | 0 .../effect/test/httpapi/OpenApi.test.ts | 6 +- .../httpapi/OpenApiRepresentation.test.ts | 0 .../effect/test/internal/Uuid.test.ts | 0 .../effect/test/net/AddressResolver.test.ts | 87 + packages/effect/test/net/Dns.test.ts | 288 + packages/effect/test/net/Host.test.ts | 154 + .../effect/test/net/IpInterface.test.ts | 45 + .../effect/test/net/IpNetwork.test.ts | 39 + .../effect/test/net/NetAddress.test.ts | 155 +- .../observability/OtlpCompression.test.ts | 150 + .../test/observability/OtlpConfig.test.ts | 0 .../test/observability/OtlpEnvHeaders.test.ts | 0 .../test/observability/OtlpExporter.test.ts | 0 .../test/observability/OtlpLogger.test.ts | 14 +- .../test/observability/OtlpMetrics.test.ts | 31 + .../test/observability/OtlpResource.test.ts | 0 .../observability/OtlpSerialization.test.ts | 0 .../test/observability/OtlpTracer.test.ts | 54 + .../observability/PrometheusMetrics.test.ts | 19 + .../test/persistence/KeyValueStore.test.ts | 0 .../test/persistence/KeyValueStoreTest.ts | 0 .../test/persistence/PersistedCache.test.ts | 0 .../test/persistence/PersistedCacheTest.ts | 0 .../test/persistence/PersistedQueue.test.ts | 0 .../test/persistence/PersistedQueueSqlTest.ts | 59 + .../test/persistence/PersistedQueueTest.ts | 109 +- .../test/persistence/RateLimiter.test.ts | 0 .../test/persistence/RateLimiterTest.ts | 0 .../effect/test/persistence/Redis.test.ts | 0 .../effect/test/persistence/SqlCleanupTest.ts | 0 .../effect/test/process/ChildProcess.test.ts | 0 .../test/process/ChildProcessSpawnerTest.ts | 0 .../process/fixtures/bash/no-permissions.sh | 0 .../fixtures/bash/parent-exits-early.sh | 0 .../process/fixtures/bash/spawn-children.sh | 0 .../test/process/fixtures/config/SHOUTING | 0 .../test/process/fixtures/config/integer | 0 .../process/fixtures/config/nested/config | 0 .../test/process/fixtures/config/secret | 0 .../test/reactivity/AsyncResult.test.ts | 37 +- .../effect/test/reactivity/Atom.test.ts | 43 + .../test/reactivity/AtomHttpApi.test.ts | 4 +- .../effect/test/reactivity/AtomRpc.test.ts | 0 .../effect/test/rpc/Rpc.test.ts | 0 .../effect/test/rpc/RpcClient.test.ts | 166 +- .../effect/test/rpc/RpcSerialization.test.ts | 47 +- .../effect/test/rpc/RpcServer.test.ts | 118 +- .../effect/test/schema/CodePoints.test.ts | 0 .../effect/test/schema/Graph.test.ts | 0 .../effect/test/schema/HMR.test.ts | 0 .../effect/test/schema/Schema.test.ts | 50 +- .../test/schema/SchemaAOTCompiler.test.ts | 0 .../schema/SchemaAOTCompilerBuild.test.ts | 0 .../effect/test/schema/SchemaAST.test.ts | 0 .../test/schema/SchemaCompilerApi.test.ts | 0 .../test/schema/SchemaCompilerArray.test.ts | 0 .../schema/SchemaCompilerConcurrency.test.ts | 0 .../schema/SchemaCompilerConstruction.test.ts | 0 .../schema/SchemaCompilerRegression.test.ts | 0 .../test/schema/SchemaCompilerStartup.test.ts | 0 .../effect/test/schema/SchemaGetter.test.ts | 53 + .../effect/test/schema/SchemaIssue.test.ts | 0 .../test/schema/SchemaJITCompiler.test.ts | 0 .../SchemaJITCompilerCheckedSources.test.ts | 0 .../SchemaJITCompilerExcessProperties.test.ts | 0 .../schema/SchemaJITCompilerFallback.test.ts | 0 .../effect/test/schema/SchemaParser.test.ts | 0 .../test/schema/SchemaTransformation.test.ts | 0 .../test/schema/TemplateLiteral.test.ts | 0 .../effect/test/schema/VariantSchema.test.ts | 0 .../effect/test/schema/fixtures/HMR-Class.ts | 0 .../test/schema/fixtures/HMR-sanity-check.ts | 0 .../effect/test/schema/fixtures/aot-build.ts | 0 .../test/schema/fixtures/aot-import-guard.ts | 0 .../effect/test/schema/fixtures/aot-runner.ts | 0 .../effect/test/schema/fixtures/aot.ts | 0 .../test/schema/fixtures/construction.ts | 0 .../test/schema/jsonSchemaRoundTrip.test.ts | 0 .../representation/builtInRevivers.test.ts | 0 .../schema/representation/fromJson.test.ts | 0 .../fromJsonMultiDocument.test.ts | 0 .../fromJsonSchemaDocument.test.ts | 36 +- .../fromJsonSchemaMultiDocument.test.ts | 2 +- .../representation/fromRepresentation.test.ts | 0 .../fromRepresentations.test.ts | 0 .../schema/representation/makeCode.test.ts | 0 .../schemaToJsonSchemaDocument.test.ts | 0 .../stringTreeRoundtrip.test.ts | 0 .../toCodeDocument.annotations.test.ts | 0 .../representation/toCodeDocument.test.ts | 36 +- .../test/schema/representation/toJson.test.ts | 0 .../toJsonMultiDocument.test.ts | 0 .../toJsonSchemaDocument.test.ts | 25 +- .../toJsonSchemaMultiDocument.test.ts | 0 .../representation/toMultiDocument.test.ts | 0 .../representation/toRepresentation.test.ts | 0 .../representation/toRepresentations.test.ts | 0 .../effect/test/schema/toCodec.test.ts | 0 .../test/schema/toDifferJsonPatch.test.ts | 0 .../effect/test/schema/toEquivalence.test.ts | 0 .../effect/test/schema/toFormatter.test.ts | 0 .../effect/test/schema/toIso.test.ts | 0 .../test/schema/toJsonSchemaDocument.test.ts | 90 +- .../schema/toStandardJSONSchemaV1.test.ts | 0 .../test/schema/toStandardSchemaV1.test.ts | 0 .../effect/test/schema/v3-v4.test.ts | 0 .../effect/test/socket/DatagramSocket.test.ts | 344 + .../effect/test/socket/Socket.test.ts | 0 .../effect/test/sql/SqlClient.test.ts | 57 +- .../effect/test/sql/SqlError.test.ts | 0 .../effect/test/sql/SqlResolver.test.ts | 0 .../effect/test/sql/SqlSchema.test.ts | 0 .../effect/test/sql/Statement.test.ts | 27 + .../effect/test/testing/TestCrypto.test.ts | 43 + .../effect/test/testing/TestSchema.test.ts | 0 .../effect/test/utils/assert.ts | 0 .../effect/test/utils/chunkCoordination.ts | 0 .../effect/test/utils/counter.ts | 0 .../effect/test/utils/gc.ts | 0 .../effect/test/workers/Worker.test.ts | 0 .../effect/test/workers/WorkerError.test.ts | 0 .../effect/test/workflow/DurableQueue.test.ts | 0 .../effect/test/workflow/Workflow.test.ts | 0 .../test/workflow/WorkflowEngine.test.ts | 378 +- .../effect/tsconfig.json | 0 .../effect/typeperf/README.md | 0 .../effect/typeperf/compare.mjs | 0 .../effect/typeperf/compare/httpapi.mjs | 0 .../effect/typeperf/config.json | 15 + .../effect/typeperf/run.mjs | 0 .../suites/graphql-selections/baseline.ts | 3 + .../fixtures/opaque-class-count-5.ts | 83 + .../fixtures/struct-interface-count-5.ts | 93 + .../suites/graphql-selections/thresholds.json | 10 + .../typeperf/suites/httpapi/baseline.ts | 0 .../fixtures/_endpoint-selection-types.ts | 0 .../httpapi/fixtures/_grouped-api-500.ts | 0 .../httpapi/fixtures/_grouped-api-types.ts | 0 .../fixtures/builder-endpoint-count-500.ts | 0 .../fixtures/builder-handle-all-count-500.ts | 0 ...uilder-handle-all-two-batches-count-500.ts | 0 .../fixtures/builder-handlers-count-100.ts | 0 .../builder-raw-handlers-count-100.ts | 0 .../fixtures/client-endpoint-count-500.ts | 0 .../fixtures/client-group-count-500.ts | 0 .../fixtures/client-groups-100x5-count-500.ts | 0 .../fixtures/client-methods-count-500.ts | 0 .../fixtures/client-top-level-count-500.ts | 0 .../fixtures/endpoint-class-count-500.ts | 0 .../httpapi/fixtures/endpoint-count-500.ts | 0 .../fixtures/group-endpoints-count-500.ts | 0 .../httpapi/fixtures/url-builder-count-500.ts | 0 .../url-builder-top-level-count-500.ts | 0 .../typeperf/suites/httpapi/thresholds.json | 0 .../effect/typeperf/suites/schema/baseline.ts | 0 .../typeperf/suites/schema/fixtures/array.ts | 0 .../typeperf/suites/schema/fixtures/brand.ts | 0 .../schema/fixtures/decode-unknown-effect.ts | 0 .../schema/fixtures/encode-unknown-effect.ts | 0 .../typeperf/suites/schema/fixtures/exit.ts | 0 .../suites/schema/fixtures/non-empty-array.ts | 0 .../typeperf/suites/schema/fixtures/record.ts | 0 .../suites/schema/fixtures/struct-mixed.ts | 0 .../schema/fixtures/struct-mutable-only.ts | 0 .../schema/fixtures/struct-optional-only.ts | 0 .../suites/schema/fixtures/struct-required.ts | 0 .../schema/fixtures/struct-with-rest.ts | 0 .../suites/schema/fixtures/tagged-union.ts | 0 .../fixtures/to-codec-array-from-single.ts | 0 .../suites/schema/fixtures/to-codec-json.ts | 0 .../schema/fixtures/to-codec-string-tree.ts | 0 .../suites/schema/fixtures/to-encoded.ts | 0 .../fixtures/to-standard-json-schema.ts | 0 .../suites/schema/fixtures/to-type.ts | 0 .../suites/schema/fixtures/tuple-with-rest.ts | 0 .../typeperf/suites/schema/fixtures/tuple.ts | 0 .../typeperf/suites/schema/fixtures/union.ts | 0 .../typeperf/suites/schema/thresholds.json | 0 .../effect/typetest/AiError.tst.ts | 0 .../effect/typetest/Array.tst.ts | 32 + .../effect/typetest/Brand.tst.ts | 0 .../effect/typetest/ByteSize.tst.ts | 0 .../effect/typetest/Channel.tst.ts | 0 .../effect/typetest/ChannelSchema.tst.ts | 0 .../effect/typetest/Clock.tst.ts | 0 .../effect/typetest/Config.tst.ts | 0 .../effect/typetest/ConfigProvider.tst.ts | 0 .../effect/typetest/Context.tst.ts | 0 packages/effect/typetest/Crypto.tst.ts | 41 + .../effect/typetest/Data.tst.ts | 0 .../effect/typetest/Effect.tst.ts | 0 .../effect/typetest/Effectable.tst.ts | 0 .../effect/typetest/Equivalence.tst.ts | 0 .../effect/typetest/Fiber.tst.ts | 0 .../effect/typetest/FileSystem.tst.ts | 0 .../effect/typetest/Function.tst.ts | 0 .../effect/typetest/Graph.tst.ts | 0 .../effect/typetest/Iterable.tst.ts | 0 .../effect/typetest/Layer.tst.ts | 0 .../effect/typetest/Match.tst.ts | 92 + .../effect/typetest/MigratedSubpaths.tst.ts | 0 .../effect/typetest/Newtype.tst.ts | 0 .../effect/typetest/Optic.tst.ts | 0 .../effect/typetest/Option.tst.ts | 0 .../effect/typetest/Order.tst.ts | 0 .../effect/typetest/Predicate.tst.ts | 0 .../effect/typetest/Queue.tst.ts | 0 .../effect/typetest/Random.tst.ts | 0 .../effect/typetest/Record.tst.ts | 0 .../effect/typetest/Result.tst.ts | 0 .../effect/typetest/Schedule.tst.ts | 0 .../effect/typetest/Scope.tst.ts | 0 .../effect/typetest/Sink.tst.ts | 0 .../effect/typetest/Stream.tst.ts | 0 .../effect/typetest/Struct.tst.ts | 0 .../effect/typetest/SynchronizedRef.tst.ts | 0 .../effect/typetest/TestSchema.tst.ts | 0 .../effect/typetest/Tuple.tst.ts | 0 .../effect/typetest/Types.tst.ts | 0 .../effect/typetest/VariantSchema.tst.ts | 0 .../effect/typetest/ai/DecisionModel.tst.ts | 38 +- .../effect/typetest/ai/LanguageModel.tst.ts | 54 + .../effect/typetest/ai/McpServer.tst.ts | 109 + .../effect/typetest/ai/Response.tst.ts | 0 .../effect/typetest/ai/Tool.tst.ts | 0 .../effect/typetest/ai/Toolkit.tst.ts | 0 .../typetest/arbitrary/Arbitrary.tst.ts | 0 .../effect/typetest/cli/Command.tst.ts | 0 .../effect/typetest/cli/Param.tst.ts | 0 .../effect/typetest/cli/Prompt.tst.ts | 0 .../typetest/cluster/EntityProxyServer.tst.ts | 0 .../typetest/cluster/K8sHttpClient.tst.ts | 0 .../effect/typetest/cluster/Reply.tst.ts | 0 .../effect/typetest/encoding/Ndjson.tst.ts | 0 .../effect/typetest/graphql/GraphQL.tst.ts | 15 + .../typetest/graphql/GraphQLClient.tst.ts | 141 + .../typetest/graphql/GraphQLMiddleware.tst.ts | 14 + .../typetest/graphql/GraphQLProtocol.tst.ts | 68 + .../typetest/graphql/GraphQLResolver.tst.ts | 65 + .../effect/typetest/http/HttpClient.tst.ts | 18 +- .../effect/typetest/http/HttpPlatform.tst.ts | 0 .../effect/typetest/http/HttpRouter.tst.ts | 0 .../effect/typetest/http/HttpServer.tst.ts | 0 .../effect/typetest/httpapi/HttpApi.tst.ts | 0 .../typetest/httpapi/HttpApiBuilder.tst.ts | 0 .../typetest/httpapi/HttpApiClient.tst.ts | 0 .../typetest/httpapi/HttpApiEndpoint.tst.ts | 0 .../typetest/httpapi/HttpApiGroup.tst.ts | 0 .../typetest/httpapi/HttpApiMiddleware.tst.ts | 0 .../typetest/httpapi/HttpApiSchema.tst.ts | 0 .../httpapi/OpenApiRepresentation.tst.ts | 0 .../typetest/net/AddressResolver.tst.ts | 32 + packages/effect/typetest/net/Dns.tst.ts | 48 + .../effect/typetest/net/IpInterface.tst.ts | 11 + .../effect/typetest/net/IpNetwork.tst.ts | 8 + .../effect/typetest/net/NetAddress.tst.ts | 28 + .../typetest/reactivity/AsyncResult.tst.ts | 0 .../effect/typetest/reactivity/Atom.tst.ts | 0 .../typetest/reactivity/AtomHttpApi.tst.ts | 0 .../effect/typetest/reactivity/AtomRpc.tst.ts | 0 .../effect/typetest/rpc/Rpc.tst.ts | 0 .../effect/typetest/rpc/RpcClient.tst.ts | 0 .../effect/typetest/rpc/RpcGroup.tst.ts | 0 .../effect/typetest/rpc/RpcMessage.tst.ts | 0 .../effect/typetest/rpc/RpcMiddleware.tst.ts | 0 .../effect/typetest/rpc/RpcServer.tst.ts | 0 .../effect/typetest/schema/Array.tst.ts | 0 .../typetest/schema/FromJsonSchema.tst.ts | 0 .../effect/typetest/schema/Graph.tst.ts | 0 .../effect/typetest/schema/Literals.tst.ts | 0 .../effect/typetest/schema/Record.tst.ts | 0 .../effect/typetest/schema/Schema.tst.ts | 0 .../typetest/schema/SchemaAOTCompiler.tst.ts | 0 .../schema/SchemaAOTCompilerBuild.tst.ts | 0 .../schema/SchemaBuiltInRevivers.tst.ts | 0 .../typetest/schema/SchemaCompiler.tst.ts | 0 .../typetest/schema/SchemaGetter.tst.ts | 0 .../schema/SchemaJsonSchemaConsumer.tst.ts | 0 .../schema/SchemaRepresentation.tst.ts | 0 .../SchemaRepresentationCompilers.tst.ts | 0 .../schema/SchemaRepresentationReviver.tst.ts | 0 .../effect/typetest/schema/Struct.tst.ts | 21 + .../effect/typetest/schema/Tuple.tst.ts | 0 .../effect/typetest/schema/Union.tst.ts | 40 +- .../effect/typetest/schema/api.tst.ts | 0 .../effect/typetest/schema/options.tst.ts | 0 .../effect/typetest/schema/toIso.tst.ts | 0 .../effect/typetest/socket/Socket.tst.ts | 0 .../effect/typetest/sql/SqlSchema.tst.ts | 0 .../effect/typetest/workflow/Workflow.tst.ts | 11 + packages/eslint-codegen-model/CHANGELOG.md | 1434 - .../examples/model-facades.ts | 318 - .../examples/workflow-facades.ts | 184 - packages/eslint-codegen-model/index.js | 3 - packages/eslint-codegen-model/package.json | 75 - packages/eslint-codegen-model/src/cli.ts | 638 - packages/eslint-codegen-model/src/index.ts | 4 - .../eslint-codegen-model/src/js-yaml.d.ts | 3 - .../eslint-codegen-model/src/normalise.ts | 38 - .../eslint-codegen-model/src/oxlint/index.ts | 8 - .../src/oxlint/rules/codegen.ts | 142 - .../src/presets/barrel.ts | 219 - .../eslint-codegen-model/src/presets/index.ts | 6 - .../eslint-codegen-model/src/presets/meta.ts | 48 - .../src/presets/model-facade.ts | 48 - .../eslint-codegen-model/src/presets/model.ts | 194 - .../src/shared/codegen-block.ts | 101 - .../src/shared/native-type-resolver.ts | 110 - .../src/shared/type-resolver.ts | 631 - packages/eslint-codegen-model/tsconfig.json | 14 - packages/eslint-shared-config/CHANGELOG.md | 610 - packages/eslint-shared-config/package.json | 59 - .../eslint-shared-config/src/dprint.jsonc | 36 - .../src/eslint.base.config.mjs | 114 - .../src/eslint.vue.config.mjs | 79 - .../eslint-shared-config/src/oxlintrc.json | 136 - .../src/plugin-effect-app.mjs | 91 - .../test/no-await-effect.test.mts | 204 - .../eslint-shared-config/vitest.config.ts | 9 - packages/infra/.madgerc | 7 - packages/infra/.npmignore | 14 - packages/infra/CHANGELOG.md | 16215 ---- packages/infra/_check.sh | 3 - packages/infra/docs/cluster-storage.md | 26 - packages/infra/docs/workflow-engine.md | 262 - packages/infra/examples/query.ts | 138 - packages/infra/package.json | 111 - packages/infra/src/CUPS.ts | 196 - packages/infra/src/ClusterCosmos.ts | 1170 - packages/infra/src/ClusterServiceBus.ts | 244 - packages/infra/src/ContextProvider.ts | 165 - packages/infra/src/Emailer/Sendgrid.ts | 161 - packages/infra/src/Emailer/fake.ts | 18 - packages/infra/src/MainFiberSet.ts | 95 - packages/infra/src/QueueMaker/errors.ts | 32 - packages/infra/src/SQL.ts | 1 - packages/infra/src/SQL/Model.ts | 932 - packages/infra/src/Store/Cosmos.ts | 656 - packages/infra/src/Store/Cosmos/query.ts | 531 - packages/infra/src/Store/Disk.ts | 270 - packages/infra/src/Store/Memory.ts | 609 - packages/infra/src/Store/SQL.ts | 805 - packages/infra/src/Store/SQL/Pg.ts | 411 - packages/infra/src/Store/SQL/query.ts | 748 - packages/infra/src/Store/codeFilter.ts | 244 - packages/infra/src/Store/index.test.ts.bak | 112 - packages/infra/src/Store/index.ts | 48 - packages/infra/src/Store/jsonDocument.ts | 143 - packages/infra/src/Store/utils.ts | 345 - packages/infra/src/WorkflowEngineCosmos.ts | 749 - packages/infra/src/WorkflowEngineSqlite.ts | 813 - packages/infra/src/arbs.ts | 40 - packages/infra/src/codec.ts | 9 - packages/infra/src/cosmos-client.ts | 72 - packages/infra/src/errorReporter.ts | 102 - packages/infra/src/errors.ts | 1 - packages/infra/src/fileUtil.ts | 130 - packages/infra/src/index.ts | 2 - .../src/internal/RequestContextMiddleware.ts | 59 - packages/infra/src/internal/auth.ts | 272 - packages/infra/src/internal/events.ts | 77 - packages/infra/src/internal/health.ts | 5 - packages/infra/src/layerUtils.ts | 68 - packages/infra/src/logger.ts | 3 - packages/infra/src/logger/jsonLogger.ts | 35 - packages/infra/src/logger/logFmtLogger.ts | 18 - packages/infra/src/logger/shared.ts | 19 - packages/infra/src/middlewares.ts | 10 - packages/infra/src/mongo-client.ts | 25 - packages/infra/src/otel.ts | 177 - packages/infra/src/rateLimit.ts | 126 - packages/infra/src/redis-client.ts | 133 - packages/infra/src/reportError.ts | 30 - packages/infra/src/routing.ts | 827 - packages/infra/src/routing/middleware.ts | 4 - .../routing/middleware/RouterMiddleware.ts | 27 - .../src/routing/middleware/middleware.ts | 196 - packages/infra/src/routing/schema/jwt.ts | 21 - packages/infra/src/routing/tsort.ts | 56 - packages/infra/src/routing/utils.ts | 48 - packages/infra/src/test.ts | 30 - packages/infra/src/util.ts | 14 - packages/infra/src/vitest.ts | 2 - packages/infra/test/arbs-magnitude.test.ts | 33 - packages/infra/test/arbs.test.ts | 41 - packages/infra/test/auth.test.ts | 101 - packages/infra/test/cluster-cosmos.test.ts | 634 - .../infra/test/cluster-servicebus.test.ts | 180 - packages/infra/test/cluster-sqlite.test.ts | 207 - packages/infra/test/contextProvider.test.ts | 161 - packages/infra/test/controller.test.ts | 425 - .../test/cosmos-container-throughput.test.ts | 80 - packages/infra/test/cosmos-query.test.ts | 368 - packages/infra/test/fixtures.ts | 139 - packages/infra/test/json-lower.test.ts | 129 - packages/infra/test/layerUtils.test.ts | 21 - packages/infra/test/query.test.ts | 2436 - packages/infra/test/rawQuery.test.ts | 851 - packages/infra/test/repository-ext.test.ts | 456 - .../test/repository-legacy-document.test.ts | 394 - packages/infra/test/requires.test.ts | 182 - packages/infra/test/router-generator.test.ts | 187 - .../test/routing-interruptibility.test.ts | 66 - .../test/rpc-context-map-streaming.test.ts | 265 - .../infra/test/rpc-e2e-invalidation.test.ts | 421 - .../infra/test/rpc-multi-middleware.test.ts | 213 - .../infra/test/rpc-stream-fullstack.test.ts | 307 - packages/infra/test/sql-store.test.ts | 1931 - packages/infra/test/validateSample.test.ts | 265 - .../infra/test/workflow-engine-cosmos.test.ts | 345 - .../infra/test/workflow-engine-sqlite.test.ts | 297 - packages/infra/tsconfig.examples.json | 15 - packages/infra/tsconfig.json | 12 - packages/infra/tsconfig.src.json | 38 - packages/infra/tsconfig.test.json | 33 - packages/infra/types/modules.d.ts | 0 packages/infra/vitest.config.ts | 5 - .../opentelemetry/CHANGELOG.md | 27 + .../opentelemetry/LICENSE | 0 .../opentelemetry/README.md | 0 .../opentelemetry/package.json | 26 +- .../opentelemetry/src/NodeSdk.ts | 0 .../opentelemetry/src/OtelLogger.ts | 9 +- .../opentelemetry/src/OtelMetrics.ts | 0 .../opentelemetry/src/OtelTracer.ts | 26 +- .../opentelemetry/src/Resource.ts | 0 .../opentelemetry/src/WebSdk.ts | 0 .../opentelemetry/src/index.ts | 0 .../opentelemetry/src/internal/attributes.ts | 0 .../opentelemetry/src/internal/metrics.ts | 16 +- .../opentelemetry/src/internal/utilities.ts | 0 .../opentelemetry/test/OtelLogger.test.ts | 67 + .../opentelemetry/test/OtelMetrics.test.ts | 13 + .../opentelemetry/test/OtelTracer.test.ts | 68 +- .../opentelemetry/test/Resource.test.ts | 0 .../opentelemetry/tsconfig.json | 0 .../platform/browser/CHANGELOG.md | 21 + .../platform/browser/LICENSE | 0 .../platform/browser/README.md | 0 .../platform/browser/package.json | 2 +- .../platform/browser/src/BrowserCrypto.ts | 0 .../platform/browser/src/BrowserHttpClient.ts | 0 .../browser/src/BrowserKeyValueStore.ts | 0 .../browser/src/BrowserPersistence.ts | 0 .../platform/browser/src/BrowserRuntime.ts | 0 .../platform/browser/src/BrowserSocket.ts | 0 .../platform/browser/src/BrowserStream.ts | 0 .../platform/browser/src/BrowserWorker.ts | 0 .../browser/src/BrowserWorkerRunner.ts | 0 .../platform/browser/src/Clipboard.ts | 0 .../platform/browser/src/Geolocation.ts | 0 .../platform/browser/src/IndexedDb.ts | 0 .../platform/browser/src/IndexedDbDatabase.ts | 0 .../browser/src/IndexedDbQueryBuilder.ts | 0 .../platform/browser/src/IndexedDbTable.ts | 0 .../platform/browser/src/IndexedDbVersion.ts | 0 .../platform/browser/src/Permissions.ts | 0 .../platform/browser/src/index.ts | 0 .../browser/test/BrowserCrypto.test.ts | 4 +- .../browser/test/BrowserHttpClient.test.ts | 0 .../browser/test/BrowserKeyValueStore.test.ts | 0 .../browser/test/BrowserPersistence.test.ts | 0 .../BrowserPersistencePersistedCache.test.ts | 0 .../browser/test/BrowserWorkerRunner.test.ts | 0 .../browser/test/IndexedDbDatabase.test.ts | 0 .../test/IndexedDbQueryBuilder.test.ts | 0 .../browser/test/IndexedDbTable.test.ts | 0 .../browser/test/IndexedDbVersion.test.ts | 0 .../platform/browser/test/Permissions.test.ts | 0 .../platform/browser/test/RpcWorker.test.ts | 0 .../platform/browser/test/fixtures/rpc-e2e.ts | 0 .../browser/test/fixtures/rpc-schemas.ts | 0 .../browser/test/fixtures/rpc-worker.ts | 0 .../platform/browser/tsconfig.json | 0 .../platform/browser/vitest.setup.ts | 0 .../platform/bun/CHANGELOG.md | 42 + .../platform/bun/LICENSE | 0 .../platform/bun/README.md | 0 .../platform/bun/benchmark/DatagramSocket.ts | 1398 + .../platform/bun/package.json | 8 +- .../platform/bun/src/BunAddressResolver.ts | 15 + .../bun/src/BunChildProcessSpawner.ts | 0 .../platform/bun/src/BunClusterHttp.ts | 0 .../platform/bun/src/BunClusterSocket.ts | 0 .../platform/bun/src/BunCrypto.ts | 0 .../platform/bun/src/BunDatagramSocket.ts | 666 + packages/platform/bun/src/BunDns.ts | 82 + .../platform/bun/src/BunFileSystem.ts | 0 .../platform/bun/src/BunHttpClient.ts | 0 .../platform/bun/src/BunHttpPlatform.ts | 0 .../platform/bun/src/BunHttpServer.ts | 2 +- .../platform/bun/src/BunHttpServerRequest.ts | 0 .../platform/bun/src/BunMultipart.ts | 0 .../platform/bun/src/BunPath.ts | 0 .../platform/bun/src/BunRedis.ts | 0 .../platform/bun/src/BunRuntime.ts | 0 .../platform/bun/src/BunServices.ts | 0 .../platform/bun/src/BunSink.ts | 0 .../platform/bun/src/BunSocket.ts | 31 +- .../platform/bun/src/BunSocketServer.ts | 0 .../platform/bun/src/BunStdio.ts | 0 .../platform/bun/src/BunStream.ts | 0 .../platform/bun/src/BunTerminal.ts | 7 +- .../platform/bun/src/BunWorker.ts | 0 .../platform/bun/src/BunWorkerRunner.ts | 1 - .../platform/bun/src/index.ts | 18 + .../bun/test/BunAddressResolver.test.ts | 6 + .../platform/bun/test/BunClusterHttp.test.ts | 0 .../bun/test/BunDatagramSocket.test.ts | 149 + .../bun/test/BunDns.integration.test.ts | 4 + .../bun/test/BunHttpCompression.test.ts | 0 .../platform/bun/test/BunHttpPlatform.test.ts | 0 .../platform/bun/test/BunHttpServer.test.ts | 0 .../platform/bun/test/BunMultipart.test.ts | 0 packages/platform/bun/test/BunSocket.test.ts | 49 + .../platform/bun/test/BunStream.test.ts | 0 .../platform/bun/tsconfig.json | 0 .../platform/deno/CHANGELOG.md | 53 + .../platform/deno/LICENSE | 0 .../platform/deno/README.md | 0 .../platform/deno/benchmark/DatagramSocket.ts | 1445 + .../platform/deno/package.json | 8 +- .../platform/deno/src/DenoAddressResolver.ts | 36 + .../deno/src/DenoChildProcessSpawner.ts | 0 .../platform/deno/src/DenoClusterHttp.ts | 0 .../platform/deno/src/DenoClusterSocket.ts | 0 .../platform/deno/src/DenoCrypto.ts | 0 .../platform/deno/src/DenoDatagramSocket.ts | 571 + packages/platform/deno/src/DenoDns.ts | 196 + .../platform/deno/src/DenoFileSystem.ts | 109 +- .../platform/deno/src/DenoHttpClient.ts | 0 .../platform/deno/src/DenoHttpPlatform.ts | 0 .../platform/deno/src/DenoHttpServer.ts | 2 +- .../deno/src/DenoHttpServerRequest.ts | 0 .../platform/deno/src/DenoKeyValueStore.ts | 0 .../platform/deno/src/DenoMultipart.ts | 0 .../platform/deno/src/DenoPath.ts | 9 +- .../platform/deno/src/DenoRedis.ts | 0 .../platform/deno/src/DenoRuntime.ts | 0 .../platform/deno/src/DenoServices.ts | 0 .../platform/deno/src/DenoSocket.ts | 6 +- .../platform/deno/src/DenoSocketServer.ts | 0 .../platform/deno/src/DenoStdio.ts | 1 + .../platform/deno/src/DenoTerminal.ts | 7 +- .../platform/deno/src/DenoWorker.ts | 0 .../platform/deno/src/DenoWorkerRunner.ts | 0 .../platform/deno/src/index.ts | 18 + .../platform/deno/src/internal/error.ts | 0 .../deno/test/DenoAddressResolver.test.ts | 6 + .../deno/test/DenoChildProcessSpawner.test.ts | 0 .../platform/deno/test/DenoCrypto.test.ts | 4 +- .../deno/test/DenoDatagramSocket.test.ts | 106 + .../DenoDatagramSocketUnsupported.test.ts | 20 + .../deno/test/DenoDns.integration.test.ts | 5 + .../platform/deno/test/DenoFileSystem.test.ts | 165 + .../deno/test/DenoHttpCompression.test.ts | 0 .../deno/test/DenoHttpPlatform.test.ts | 0 .../platform/deno/test/DenoHttpServer.test.ts | 0 .../deno/test/DenoKeyValueStore.test.ts | 0 .../platform/deno/test/DenoPath.test.ts | 0 .../deno/test/DenoRedis.integration.test.ts | 0 .../platform/deno/test/DenoSocket.test.ts | 58 +- .../deno/test/DenoSocketServer.test.ts | 0 .../platform/deno/test/DenoStdio.test.ts | 17 +- .../platform/deno/test/DenoTerminal.test.ts | 0 .../deno/test/DenoWorkerRunner.test.ts | 0 .../platform/deno/test/RpcWorker.test.ts | 0 .../deno/test/cluster/SocketRunner.test.ts | 0 .../deno/test/fixtures/deno-terminal.ts | 0 .../platform/deno/test/fixtures/rpc-e2e.ts | 0 .../deno/test/fixtures/rpc-schemas.ts | 0 .../platform/deno/test/fixtures/rpc-worker.ts | 0 .../platform/deno/test/fixtures/text.txt | 0 .../platform/deno/test/fixtures/tls/ca.pem | 0 .../platform/deno/test/fixtures/tls/cert.pem | 0 .../platform/deno/test/fixtures/tls/key.pem | 0 .../platform/deno/test/internal/error.test.ts | 0 .../platform/deno/tsconfig.json | 0 .../platform/node-shared/CHANGELOG.md | 48 + .../platform/node-shared/LICENSE | 0 .../platform/node-shared/README.md | 0 .../node-shared/benchmark/DatagramSocket.ts | 1286 + .../platform/node-shared/benchmark/Socket.ts | 0 .../platform/node-shared/package.json | 8 +- .../node-shared/src/NodeAddressResolver.ts | 32 + .../src/NodeChildProcessSpawner.ts | 25 +- .../node-shared/src/NodeClusterSocket.ts | 0 .../platform/node-shared/src/NodeCrypto.ts | 0 .../node-shared/src/NodeDatagramSocket.ts | 657 + packages/platform/node-shared/src/NodeDns.ts | 489 + .../node-shared/src/NodeFileSystem.ts | 96 +- .../node-shared/src/NodeHttpCompression.ts | 0 .../platform/node-shared/src/NodePath.ts | 17 +- .../platform/node-shared/src/NodeRuntime.ts | 0 .../platform/node-shared/src/NodeSink.ts | 0 .../platform/node-shared/src/NodeSocket.ts | 2 +- .../node-shared/src/NodeSocketServer.ts | 0 .../platform/node-shared/src/NodeStdio.ts | 1 + .../platform/node-shared/src/NodeStream.ts | 0 .../platform/node-shared/src/NodeTerminal.ts | 14 +- .../platform/node-shared/src/index.ts | 18 + .../src/internal/nodeChildProcessSpawner.ts | 0 .../node-shared/src/internal/utils.ts | 0 .../test/AddressResolver.test-utils.ts | 40 + .../node-shared/test/Dns.test-utils.ts | 312 + .../test/HttpPlatform.test-utils.ts | 2 +- .../test/NodeAddressResolver.test.ts | 6 + .../test/NodeChildProcessSpawner.test.ts | 60 + .../test/NodeDatagramSocket.test.ts | 123 + .../test/NodeDns.integration.test.ts | 5 + .../node-shared/test/NodeFileSystem.test.ts | 113 +- .../node-shared/test/NodePath.test.ts | 17 + .../node-shared/test/NodeSink.test.ts | 0 .../node-shared/test/NodeStdio.test.ts | 19 +- .../node-shared/test/NodeStream.test.ts | 0 .../node-shared/test/NodeTerminal.test.ts | 56 +- .../test/fixtures/buffered-input.ts | 30 + .../test/fixtures/helloworld.tar.gz | Bin .../test/fixtures/node-terminal.ts | 23 +- .../test/fixtures/process-group.ts | 0 .../platform/node-shared/tsconfig.json | 0 .../platform/node/CHANGELOG.md | 41 + .../platform/node/LICENSE | 0 .../platform/node/README.md | 0 .../platform/node/package.json | 7 +- .../platform/node/src/NodeAddressResolver.ts | 16 + .../node/src/NodeChildProcessSpawner.ts | 0 .../platform/node/src/NodeClusterHttp.ts | 0 .../platform/node/src/NodeClusterSocket.ts | 0 .../platform/node/src/NodeCrypto.ts | 0 .../platform/node/src/NodeDatagramSocket.ts | 10 + packages/platform/node/src/NodeDns.ts | 54 + .../platform/node/src/NodeFileSystem.ts | 0 .../platform/node/src/NodeHttpClient.ts | 130 +- .../node/src/NodeHttpIncomingMessage.ts | 6 +- .../platform/node/src/NodeHttpPlatform.ts | 0 .../platform/node/src/NodeHttpServer.ts | 5 +- .../node/src/NodeHttpServerRequest.ts | 0 .../platform/node/src/NodeMultipart.ts | 0 .../platform/node/src/NodeMultipartParser.ts | 0 .../platform/node/src/NodePath.ts | 0 .../platform/node/src/NodeRedis.ts | 0 .../platform/node/src/NodeRuntime.ts | 0 .../platform/node/src/NodeServices.ts | 0 .../platform/node/src/NodeSink.ts | 0 .../platform/node/src/NodeSocket.ts | 16 +- .../platform/node/src/NodeSocketServer.ts | 0 .../platform/node/src/NodeStdio.ts | 0 .../platform/node/src/NodeStream.ts | 0 .../platform/node/src/NodeTerminal.ts | 7 +- .../platform/node/src/NodeWorker.ts | 0 .../platform/node/src/NodeWorkerRunner.ts | 0 .../platform/node/src/Undici.ts | 0 .../platform/node/src/index.ts | 18 + .../node/test/GraphQLProtocol.test.ts | 119 + .../platform/node/test/HttpApi.test.ts | 0 .../node/test/HttpStaticServer.test.ts | 0 .../test/HttpStaticServerConditional.test.ts | 2 +- .../platform/node/test/KeyValueStore.test.ts | 0 .../node/test/MultipartParser.test.ts | 0 .../platform/node/test/NodeCrypto.test.ts | 6 +- .../platform/node/test/NodeHttpClient.test.ts | 18 + .../node/test/NodeHttpCompression.test.ts | 0 .../node/test/NodeHttpPlatform.test.ts | 0 .../platform/node/test/NodeHttpServer.test.ts | 49 +- .../node/test/NodeRedis.integration.test.ts | 0 .../platform/node/test/NodeSocket.test.ts | 73 +- .../node/test/NodeWorkerRunner.test.ts | 0 .../platform/node/test/OpenApi.test.ts | 0 .../platform/node/test/RpcServer.test.ts | 0 .../test/__snapshots__/HttpApi.test.ts.snap | 10 + .../cluster-integration/ClusterCron.test.ts | 0 .../test/cluster-integration/Entity.test.ts | 0 .../test/cluster-integration/Locks.test.ts | 0 .../cluster-integration/Persistence.test.ts | 0 .../node/test/cluster-integration/README.md | 0 .../cluster-integration/Residency.test.ts | 0 .../test/cluster-integration/Smoke.test.ts | 0 .../StreamDisconnect.test.ts | 0 .../cluster-integration/Transport.test.ts | 0 .../test/cluster-integration/Workflow.test.ts | 0 .../test/cluster-integration/globalSetup.ts | 0 .../node/test/cluster-integration/harness.ts | 0 .../node/test/cluster/MessageStorageTest.ts | 0 .../node/test/cluster/SocketRunner.test.ts | 0 .../SqlMessageStorage.integration.test.ts | 0 .../SqlRunnerStorage.integration.test.ts | 129 +- .../fixtures/http-static-server-outside.txt | 0 .../http-static-server/conditional.txt | 0 .../http-static-server/custom/home.html | 0 .../fixtures/http-static-server/file.binx | 0 .../http-static-server/guide/index.html | 0 .../fixtures/http-static-server/hello.txt | 0 .../fixtures/http-static-server/index.html | 0 .../fixtures/http-static-server/range.txt | 0 .../node/test/fixtures/mysql2-utils.ts | 0 .../platform/node/test/fixtures/pg-utils.ts | 11 +- .../platform/node/test/fixtures/rpc-e2e.ts | 0 .../node/test/fixtures/rpc-schemas.ts | 0 .../platform/node/test/fixtures/text.txt | 0 .../platform/node/test/fixtures/tls/cert.pem | 0 .../platform/node/test/fixtures/tls/key.pem | 0 .../node/test/fixtures/worker-runner.ts | 0 .../platform/node/tsconfig.json | 0 .../sql/clickhouse/CHANGELOG.md | 25 + .../sql/clickhouse/LICENSE | 0 .../sql/clickhouse/README.md | 0 .../sql/clickhouse/package.json | 4 +- .../sql/clickhouse/src/ClickhouseClient.ts | 0 .../sql/clickhouse/src/ClickhouseMigrator.ts | 0 .../sql/clickhouse/src/index.ts | 0 .../sql/clickhouse/test/Client.test.ts | 0 .../test/SqlErrorClassification.test.ts | 0 .../sql/clickhouse/tsconfig.json | 0 .../packages => packages}/sql/d1/CHANGELOG.md | 22 + .../packages => packages}/sql/d1/LICENSE | 0 .../packages => packages}/sql/d1/README.md | 0 .../packages => packages}/sql/d1/package.json | 4 +- .../sql/d1/src/D1Client.ts | 0 .../packages => packages}/sql/d1/src/index.ts | 0 .../sql/d1/test/Client.test.ts | 0 .../sql/d1/test/Resolver.test.ts | 0 .../sql/d1/test/utils.ts | 0 .../sql/d1/tsconfig.json | 0 .../sql/libsql/CHANGELOG.md | 22 + .../packages => packages}/sql/libsql/LICENSE | 0 .../sql/libsql/README.md | 0 .../sql/libsql/package.json | 2 +- .../sql/libsql/src/LibsqlClient.ts | 8 +- .../sql/libsql/src/LibsqlMigrator.ts | 0 .../sql/libsql/src/index.ts | 0 .../libsql/test/Client.integration.test.ts | 0 .../libsql/test/Resolver.integration.test.ts | 0 .../sql/libsql/test/util.ts | 0 .../sql/libsql/tsconfig.json | 0 .../sql/mssql/CHANGELOG.md | 24 + .../packages => packages}/sql/mssql/LICENSE | 0 .../packages => packages}/sql/mssql/README.md | 0 .../sql/mssql/package.json | 4 +- .../sql/mssql/src/MssqlClient.ts | 3 +- .../sql/mssql/src/MssqlMigrator.ts | 0 .../sql/mssql/src/Parameter.ts | 0 .../sql/mssql/src/Procedure.ts | 0 .../sql/mssql/src/index.ts | 0 .../sql/mssql/test/Binary.test.ts | 0 .../sql/mssql/test/Client.test.ts | 19 + .../test/Persistence.integration.test.ts | 3 + .../mssql/test/SqlErrorClassification.test.ts | 0 .../sql/mssql/test/utils.ts | 0 .../sql/mssql/tsconfig.json | 0 .../sql/mysql2/CHANGELOG.md | 21 + .../packages => packages}/sql/mysql2/LICENSE | 0 .../sql/mysql2/README.md | 0 .../sql/mysql2/package.json | 2 +- .../sql/mysql2/src/MysqlClient.ts | 0 .../sql/mysql2/src/MysqlMigrator.ts | 0 .../sql/mysql2/src/index.ts | 0 .../mysql2/test/Client.integration.test.ts | 0 .../test/KeyValueStore.integration.test.ts | 0 .../sql/mysql2/test/Model.integration.test.ts | 0 .../test/MysqlClient.integration.test.ts | 0 .../test/Persistence.integration.test.ts | 3 + .../test/SqlErrorClassification.test.ts | 0 ...ntLogServerUnencrypted.integration.test.ts | 0 .../sql/mysql2/test/utils.ts | 0 .../sql/mysql2/tsconfig.json | 0 .../packages => packages}/sql/pg/CHANGELOG.md | 25 + .../packages => packages}/sql/pg/LICENSE | 0 .../packages => packages}/sql/pg/README.md | 0 .../sql/pg/benchmark/PgClient.ts | 0 .../sql/pg/benchmark/PgCodec.ts | 0 .../sql/pg/benchmark/README.md | 0 .../packages => packages}/sql/pg/package.json | 7 +- .../sql/pg/src/PgAuth.ts | 0 .../sql/pg/src/PgClient.ts | 2 + .../sql/pg/src/PgConnection.ts | 142 +- .../sql/pg/src/PgMigrator.ts | 0 .../sql/pg/src/PgPool.ts | 0 .../sql/pg/src/PgProtocol.ts | 0 .../sql/pg/src/PgTypes.ts | 0 .../packages => packages}/sql/pg/src/index.ts | 0 .../sql/pg/src/internal/connection.ts | 0 .../sql/pg/src/internal/password.ts | 0 .../sql/pg/src/internal/sqlError.ts | 0 .../sql/pg/test/Client.integration.test.ts | 32 +- .../pg/test/KeyValueStore.integration.test.ts | 0 .../pg/test/Persistence.integration.test.ts | 3 + .../sql/pg/test/PgAuth.test.ts | 0 .../pg/test/PgConnection.in-process.test.ts | 104 +- .../pg/test/PgConnection.integration.test.ts | 59 +- .../sql/pg/test/PgConnection.test.ts | 15 + .../pg/test/PgMigrator.integration.test.ts | 61 + .../sql/pg/test/PgMigrator.test.ts | 0 .../sql/pg/test/PgPool.in-process.test.ts | 0 .../sql/pg/test/PgPool.integration.test.ts | 79 +- .../sql/pg/test/PgProtocol.test.ts | 0 .../sql/pg/test/PgTypes.test.ts | 0 .../pg/test/SqlErrorClassification.test.ts | 0 .../test/SqlEventJournal.integration.test.ts | 0 ...ntLogServerUnencrypted.integration.test.ts | 0 .../StartupParameters.integration.test.ts | 0 .../sql/pg/test/TransactionAcquire.test.ts | 0 .../sql/pg/test/fixtures/goldens.ts | 0 .../sql/pg/test/fixtures/regenerate.ts | 0 packages/sql/pg/test/utils.ts | 205 + .../sql/pg/tsconfig.json | 0 .../sql/pglite/CHANGELOG.md | 22 + .../packages => packages}/sql/pglite/LICENSE | 0 .../sql/pglite/README.md | 0 .../sql/pglite/package.json | 2 +- .../sql/pglite/src/PgliteClient.ts | 22 +- .../sql/pglite/src/PgliteMigrator.ts | 0 .../sql/pglite/src/index.ts | 0 .../sql/pglite/test/Client.test.ts | 0 .../sql/pglite/test/Json.test.ts | 0 .../sql/pglite/test/Migrator.test.ts | 16 + .../sql/pglite/test/PersistedQueue.test.ts | 0 .../test/SqlErrorClassification.test.ts | 0 .../sql/pglite/test/Transaction.test.ts | 0 .../sql/pglite/tsconfig.json | 0 .../sql/sqlite-bun/CHANGELOG.md | 23 + .../sql/sqlite-bun/LICENSE | 0 .../sql/sqlite-bun/README.md | 0 .../sql/sqlite-bun/package.json | 2 +- .../sql/sqlite-bun/src/SqliteClient.ts | 7 +- .../sql/sqlite-bun/src/SqliteMigrator.ts | 0 .../sql/sqlite-bun/src/index.ts | 0 .../sql/sqlite-bun/test/Client.test.ts | 0 .../sql/sqlite-bun/tsconfig.json | 0 .../sql/sqlite-do/CHANGELOG.md | 23 + .../sql/sqlite-do/LICENSE | 0 .../sql/sqlite-do/README.md | 0 .../sql/sqlite-do/package.json | 4 +- .../sql/sqlite-do/src/SqliteClient.ts | 22 +- .../sql/sqlite-do/src/SqliteMigrator.ts | 0 .../sql/sqlite-do/src/index.ts | 0 .../sql/sqlite-do/test/Client.test.ts | 41 +- .../sql/sqlite-do/test/Miniflare.test.ts | 0 .../sqlite-do/test/fixtures/transaction.ts | 0 .../sql/sqlite-do/tsconfig.json | 0 .../sql/sqlite-node/CHANGELOG.md | 26 + .../sql/sqlite-node/LICENSE | 0 .../sql/sqlite-node/README.md | 0 .../sql/sqlite-node/package.json | 4 +- .../sql/sqlite-node/src/SqliteClient.ts | 29 +- .../sql/sqlite-node/src/SqliteMigrator.ts | 0 .../sql/sqlite-node/src/index.ts | 0 .../sql/sqlite-node/test/Client.test.ts | 46 +- .../sqlite-node/test/KeyValueStore.test.ts | 0 .../sql/sqlite-node/test/Persistence.test.ts | 3 + .../sql/sqlite-node/test/Resolver.test.ts | 5 +- .../sqlite-node/test/SqlEventJournal.test.ts | 0 .../test/SqlEventLogServerEncrypted.test.ts | 0 .../test/SqlEventLogServerUnencrypted.test.ts | 0 .../sqlite-node/test/SqlModelServices.test.ts | 0 .../sqlite-node/test/SqliteMigrator.test.ts | 0 .../sql/sqlite-node/tsconfig.json | 0 .../sql/sqlite-react-native/CHANGELOG.md | 22 + .../sql/sqlite-react-native/LICENSE | 0 .../sql/sqlite-react-native/README.md | 0 .../sql/sqlite-react-native/package.json | 2 +- .../sqlite-react-native/src/SqliteClient.ts | 3 +- .../sqlite-react-native/src/SqliteMigrator.ts | 0 .../sql/sqlite-react-native/src/index.ts | 0 .../sqlite-react-native/test/Client.test.ts | 0 .../sql/sqlite-react-native/tsconfig.json | 0 .../sql/sqlite-wasm/CHANGELOG.md | 23 + .../sql/sqlite-wasm/LICENSE | 0 .../sql/sqlite-wasm/README.md | 0 .../sql/sqlite-wasm/package.json | 2 +- .../sql/sqlite-wasm/src/OpfsWorker.ts | 0 .../sql/sqlite-wasm/src/SqliteClient.ts | 10 +- .../sql/sqlite-wasm/src/SqliteMigrator.ts | 0 .../sql/sqlite-wasm/src/index.ts | 0 .../sqlite-wasm/src/internal/opfsWorker.ts | 0 .../sql/sqlite-wasm/src/sqlite-wasm.d.ts | 0 .../sql/sqlite-wasm/test/Client.test.ts | 0 .../sql/sqlite-wasm/test/Memory.test.ts | 0 .../sql/sqlite-wasm/tsconfig.json | 0 .../tools/ai-codegen/codegen.schema.json | 2 +- .../tools/ai-codegen/package.json | 2 +- .../tools/ai-codegen/src/Config.ts | 2 +- .../tools/ai-codegen/src/Discovery.ts | 0 packages/tools/ai-codegen/src/Generator.ts | 611 + .../tools/ai-codegen/src/Glob.ts | 0 .../tools/ai-codegen/src/PostProcess.ts | 0 .../tools/ai-codegen/src/SpecFetcher.ts | 2 +- .../tools/ai-codegen/src/bin.ts | 0 .../tools/ai-codegen/src/main.ts | 0 .../tools/ai-codegen/test/Generator.test.ts | 0 .../ai-codegen/test/OpenAiGeneration.test.ts | 359 + .../test/OpenRouterTranscriptions.test.ts | 0 .../tools/ai-codegen/tsconfig.json | 0 .../tools/ai-docgen/package.json | 2 +- .../tools/ai-docgen/src/Glob.ts | 0 .../tools/ai-docgen/src/main.ts | 0 .../tools/ai-docgen/tsconfig.json | 0 .../tools/api-diff/README.md | 0 .../tools/api-diff/package.json | 2 +- .../tools/api-diff/src/Annotations.ts | 0 .../tools/api-diff/src/ApiDiff.ts | 0 .../tools/api-diff/src/Cli.ts | 0 .../tools/api-diff/src/Diff.ts | 0 .../tools/api-diff/src/Discovery.ts | 0 .../tools/api-diff/src/Error.ts | 0 .../tools/api-diff/src/Json.ts | 0 .../tools/api-diff/src/MigrationDoc.ts | 0 .../tools/api-diff/src/Model.ts | 0 .../tools/api-diff/src/Report.ts | 0 .../tools/api-diff/src/Snapshot.ts | 0 .../tools/api-diff/src/Worktrees.ts | 0 .../tools/api-diff/src/bin.ts | 0 .../tools/api-diff/test/Annotations.test.ts | 0 .../tools/api-diff/test/Cli.test.ts | 0 .../tools/api-diff/test/Diff.test.ts | 0 .../tools/api-diff/test/Discovery.test.ts | 0 .../tools/api-diff/test/Error.test.ts | 0 .../tools/api-diff/test/MigrationDoc.test.ts | 0 .../tools/api-diff/test/Report.test.ts | 0 .../tools/api-diff/test/Snapshot.test.ts | 4 +- .../tools/api-diff/test/Worktrees.test.ts | 0 .../tools/api-diff/test/utils.ts | 0 .../tools/api-diff/tsconfig.json | 0 .../tools/bundle/README.md | 0 .../bundle/fixtures/arbitrary-combinators.ts | 0 .../tools/bundle/fixtures/basic.ts | 0 .../tools/bundle/fixtures/batching.ts | 0 .../tools/bundle/fixtures/brand.ts | 0 .../tools/bundle/fixtures/cache.ts | 0 .../tools/bundle/fixtures/config.ts | 0 .../tools/bundle/fixtures/differ.ts | 0 .../tools/bundle/fixtures/http-client.ts | 0 .../tools/bundle/fixtures/http-router.ts | 0 .../tools/bundle/fixtures/logger.ts | 0 .../tools/bundle/fixtures/metric.ts | 0 .../tools/bundle/fixtures/optic.ts | 0 .../tools/bundle/fixtures/pubsub.ts | 0 .../tools/bundle/fixtures/queue.ts | 0 .../tools/bundle/fixtures/schedule.ts | 0 .../bundle/fixtures/schema-bigdecimal.ts | 0 .../tools/bundle/fixtures/schema-binary.ts | 0 .../tools/bundle/fixtures/schema-class.ts | 0 .../fixtures/schema-fromJsonSchemaDocument.ts | 0 .../schema-representation-roundtrip.ts | 0 .../fixtures/schema-string-transformation.ts | 0 .../tools/bundle/fixtures/schema-string.ts | 0 .../fixtures/schema-template-literal.ts | 0 .../bundle/fixtures/schema-toArbitrary.ts | 0 .../bundle/fixtures/schema-toCodeDocument.ts | 0 .../bundle/fixtures/schema-toCodecJson.ts | 0 .../bundle/fixtures/schema-toEquivalence.ts | 0 .../bundle/fixtures/schema-toFormatter.ts | 0 .../fixtures/schema-toJsonSchemaDocument.ts | 0 .../fixtures/schema-toRepresentation.ts | 0 .../tools/bundle/fixtures/schema.ts | 0 .../tools/bundle/fixtures/stm.ts | 0 .../tools/bundle/fixtures/stream.ts | 0 .../tools/bundle/package.json | 6 +- .../tools/bundle/src/Cli.ts | 0 .../tools/bundle/src/Fixtures.ts | 0 .../tools/bundle/src/Plugins.ts | 0 .../tools/bundle/src/Reporter.ts | 0 .../tools/bundle/src/Rollup.ts | 0 .../tools/bundle/src/bin.ts | 0 .../bundle/test/HttpRouterEsbuild.test.ts | 0 .../tools/bundle/test/Plugins.test.ts | 0 .../tools/bundle/tsconfig.fixtures.json | 0 .../tools/bundle/tsconfig.json | 0 .../tools/bundle/tsconfig.src.json | 0 .../tools/docgen/CHANGELOG.md | 25 + .../tools/docgen/LICENSE | 0 .../tools/docgen/README.md | 0 .../tools/docgen/package.json | 6 +- .../tools/docgen/schema.json | 0 .../tools/docgen/src/CLI.ts | 0 .../tools/docgen/src/Checker.ts | 0 .../tools/docgen/src/Configuration.ts | 0 .../tools/docgen/src/Core.ts | 0 .../tools/docgen/src/Domain.ts | 0 .../tools/docgen/src/Parser.ts | 0 .../tools/docgen/src/Printer.ts | 0 .../tools/docgen/src/bin.ts | 0 .../tools/docgen/src/index.ts | 0 .../docgen/src/internal/markdown-toc.d.ts | 0 .../tools/docgen/test/Checker.test.ts | 0 .../tools/docgen/test/Configuration.test.ts | 2 +- .../tools/docgen/test/Core.test.ts | 0 .../tools/docgen/test/Parser.test.ts | 0 .../docgen/test/fixtures/invalid-json.txt | 0 .../tools/docgen/tsconfig.json | 0 .../tools/doctest/CHANGELOG.md | 22 + .../tools/doctest/README.md | 0 .../tools/doctest/package.json | 8 +- .../tools/doctest/src/Plugin.ts | 0 .../tools/doctest/src/Protocol.ts | 0 .../tools/doctest/src/Runner.ts | 0 .../tools/doctest/src/Runtime.ts | 0 .../tools/doctest/src/Source.ts | 0 .../tools/doctest/src/Transform.ts | 0 .../tools/doctest/src/index.ts | 0 .../tools/doctest/test/Plugin.test.ts | 0 .../tools/doctest/test/Protocol.test.ts | 0 .../tools/doctest/test/Runtime.test.ts | 0 .../tools/doctest/test/Source.test.ts | 0 .../tools/doctest/test/Transform.test.ts | 0 .../test/fixtures/markdown/docs/helper.ts | 0 .../test/fixtures/markdown/docs/typed.md | 0 .../test/fixtures/markdown/docs/typed.mdx | 0 .../test/fixtures/markdown/src/typed.ts | 0 .../test/fixtures/markdown/vitest.config.ts | 0 .../tools/doctest/tsconfig.json | 0 packages/tools/graphql-generator/CHANGELOG.md | 21 + .../tools/graphql-generator}/LICENSE | 0 packages/tools/graphql-generator/README.md | 685 + packages/tools/graphql-generator/package.json | 73 + .../tools/graphql-generator/src/Config.ts | 84 + .../tools/graphql-generator/src/Generator.ts | 119 + .../tools/graphql-generator}/src/bin.ts | 0 .../graphql-generator/src/internal/Ast.ts | 452 + .../graphql-generator/src/internal/Cli.ts | 169 + .../src/internal/Diagnostic.ts | 79 + .../graphql-generator/src/internal/Emitter.ts | 1593 + .../src/internal/Generate.ts | 286 + .../graphql-generator/src/internal/Glob.ts | 94 + .../src/internal/IntrospectionReader.ts | 233 + .../graphql-generator/src/internal/Lexer.ts | 435 + .../graphql-generator/src/internal/Parser.ts | 791 + .../graphql-generator/src/internal/Printer.ts | 255 + .../src/internal/SchemaModel.ts | 204 + .../src/internal/SdlReader.ts | 237 + .../src/internal/Validate.ts | 1176 + .../graphql-generator/src/internal/Watch.ts | 439 + packages/tools/graphql-generator/src/main.ts | 180 + .../graphql-generator/test/Config.test.ts | 29 + .../graphql-generator/test/Diagnostic.test.ts | 47 + .../test/Generator.abstract.test.ts | 45 + .../test/Generator.bindings.test.ts | 122 + .../test/Generator.diagnostics.test.ts | 136 + .../test/Generator.directives.test.ts | 114 + .../test/Generator.mapping.test.ts | 111 + .../test/Generator.naming.test.ts | 153 + .../test/Generator.output.test.ts | 220 + .../test/Generator.runtime.abstract.test.ts | 218 + .../test/Generator.runtime.test.ts | 109 + .../test/Generator.snapshots.test.ts | 72 + .../test/Generator.template.test.ts | 177 + .../tools/graphql-generator/test/Glob.test.ts | 36 + .../test/GraphQLGeneratorCli.test.ts | 250 + .../graphql-generator/test/Lexical.test.ts | 57 + .../test/Parser.executable.test.ts | 74 + .../test/Parser.typeSystem.test.ts | 133 + .../graphql-generator/test/Printer.test.ts | 69 + .../test/SchemaReaders.fixtures.test.ts | 116 + .../graphql-generator/test/Validate.test.ts | 289 + .../graphql-generator/test/Watch.test.ts | 432 + .../test/fixtures/github/README.md | 39 + .../github/documents/fragments.graphql | 16 + .../fixtures/github/documents/issues.graphql | 36 + .../test/fixtures/github/documents/scalars.ts | 11 + .../github/documents/timeline.graphql | 39 + .../github/operations/issue-timeline.graphql | 48 + .../fixtures/github/operations/search.graphql | 46 + .../test/fixtures/github/schema.graphql | 699 + .../fixtures/introspection/modern.graphql | 30 + .../test/fixtures/introspection/modern.json | 1470 + .../test/fixtures/sdl/extensions.graphql | 67 + .../test/fixtures/sdl/one-of.graphql | 16 + .../test/fixtures/sdl/subscriptions.graphql | 111 + .../test/fixtures/subscriptions/rooms.graphql | 25 + .../generated/subscriptions/rooms.graphql.ts | 105 + .../generated/subscriptions/shared.graphql.ts | 35 + .../tools/graphql-generator/test/utils/ast.ts | 151 + .../graphql-generator/test/utils/generator.ts | 273 + .../graphql-generator/test/utils/model.ts | 217 + .../tools/graphql-generator}/tsconfig.json | 0 .../typetest/SelectionShape.tst.ts | 10 + .../tools/jsdocs/package.json | 2 +- .../tools/jsdocs/src/Jsdocs.ts | 0 .../tools/jsdocs/src/bin.ts | 0 .../tools/jsdocs/test/jsdocs.test.ts | 0 .../tools/jsdocs/tsconfig.json | 0 packages/tools/npm-promote/README.md | 88 + packages/tools/npm-promote/package.json | 29 + packages/tools/npm-promote/src/Cli.ts | 78 + packages/tools/npm-promote/src/GitHub.ts | 33 + packages/tools/npm-promote/src/NpmPromote.ts | 274 + .../tools/npm-promote/src/NpmPromoteError.ts | 28 + packages/tools/npm-promote/src/Process.ts | 36 + packages/tools/npm-promote/src/Release.ts | 247 + .../tools/npm-promote/src/TrackingIssue.ts | 143 + .../tools/npm-promote/src/WebsiteDeploy.ts | 107 + packages/tools/npm-promote/src/Workspace.ts | 70 + packages/tools/npm-promote/src/bin.ts | 19 + .../tools/npm-promote/test/NpmPromote.test.ts | 121 + .../tools/npm-promote/test/Release.test.ts | 234 + packages/tools/npm-promote/test/utils.ts | 54 + .../tools/npm-promote}/tsconfig.json | 0 .../tools/openapi-generator/CHANGELOG.md | 25 + .../tools/openapi-generator}/LICENSE | 0 .../tools/openapi-generator/README.md | 0 .../tools/openapi-generator/package.json | 4 +- .../src/HttpApiTransformer.ts | 0 .../src/JsonSchemaGenerator.ts | 0 .../openapi-generator/src/OpenApiGenerator.ts | 0 .../openapi-generator/src/OpenApiPatch.ts | 0 .../src/OpenApiTransformer.ts | 8 +- .../openapi-generator/src/ParsedOperation.ts | 0 .../tools/openapi-generator/src/Utils.ts | 0 packages/tools/openapi-generator/src/bin.ts | 10 + .../tools/openapi-generator/src/main.ts | 0 .../test/JsonSchemaGenerator.test.ts | 0 .../JsonSchemaGeneratorRepresentation.test.ts | 0 .../test/OpenApiGenerator.test.ts | 13 +- .../test/OpenApiGeneratorCli.test.ts | 0 .../test/OpenApiPatch.test.ts | 0 .../test/OpenApiTransformer.test.ts | 0 .../openapi-generator/test/Utils.test.ts | 0 .../test/fixtures/cli-basic-spec.json | 0 .../test/fixtures/cli-warning-spec.json | 0 .../test/fixtures/patches/invalid-op.json | 0 .../test/fixtures/patches/missing-path.json | 0 .../test/fixtures/patches/missing-value.json | 0 .../test/fixtures/patches/valid-add.json | 0 .../test/fixtures/patches/valid-multiple.json | 0 .../test/fixtures/patches/valid-patch.yaml | 0 .../test/fixtures/patches/valid-remove.json | 0 .../test/fixtures/patches/valid-replace.json | 0 .../tools/openapi-generator}/tsconfig.json | 0 .../tools/oxc/oxlintrc.json | 0 .../tools/oxc/package.json | 4 +- .../tools/oxc/src/oxlint/index.ts | 0 .../src/oxlint/rules/no-bigint-literals.ts | 0 .../rules/no-import-from-barrel-package.ts | 0 .../oxlint/rules/no-js-extension-imports.ts | 0 .../oxlint/rules/no-opaque-instance-fields.ts | 0 .../src/oxlint/rules/no-unused-internal.ts | 0 .../no-import-from-barrel-package.test.ts | 0 .../oxc/test/no-js-extension-imports.test.ts | 0 .../test/no-opaque-instance-fields.test.ts | 0 .../tools/oxc/test/no-unused-internal.test.ts | 0 .../tools/oxc/test/utils.ts | 0 packages/tools/oxc/tsconfig.json | 8 + .../tools/utils/package.json | 2 +- .../tools/utils/src/Codegen.ts | 0 .../tools/utils/src/Glob.ts | 0 .../tools/utils/src/bin.ts | 0 .../tools/utils/src/commands/codegen.ts | 0 .../tools/utils/test/Codegen.test.ts | 0 packages/tools/utils/tsconfig.json | 9 + .../packages => packages}/vitest/CHANGELOG.md | 21 + packages/vitest/LICENSE | 21 + .../packages => packages}/vitest/README.md | 0 .../packages => packages}/vitest/package.json | 4 +- .../packages => packages}/vitest/src/index.ts | 0 .../vitest/src/internal/internal.ts | 0 .../packages => packages}/vitest/src/utils.ts | 0 .../test/arbitrary-configuration.test.ts | 0 .../vitest/test/fixtures.test.ts | 0 .../vitest/test/index.test.ts | 0 .../vitest/test/isolation.test.ts | 0 .../vitest/test/layer-concurrency.test.ts | 0 .../vitest/test/nested-isolation.test.ts | 0 .../vitest/test/timeout.test.ts | 0 .../vitest/tsconfig.json | 0 .../vitest/typetest/index.tst.ts | 0 packages/vue-components/.prettierrc | 7 - packages/vue-components/.storybook/main.ts | 14 - packages/vue-components/.storybook/preview.ts | 52 - .../vue-components/.storybook/tsconfig.json | 9 - .../vue-components/.vscode/extensions.json | 7 - packages/vue-components/CHANGELOG.md | 6599 -- packages/vue-components/README.md | 233 - .../__tests__/FixedNuxtErrorBoundary.test.ts | 213 - .../__tests__/IntegerValidation.test.ts | 49 - .../__tests__/OmegaForm.test.ts | 205 - .../OmegaForm/CustomFilterFieldSchema.test.ts | 37 - .../OmegaForm/DateValidation.test.ts | 68 - .../OmegaForm/DecimalSeparatorInput.test.ts | 115 - .../OmegaForm/DecodeTransformFallback.test.ts | 151 - .../OmegaForm/DefaultValuesPriority.test.ts | 23 - .../__tests__/OmegaForm/Defaults.test.ts | 551 - .../OmegaForm/Defaults.values.test.ts | 114 - .../OmegaForm/DialogBlockingI18n.test.ts | 82 - .../OmegaForm/EmailFormatDetection.test.ts | 11 - .../OmegaForm/EmptyStringValidation.test.ts | 77 - .../__tests__/OmegaForm/GeneralErrors.test.ts | 59 - .../OmegaForm/IndexedInputErrors.test.ts | 82 - .../OmegaForm/InputAttributes.test.ts | 221 - .../OmegaForm/InputReactivity.test.ts | 49 - .../__tests__/OmegaForm/InputRegistry.test.ts | 95 - .../OmegaForm/IntersectionError.test.ts | 223 - .../__tests__/OmegaForm/Meta.test.ts | 40 - .../NullableFieldInitialization.test.ts | 184 - .../NullableNestedStructValidation.test.ts | 148 - .../__tests__/OmegaForm/OmegaAutoGen.test.ts | 331 - .../__tests__/OmegaForm/OptionalKey.test.ts | 62 - .../__tests__/OmegaForm/RedactedMeta.test.ts | 181 - .../SelectMultipleLocalization.test.ts | 123 - .../__tests__/OmegaForm/SubmitEffect.test.ts | 142 - .../OmegaForm/SubmitErrorClear.test.ts | 59 - .../OmegaForm/TaggedUnionBehavior.test.ts | 251 - .../TaggedUnionLegacyWarning.test.ts | 31 - .../OmegaForm/TaggedUnionNested.test.ts | 54 - .../OmegaForm/TaggedUnionRequired.test.ts | 81 - .../OmegaForm/TaggedUnionRoot.test.ts | 77 - .../OmegaForm/TransformationSchema.test.ts | 84 - .../OmegaForm/TransformedStruct.test.ts | 174 - .../OmegaForm/UnregisteredFieldError.test.ts | 94 - .../WithDefaultConstructorPersistency.test.ts | 107 - .../createUseFormWithCustomInput.test.ts | 374 - .../__tests__/OmegaFormTest.vue | 62 - .../__tests__/OmegaIntlProvider.vue | 12 - .../migration/schema-v4-migration.test.ts | 703 - packages/vue-components/__tests__/setup.ts | 16 - .../__tests__/test-array-meta.test.ts | 55 - .../__tests__/test-nullor-duplicate.test.ts | 42 - .../__tests__/test-union-meta.test.ts | 17 - .../vue-components/__tests__/tsconfig.json | 9 - .../plans/2026-04-27-omegaform-refactor.md | 2655 - .../2026-04-27-omegaform-refactor-design.md | 242 - packages/vue-components/env.d.ts | 8 - packages/vue-components/eslint.config.mjs | 20 - packages/vue-components/package.json | 95 - packages/vue-components/src/assets/.keep | 0 .../src/components/CommandButton.vue | 161 - .../vue-components/src/components/Dialog.vue | 58 - .../src/components/FixedNuxtErrorBoundary.vue | 112 - .../src/components/OmegaForm/InputProps.ts | 100 - .../src/components/OmegaForm/OmegaArray.vue | 76 - .../src/components/OmegaForm/OmegaAutoGen.vue | 105 - .../OmegaForm/OmegaErrorsInternal.vue | 238 - .../components/OmegaForm/OmegaFormInput.vue | 41 - .../src/components/OmegaForm/OmegaInput.vue | 95 - .../OmegaForm/OmegaInputVuetify.vue | 370 - .../OmegaForm/OmegaInternalInput.vue | 207 - .../components/OmegaForm/OmegaTaggedUnion.vue | 106 - .../OmegaForm/OmegaTaggedUnionInternal.vue | 41 - .../src/components/OmegaForm/OmegaWrapper.vue | 73 - .../src/components/OmegaForm/blockDialog.ts | 73 - .../OmegaForm/createUseFormWithCustomInput.ts | 70 - .../OmegaForm/decimalSeparatorInput.ts | 38 - .../src/components/OmegaForm/errors.ts | 139 - .../src/components/OmegaForm/getOmegaStore.ts | 31 - .../src/components/OmegaForm/hocs.ts | 19 - .../src/components/OmegaForm/index.ts | 27 - .../src/components/OmegaForm/inputs.ts | 22 - .../src/components/OmegaForm/meta/checks.ts | 107 - .../components/OmegaForm/meta/createMeta.ts | 140 - .../src/components/OmegaForm/meta/defaults.ts | 267 - .../src/components/OmegaForm/meta/redacted.ts | 71 - .../src/components/OmegaForm/meta/types.ts | 78 - .../src/components/OmegaForm/meta/walker.ts | 248 - .../OmegaForm/onMountedWithCleanup.ts | 22 - .../src/components/OmegaForm/persistency.ts | 247 - .../src/components/OmegaForm/submit.ts | 131 - .../src/components/OmegaForm/types.ts | 801 - .../src/components/OmegaForm/useOmegaForm.ts | 200 - .../components/OmegaForm/useRegisterField.ts | 17 - .../OmegaForm/validation/localized.ts | 242 - .../vue-components/src/components/index.ts | 4 - .../vue-components/src/components/style.css | 3 - packages/vue-components/src/index.ts | 20 - packages/vue-components/src/reset.css | 52 - packages/vue-components/src/utils/index.ts | 31 - .../stories/Commands.stories.ts | 46 - .../vue-components/stories/Commands/One.vue | 122 - .../stories/Commands/components.ts | 1 - .../stories/Commands/helpers.ts | 82 - .../stories/Commands/useEffectRouter.ts | 73 - .../vue-components/stories/Dialog.stories.ts | 34 - .../stories/Dialog/AsyncBlocking.vue | 47 - .../stories/Dialog/AsyncBlockingContent.vue | 91 - .../stories/Dialog/SimpleBlocking.vue | 49 - .../stories/Dialog/SimpleBlockingContent.vue | 30 - .../stories/Dialog/UploadBlocking.vue | 47 - .../stories/Dialog/UploadBlockingContent.vue | 105 - .../stories/FixedNuxtErrorBoundary.stories.ts | 18 - .../stories/FixedNuxtErrorBoundary/Demo.vue | 92 - .../stories/OmegaForm.stories.ts | 448 - .../stories/OmegaForm/Array.vue | 118 - .../stories/OmegaForm/AutoGeneration.vue | 74 - .../stories/OmegaForm/Booleans.vue | 42 - .../stories/OmegaForm/Clearable.vue | 25 - .../stories/OmegaForm/ComplexForm.vue | 147 - .../stories/OmegaForm/CustomInput.vue | 62 - .../OmegaForm/CustomInputClassName.vue | 53 - .../stories/OmegaForm/CustomLabelSlot.vue | 146 - .../vue-components/stories/OmegaForm/Date.vue | 35 - .../OmegaForm/DecodeTransformFallback.vue | 48 - .../stories/OmegaForm/Defaults.vue | 208 - .../OmegaForm/DialogBlockingExamples.vue | 76 - .../stories/OmegaForm/EmailForm.vue | 46 - .../stories/OmegaForm/EnterSubmitRepro.vue | 78 - .../stories/OmegaForm/FormTaggedUnion.vue | 116 - .../stories/OmegaForm/IndexedTable.vue | 79 - .../stories/OmegaForm/InputRegistry.vue | 109 - .../OmegaForm/IntegerValidationGerman.vue | 83 - .../stories/OmegaForm/IntersectionExample.vue | 55 - .../vue-components/stories/OmegaForm/Meta.vue | 92 - .../vue-components/stories/OmegaForm/Null.vue | 75 - .../stories/OmegaForm/Nullable.vue | 85 - .../OmegaForm/NullableNestedStruct.vue | 69 - .../stories/OmegaForm/NumberInput.vue | 68 - .../OmegaForm/OmitConstructorDefaults.vue | 131 - .../stories/OmegaForm/OptionalKey.vue | 142 - .../stories/OmegaForm/PersistencyForm.vue | 74 - ...rogrammaticallyHandleSubmitCheckErrors.vue | 39 - .../stories/OmegaForm/RatingInput.vue | 49 - .../stories/OmegaForm/Redacted.vue | 80 - .../OmegaForm/RootLevelTaggedUnion.vue | 127 - .../OmegaForm/RootLevelTaggedUnionLegacy.vue | 118 - .../stories/OmegaForm/SetErrorOnSubmit.vue | 35 - .../stories/OmegaForm/SimpleForm.vue | 43 - .../OmegaForm/SimpleFormVuetifyDefault.vue | 37 - .../stories/OmegaForm/SumExample.vue | 43 - .../stories/OmegaForm/Tanstack.vue | 210 - .../stories/OmegaForm/Union.vue | 77 - .../stories/OmegaForm/UsingOmegaForm.vue | 284 - .../OmegaForm/WindowExitPrevention.vue | 280 - .../OmegaForm/WithDefaultConstructor.vue | 53 - .../createUseFormWIthCustomInput.vue | 45 - .../stories/OmegaForm/form.Input.vue | 129 - packages/vue-components/stories/README.md | 29 - packages/vue-components/stories/tsconfig.json | 9 - packages/vue-components/tsconfig.build.json | 11 - packages/vue-components/tsconfig.json | 35 - .../vue-components/types/vuetify-shims.d.ts | 6 - packages/vue-components/vite.config.mts | 89 - packages/vue-components/vitest.config.mts | 19 - packages/vue/.madgerc | 7 - packages/vue/.npmignore | 14 - packages/vue/.prettierignore | 6 - packages/vue/CHANGELOG.md | 14438 ---- packages/vue/docs/atom-query-api-redesign.md | 207 - packages/vue/docs/mutation-command-atoms.md | 40 - packages/vue/docs/query-key-invalidation.md | 107 - packages/vue/examples/streamMutation.ts | 72 - packages/vue/package.json | 102 - packages/vue/src/atomQuery.ts | 600 - packages/vue/src/commander.ts | 3408 - packages/vue/src/confirm.ts | 34 - packages/vue/src/dependencyMetadata.ts | 84 - packages/vue/src/errorReporter.ts | 108 - packages/vue/src/form.ts | 501 - packages/vue/src/index.ts | 8 - packages/vue/src/internal/tanstackQuery.ts | 308 - packages/vue/src/intl.ts | 12 - packages/vue/src/lib.ts | 115 - packages/vue/src/liveQueryInvalidation.ts | 138 - packages/vue/src/makeClient.ts | 1314 - packages/vue/src/makeContext.ts | 34 - packages/vue/src/makeIntl.ts | 77 - packages/vue/src/makeUseCommand.ts | 29 - packages/vue/src/mutate.ts | 516 - packages/vue/src/query.ts | 940 - packages/vue/src/queryLifetime.ts | 74 - packages/vue/src/routeParams.ts | 55 - packages/vue/src/runtime.ts | 1 - packages/vue/src/suspense.ts | 43 - packages/vue/src/toast.ts | 1 - packages/vue/src/withToast.ts | 1 - packages/vue/test/Mutation.test.ts | 917 - .../vue/test/dependencyInvalidation.test.ts | 444 - .../vue/test/form-validation-errors.test.ts | 163 - packages/vue/test/form.test.ts | 280 - .../vue/test/inactiveInvalidation.test.ts | 172 - .../vue/test/interrupt-refetch-repro.test.ts | 625 - packages/vue/test/lib.test.ts | 240 - .../vue/test/liveQueryInvalidation.test.ts | 108 - packages/vue/test/makeClient.test.ts | 492 - packages/vue/test/query-span.test.ts | 92 - packages/vue/test/queryOptions.test.ts | 88 - packages/vue/test/streamFinal.test.ts | 64 - packages/vue/test/streamFn.test.ts | 457 - packages/vue/test/stubs.ts | 310 - packages/vue/test/suspense-regression.test.ts | 168 - packages/vue/test/suspense.test.ts | 160 - packages/vue/tsconfig.examples.json | 20 - packages/vue/tsconfig.json | 15 - packages/vue/tsconfig.src.json | 36 - packages/vue/tsconfig.test.json | 33 - packages/vue/vitest.config.ts | 18 - patches/.gitkeep | 0 patches/@ben_12__eslint-plugin-dprint.patch | 14 - patches/@changesets__cli@2.31.0.patch | 22 - .../@changesets__get-github-info@1.0.1.patch | 0 patches/@sendgrid__mail.patch | 23 - patches/@tanstack__query-core.patch | 203 - .../ts-plugin-sort-import-suggestions.patch | 45 - patches/typescript.patch | 159 - pnpm-lock.yaml | 21402 ++--- pnpm-workspace.yaml | 70 +- repos/effect/.changeset/config.json | 55 - repos/effect/.envrc | 1 - .../.github/actions/deploy-website/action.yml | 40 - repos/effect/.gitignore | 41 - repos/effect/.oxlintrc.json | 36 - repos/effect/.vscode/extensions.json | 6 - repos/effect/.vscode/settings.json | 51 - repos/effect/README.md | 97 - repos/effect/flake.lock | 27 - repos/effect/flake.nix | 24 - .../annotations/effect__ExecutionPlan.yaml | 9 - repos/effect/package.json | 84 - .../packages/ai/cloudflare/CHANGELOG.md | 17 - .../packages/ai/openai/src/Generated.ts | 35796 -------- .../packages/effect/src/internal/version.ts | 2 - .../effect/test/http/HttpTraceContext.test.ts | 53 - .../effect/test/http/UrlParams.test.ts | 72 - .../test/observability/OtlpTracer.test.ts | 35 - .../platform/deno/test/DenoFileSystem.test.ts | 69 - repos/effect/packages/sql/pg/test/utils.ts | 80 - .../tools/ai-codegen/src/Generator.ts | 229 - repos/effect/pnpm-lock.yaml | 12419 --- repos/effect/pnpm-workspace.yaml | 46 - repos/effect/tsconfig.base.json | 66 - repos/effect/vitest.config.ts | 222 - repos/effect/vitest.setup.ts | 4 - .../effect/scratchpad => scratchpad}/index.ts | 0 .../scratchpad => scratchpad}/package.json | 0 .../scratchpad => scratchpad}/tsconfig.json | 0 scripts/agent-gh.mjs | 60 - scripts/agent-pr-ready.mjs | 65 - scripts/agent-pre-push.mjs | 140 - .../scripts => scripts}/bundle-analyze.sh | 0 .../bundle-compare-selected.sh | 0 .../scripts => scripts}/bundle-compare.sh | 0 .../scripts => scripts}/check-dist-types.mjs | 0 .../effect/scripts => scripts}/circular.mjs | 0 scripts/clean-dist.sh | 41 - {repos/effect/scripts => scripts}/clean.mjs | 0 {repos/effect/scripts => scripts}/codemod.mjs | 0 .../scripts => scripts}/codemods/jsdoc.ts | 0 scripts/convert_patch.js | 16 - .../scripts => scripts}/copy-ai-docs.mjs | 0 .../effect/scripts => scripts}/effect-bot.mjs | 0 scripts/extract.sh | 26 - scripts/hooks/post-checkout.test.sh | 84 - scripts/install-git-hooks.mjs | 71 - scripts/lib/agent-env.mjs | 26 - scripts/lib/agent-gh-policy.mjs | 155 - scripts/lib/agent-pr-state.mjs | 119 - scripts/lib/agent-ship-gate-cache.mjs | 121 - scripts/mergeTsConfig.mjs | 49 - .../scripts => scripts}/package-scalar.mjs | 0 .../scripts => scripts}/package-swagger.mjs | 0 .../effect/scripts => scripts}/package.json | 0 scripts/rewrite-dist-declaration-imports.mjs | 37 - .../set-strip-internal.mjs | 0 .../scripts => scripts}/setup-agents.mjs | 0 scripts/sort.js | 31 - scripts/stagePublishPackage.mjs | 157 - scripts/sync-effect-subtree.js | 8 - .../effect/scripts => scripts}/tsconfig.json | 0 {repos/effect/scripts => scripts}/version.mjs | 0 .../scripts => scripts}/worktree-setup.sh | 0 t3.json | 11 - tsconfig.all.json | 29 - tsconfig.base.json | 124 +- repos/effect/tsconfig.json => tsconfig.json | 0 tsconfig.lib.json | 14 - ...ig.packages.json => tsconfig.packages.json | 2 + tsconfig.plugins.json | 27 - ...tsconfig.tests.json => tsconfig.tests.json | 4 + repos/effect/tstyche.json => tstyche.json | 0 vite.config.base.ts | 40 - vitest.config.ts | 219 +- repos/effect/vitest.docs.ts => vitest.docs.ts | 0 vitest.setup.ts | 1 + wiki/Home.md | 3 - wiki/ResourceClients.md | 41 - wiki/bag-of-tricks.md | 95 - .../effect\342\200\220app\342\200\220cli.md" | 344 - wiki/error-reporter-migration.md | 174 - wiki/repository-derived-query-invalidation.md | 291 - 3374 files changed, 135314 insertions(+), 208003 deletions(-) rename {repos/effect/.agents => .agents}/AGENTS.md (100%) rename {repos/effect/.agents => .agents}/skills/ai-docs/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/bundle-analysis/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/changesets/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/changesets/authoring.md (100%) rename {repos/effect/.agents => .agents}/skills/ci-maintenance/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/ci-maintenance/privileged-workflows.md (100%) rename {repos/effect/.agents => .agents}/skills/dependency-maintenance/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/dependency-maintenance/coordinated-upgrades.md (100%) rename {repos/effect/.agents => .agents}/skills/dependency-maintenance/manifest-roles.md (100%) rename {repos/effect/.agents => .agents}/skills/effect-development/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/categories.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/declarations.md (100%) rename {repos/effect/.agents => .agents}/skills/jsdocs/examples.md (100%) rename {repos/effect/.agents => .agents}/skills/migration-guidance/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/migration-guidance/annotations.md (100%) rename {repos/effect/.agents => .agents}/skills/migration-guidance/generation.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/dependencies.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/publishing.md (100%) rename {repos/effect/.agents => .agents}/skills/package-development/registration.md (100%) rename {repos/effect/.agents => .agents}/skills/performance-analysis/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/performance-analysis/runtime.md (100%) rename {repos/effect/.agents => .agents}/skills/performance-analysis/types.md (100%) create mode 100644 .agents/skills/pull-requests/SKILL.md rename {repos/effect/.agents => .agents}/skills/test-development/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/displayed-types.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/runtime.md (100%) rename {repos/effect/.agents => .agents}/skills/test-development/types.md (100%) rename {repos/effect/.agents => .agents}/skills/vendored-assets/SKILL.md (100%) rename {repos/effect/.agents => .agents}/skills/vendored-assets/browser-assets.md (100%) delete mode 100644 .changeset/README.md delete mode 100644 .changeset/update-effect-4.0.2.md delete mode 100644 .claude/settings.json delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .devcontainer/humanlog.sh delete mode 100644 .devcontainer/usepnpm.sh delete mode 100644 .githooks/README.md delete mode 100755 .githooks/post-checkout delete mode 100755 .githooks/pre-commit delete mode 100755 .githooks/pre-push delete mode 100644 .github/FUNDING.yml rename {repos/effect/.github => .github}/actions/setup/action.yaml (100%) delete mode 100644 .github/npm-trusted-publishing.md rename {repos/effect/.github => .github}/workflows/ai-codegen.yml (100%) rename {repos/effect/.github => .github}/workflows/bundle-comment.yml (100%) rename {repos/effect/.github => .github}/workflows/check.yml (75%) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/claude.yml rename {repos/effect/.github => .github}/workflows/cluster.yml (100%) rename {repos/effect/.github => .github}/workflows/effect-bot.yml (100%) create mode 100644 .github/workflows/npm-promote.yml delete mode 100644 .github/workflows/publish-snapshot.yml delete mode 100644 .github/workflows/publish-wiki.yml rename {repos/effect/.github => .github}/workflows/release-queue.yml (56%) delete mode 100644 .github/workflows/release.yaml rename {repos/effect/.github => .github}/workflows/release.yml (85%) rename {repos/effect/.github => .github}/workflows/snapshot.yml (100%) delete mode 100644 .ncurc.json delete mode 100644 .npmrc delete mode 100644 .nvmrc delete mode 100644 .specs/Update-to-latest-Effect.md delete mode 100644 .specs/changeset-publishing.md delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/model.code-snippets delete mode 100644 .vscode/service.code-snippets delete mode 100644 .vscode/tasks.json delete mode 100644 AGENTS.md delete mode 100644 CLAUDE.md rename repos/effect/LICENSE => LICENSE (100%) rename repos/effect/LLMS.md => LLMS.md (92%) rename repos/effect/MIGRATION.md => MIGRATION.md (97%) rename {repos/effect/ai-docs => ai-docs}/README.md (100%) rename {repos/effect/ai-docs => ai-docs}/package.json (95%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/01_effect-gen.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/02_effect-fn.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/10_creating-effects.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/01_basics/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/02_schema/10_schema-basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/02_schema/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/01_service.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/10_reference.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/20_layer-composition.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/20_layer-unwrap.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/03_services/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/01_error-handling.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/10_catch-tags.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/20_reason-errors.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/04_errors/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/10_acquire-release.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/20_layer-side-effects.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/30_layer-map.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/05_resources/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/06_running/10_run-main.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/06_running/20_layer-launch.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/06_running/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/07_pubsub/10_pubsub.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/01_effect/07_pubsub/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/10_creating-streams.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/20_consuming-streams.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/30_encoding.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/03_stream/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/04_integration/10_managed-runtime.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/04_integration/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/05_batching/10_request-resolver.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/05_batching/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/06_schedule/10_schedules.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/06_schedule/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/07_datetime/10_creating-and-formatting.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/07_datetime/20_time-zones.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/07_datetime/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/08_observability/10_logging.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/08_observability/20_otlp-tracing.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/08_observability/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/09_testing/10_effect-tests.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/09_testing/20_layer-tests.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/09_testing/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/10_predicate/01_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/10_predicate/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/40_sql/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/40_sql/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/50_http-client/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/50_http-client/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/20_testing.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/Api.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/Authorization.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/System.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/api/Users.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/domain/User.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/domain/UserErrors.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/server/Authorization.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/server/Users.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/fixtures/server/Users/http.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/51_http-server/index.md (100%) create mode 100644 ai-docs/src/52_graphql/10_basics.ts create mode 100644 ai-docs/src/52_graphql/20_testing.ts create mode 100644 ai-docs/src/52_graphql/30_batching.ts create mode 100644 ai-docs/src/52_graphql/40_aliased-batching.ts create mode 100644 ai-docs/src/52_graphql/50_errors-and-retries.ts create mode 100644 ai-docs/src/52_graphql/index.md rename {repos/effect/ai-docs => ai-docs}/src/60_child-process/10_working-with-child-processes.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/60_child-process/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/70_cli/10_basics.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/70_cli/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/10_language-model.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/20_tools.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/30_chat.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/fixtures/domain/LaunchPlan.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/71_ai/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/80_cluster/10_entities.ts (100%) rename {repos/effect/ai-docs => ai-docs}/src/80_cluster/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/src/index.md (100%) rename {repos/effect/ai-docs => ai-docs}/tsconfig.json (100%) delete mode 100644 assets/dev/effect-app-apple-touch-180.png rename repos/effect/deno.json => deno.json (93%) rename repos/effect/dprint.json => dprint.json (93%) delete mode 100644 dprint.jsonc rename repos/effect/jsdocs.config.json => jsdocs.config.json (100%) delete mode 100644 lint-staged.config.js rename {repos/effect/migration => migration}/annotations/README.md (100%) rename {repos/effect/migration => migration}/annotations/effect__Arbitrary.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Array.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__BigDecimal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__BigInt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Brand.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Cache.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Cause.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Channel.yaml (98%) rename {repos/effect/migration => migration}/annotations/effect__ChildExecutorDecision.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Chunk.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Clock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Config.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ConfigError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ConfigProvider.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ConfigProviderPathPatch.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Console.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Context.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Cron.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Data.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__DateTime.yaml (83%) rename {repos/effect/migration => migration}/annotations/effect__DefaultServices.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Deferred.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Differ.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Duration.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Effect.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Effectable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Either.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Encoding.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Equal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Equivalence.yaml (100%) create mode 100644 migration/annotations/effect__ExecutionPlan.yaml rename {repos/effect/migration => migration}/annotations/effect__ExecutionStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Exit.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FastCheck.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Fiber.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberHandle.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberRefs.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberRefsPatch.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__FiberStatus.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Function.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__GlobalValue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Graph.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__GroupBy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Hash.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__HashMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__HashSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Inspectable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Iterable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__JSONSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__KeyedPool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Layer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__LayerMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__List.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__LogLevel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__LogSpan.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Logger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Mailbox.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ManagedRuntime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Match.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MergeDecision.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MergeState.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MergeStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Metric.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricBoundaries.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricHook.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricKey.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricKeyType.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricLabel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricPair.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricPolling.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricRegistry.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MetricState.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Micro.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ModuleVersion.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableHashMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableHashSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableList.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__MutableRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Number.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Option.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Order.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Ordering.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ParseResult.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__PartitionedSemaphore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Pipeable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Pool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Predicate.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Pretty.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__PubSub.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Queue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Random.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RateLimiter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RcMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RcRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Readable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Record.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RedBlackTree.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Redacted.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Ref.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Reloadable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Request.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RequestBlock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RequestResolver.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Resource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Runtime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RuntimeFlags.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__RuntimeFlagsPatch.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__STM.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Schedule.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScheduleDecision.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScheduleInterval.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScheduleIntervals.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Scheduler.yaml (86%) rename {repos/effect/migration => migration}/annotations/effect__Schema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SchemaAST.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Scope.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScopedCache.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ScopedRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Secret.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SingleProducerAsyncInput.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Sink.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SortedMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SortedSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Stream.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__StreamEmit.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__StreamHaltStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Streamable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Struct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Subscribable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SubscriptionRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Supervisor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Symbol.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__SynchronizedRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TArray.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TDeferred.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TPriorityQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TPubSub.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TRandom.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TReentrantLock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TSemaphore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TSet.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TSubscriptionRef.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Take.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestAnnotation.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestAnnotationMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestAnnotations.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestClock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestContext.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestLive.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestServices.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__TestSized.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Tracer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Trie.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Tuple.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Types.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__UpstreamPullRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__UpstreamPullStrategy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__Utils.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__AmazonBedrockTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__EventStreamEncoding.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-amazon-bedrock__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__AnthropicTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-anthropic__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__GoogleTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-google__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiEmbeddingModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiLanguageModel.yaml (68%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiTelemetry.yaml (78%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiTokenizer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__OpenAiTool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openai__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__Generated.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__OpenRouterClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__OpenRouterConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__OpenRouterLanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai-openrouter__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__AiError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Chat.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__EmbeddingModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__IdGenerator.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__LanguageModel.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__McpSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__McpServer.yaml (74%) rename {repos/effect/migration => migration}/annotations/effect__ai__Model.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Prompt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Response.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Telemetry.yaml (78%) rename {repos/effect/migration => migration}/annotations/effect__ai__Tool.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__Toolkit.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__ai__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Args.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__AutoCorrect.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__BuiltInOptions.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CliApp.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CliConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Command.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CommandDescriptor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__CommandDirective.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__ConfigFile.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__HelpDoc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__HelpDoc__Span.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Options.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Primitive.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Prompt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__Usage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__ValidationError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cli__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterCron.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ClusterWorkflowEngine.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__DeliverAt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Entity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityAddress.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityProxy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityProxyServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__EntityResource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Envelope.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__HttpRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__K8sHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__MachineId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Message.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__MessageStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Reply.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Runner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerAddress.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerHealth.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__RunnerStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Runners.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ShardId.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Sharding.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ShardingConfig.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__ShardingRegistrationEvent.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SingleRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Singleton.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SingletonAddress.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__Snowflake.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SocketRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SqlMessageStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__SqlRunnerStorage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__TestRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__cluster__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools__Client.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools__Domain.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__DevTools__Server.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Event.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventGroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventJournal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLog.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLogRemote.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLogServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__EventLogServer__Cloudflare.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine__Procedure.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine__ProcedureList.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Machine__SerializableProcedureList.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__PersistedCache.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__PersistedQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__PersistedQueue__Redis.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Persistence.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Persistence__Lmdb.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Persistence__Redis.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__RateLimiter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__RateLimiter__Redis.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Reactivity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__RequestResolver.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__Sse.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__VariantSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__experimental__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Logger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Metrics.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__NodeSdk.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Otlp.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpLogger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpMetrics.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpResource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__OtlpTracer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Resource.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__Tracer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__WebSdk.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__opentelemetry__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__BrowserHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__BrowserWorker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__BrowserWorkerRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__Clipboard.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__Geolocation.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__Permissions.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-browser__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunCommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunContext.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunFileSystem__ParcelWatcher.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunHttpPlatform.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunHttpServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunHttpServerRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunKeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunSink.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunSocket.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunSocketServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunStream.yaml (100%) create mode 100644 migration/annotations/effect__platform-bun__BunTerminal.yaml rename {repos/effect/migration => migration}/annotations/effect__platform-bun__BunWorker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-bun__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeFileSystem__ParcelWatcher.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeMultipart.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeSink.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeSocket.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node-shared__NodeStream.yaml (100%) create mode 100644 migration/annotations/effect__platform-node-shared__NodeTerminal.yaml rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeCommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeContext.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeFileSystem__ParcelWatcher.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeHttpServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeKeyValueStore.yaml (100%) create mode 100644 migration/annotations/effect__platform-node__NodeTerminal.yaml rename {repos/effect/migration => migration}/annotations/effect__platform-node__NodeWorker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__Undici.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform-node__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__ChannelSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Command.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__CommandExecutor.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Cookies.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Effectify.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Error.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Etag.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__FetchHttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__FileSystem.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Headers.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiBuilder.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiClient.yaml (83%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiEndpoint.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiGroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiMiddleware.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiScalar.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiSecurity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApiSwagger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpApp.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpBody.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClientError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClientRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpClientResponse.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpIncomingMessage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpLayerRouter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpMethod.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpMiddleware.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpMultiplex.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpPlatform.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpRouter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServer.yaml (91%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerRequest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerRespondable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__HttpServerResponse.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__KeyValueStore.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__MsgPack.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Multipart.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Ndjson.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__OpenApi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__OpenApiJsonSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Path.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__PlatformConfigProvider.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__PlatformLogger.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Runtime.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Socket.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__SocketServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Template.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Terminal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Transferable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Url.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__UrlParams.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__Worker.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__WorkerError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__WorkerRunner.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__platform__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__Ansi.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__AnsiDoc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__Color.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer-ansi__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Doc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__DocStream.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__DocTree.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Flatten.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Layout.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__Optimize.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__PageWidth.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__printer__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__Rpc.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcClientError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcGroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcMessage.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcMiddleware.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcSerialization.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__RpcTest.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__rpc__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-clickhouse__ClickhouseClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-clickhouse__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-d1__D1Client.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-d1__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-drizzle__Mysql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-drizzle__Pg.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-drizzle__Sqlite.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Kysely.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Mssql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Mysql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Pg.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__Sqlite.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-kysely__patch.types.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-libsql__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__MssqlClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__Parameter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__Procedure.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mssql__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mysql2__MysqlClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-mysql2__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-pg__PgClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-pg__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-bun__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-bun__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-do__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-do__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-node__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-node__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-react-native__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-react-native__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-wasm__SqliteClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql-sqlite-wasm__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Migrator.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Migrator__FileSystem.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Model.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlClient.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlConnection.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlError.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlEventJournal.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlEventLogServer.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlPersistedQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlResolver.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__SqlSchema.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__Statement.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__sql__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Alternative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Applicative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Bicovariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Bounded.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Chainable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Contravariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Coproduct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Covariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Filterable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__FlatMap.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Foldable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Invariant.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Monad.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Monoid.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Of.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Pointed.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Product.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiAlternative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiApplicative.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiCoproduct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__SemiProduct.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Semigroup.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__Traversable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__TraversableFilterable.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Array.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__BigInt.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Boolean.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Duration.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Effect.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Either.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Identity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Micro.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Number.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Option.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Ordering.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Predicate.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Record.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__String.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__data__Tuple.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__typeclass__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__vitest__index.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__vitest__utils.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__Activity.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableClock.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableDeferred.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableQueue.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__DurableRateLimiter.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__Workflow.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__WorkflowEngine.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__WorkflowProxy.yaml (100%) rename {repos/effect/migration => migration}/annotations/effect__workflow__WorkflowProxyServer.yaml (100%) rename {repos/effect/migration => migration}/cause.md (100%) rename {repos/effect/migration => migration}/equality.md (100%) rename {repos/effect/migration => migration}/error-handling.md (100%) rename {repos/effect/migration => migration}/fiber-keep-alive.md (100%) rename {repos/effect/migration => migration}/fiberref.md (100%) rename {repos/effect/migration => migration}/forking.md (100%) rename {repos/effect/migration => migration}/generators.md (100%) rename {repos/effect/migration => migration}/layer-memoization.md (100%) rename {repos/effect/migration => migration}/runtime.md (100%) rename {repos/effect/migration => migration}/schema.md (100%) rename {repos/effect/migration => migration}/scope.md (100%) rename {repos/effect/migration => migration}/services.md (100%) rename {repos/effect/migration => migration}/v3-to-v4.md (99%) rename {repos/effect/migration => migration}/yieldable.md (100%) delete mode 100644 old.patch rename {repos/effect/packages => packages}/ai/anthropic/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/ai/anthropic/README.md (100%) rename {repos/effect/packages => packages}/ai/anthropic/codegen.yml (96%) rename {repos/effect/packages => packages}/ai/anthropic/package.json (98%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicClient.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicConfig.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicError.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicLanguageModel.ts (98%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicTelemetry.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/AnthropicTool.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/Generated.ts (99%) rename {repos/effect/packages => packages}/ai/anthropic/src/index.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/test/AnthropicClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/anthropic/test/AnthropicLanguageModel.test.ts (87%) rename {repos/effect/packages => packages}/ai/anthropic/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts (100%) create mode 100644 packages/ai/cloudflare/CHANGELOG.md rename {repos/effect/packages => packages}/ai/cloudflare/README.md (69%) rename {repos/effect/packages => packages}/ai/cloudflare/package.json (98%) rename {repos/effect/packages => packages}/ai/cloudflare/src/CloudflareClient.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/src/CloudflareDecisionModel.ts (69%) rename {repos/effect/packages => packages}/ai/cloudflare/src/CloudflareSchema.ts (62%) rename {repos/effect/packages => packages}/ai/cloudflare/src/index.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/test/CloudflareClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/cloudflare/test/CloudflareDecisionModel.test.ts (68%) rename {repos/effect/packages => packages}/ai/cloudflare/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts (59%) rename {repos/effect/packages => packages}/ai/openai-compat/CHANGELOG.md (92%) rename {repos/effect/packages => packages}/ai/openai-compat/README.md (100%) rename {repos/effect/packages => packages}/ai/openai-compat/package.json (98%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiClient.ts (98%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiConfig.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiEmbeddingModel.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiError.ts (88%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiLanguageModel.ts (78%) rename {repos/effect/packages => packages}/ai/openai-compat/src/OpenAiTelemetry.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/index.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/src/internal/errors.ts (97%) rename {repos/effect/packages => packages}/ai/openai-compat/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiClient.test.ts (99%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiLanguageModel.test.ts (78%) rename {repos/effect/packages => packages}/ai/openai-compat/test/OpenAiTelemetry.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai-compat/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/openai-compat/typetest/OpenAiConfig.tst.ts (100%) rename {repos/effect/packages => packages}/ai/openai/CHANGELOG.md (92%) rename {repos/effect/packages => packages}/ai/openai/README.md (100%) rename {repos/effect/packages => packages}/ai/openai/codegen.yaml (77%) rename {repos/effect/packages => packages}/ai/openai/package.json (98%) create mode 100644 packages/ai/openai/src/Generated.ts rename {repos/effect/packages => packages}/ai/openai/src/OpenAiClient.ts (92%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiClientGenerated.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiConfig.ts (100%) create mode 100644 packages/ai/openai/src/OpenAiDecisionModel.ts rename {repos/effect/packages => packages}/ai/openai/src/OpenAiEmbeddingModel.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiError.ts (92%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiLanguageModel.ts (97%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiSchema.ts (89%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiTelemetry.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/OpenAiTool.ts (99%) rename {repos/effect/packages => packages}/ai/openai/src/index.ts (90%) rename {repos/effect/packages => packages}/ai/openai/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/openai/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiClient.test.ts (98%) create mode 100644 packages/ai/openai/test/OpenAiDecisionModel.test.ts rename {repos/effect/packages => packages}/ai/openai/test/OpenAiEmbeddingModel.test.ts (99%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiLanguageModel.test.ts (91%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiSchema.test.ts (99%) rename {repos/effect/packages => packages}/ai/openai/test/OpenAiTelemetry.test.ts (100%) rename {repos/effect/packages => packages}/ai/openai/tsconfig.json (100%) create mode 100644 packages/ai/openai/typetest/OpenAiDecisionModel.tst.ts rename {repos/effect/packages => packages}/ai/openai/typetest/OpenAiTool.tst.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/ai/openrouter/README.md (100%) rename {repos/effect/packages => packages}/ai/openrouter/codegen.yml (100%) rename {repos/effect/packages => packages}/ai/openrouter/package.json (98%) rename {repos/effect/packages => packages}/ai/openrouter/src/Generated.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterClient.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterConfig.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterDecisionModel.ts (95%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterError.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterLanguageModel.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/OpenRouterSchema.ts (84%) rename {repos/effect/packages => packages}/ai/openrouter/src/index.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/Generated.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterDecisionModel.test.ts (95%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterLanguageModel.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/test/OpenRouterSchema.test.ts (100%) rename {repos/effect/packages => packages}/ai/openrouter/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/CHANGELOG.md (75%) rename {repos/effect/packages => packages}/ai/typesafe/README.md (73%) rename {repos/effect/packages => packages}/ai/typesafe/package.json (98%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeClient.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeConfig.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeDecisionModel.ts (94%) rename {repos/effect/packages => packages}/ai/typesafe/src/TypeSafeSchema.ts (82%) rename {repos/effect/packages => packages}/ai/typesafe/src/index.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/test/TypeSafeClient.test.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/test/TypeSafeDecisionModel.test.ts (95%) rename {repos/effect/packages => packages}/ai/typesafe/test/TypeSafeSchema.test.ts (100%) rename {repos/effect/packages => packages}/ai/typesafe/tsconfig.json (100%) rename {repos/effect/packages => packages}/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts (100%) rename {repos/effect/packages => packages}/atom/react/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/atom/react/LICENSE (100%) rename {repos/effect/packages => packages}/atom/react/README.md (100%) rename {repos/effect/packages => packages}/atom/react/package.json (97%) rename {repos/effect/packages => packages}/atom/react/src/Hooks.ts (100%) rename {repos/effect/packages => packages}/atom/react/src/ReactHydration.ts (100%) rename {repos/effect/packages => packages}/atom/react/src/RegistryContext.ts (100%) rename {repos/effect/packages => packages}/atom/react/src/ScopedAtom.ts (100%) rename {repos/effect/packages => packages}/atom/react/src/index.ts (100%) rename {repos/effect/packages => packages}/atom/react/test/index.test.tsx (100%) rename {repos/effect/packages => packages}/atom/react/tsconfig.json (100%) rename {repos/effect/packages => packages}/atom/react/vitest.setup.ts (100%) rename {repos/effect/packages => packages}/atom/solid/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/atom/solid/LICENSE (100%) rename {repos/effect/packages => packages}/atom/solid/README.md (100%) rename {repos/effect/packages => packages}/atom/solid/package.json (97%) rename {repos/effect/packages => packages}/atom/solid/src/Hooks.ts (100%) rename {repos/effect/packages => packages}/atom/solid/src/RegistryContext.ts (100%) rename {repos/effect/packages => packages}/atom/solid/src/index.ts (100%) rename {repos/effect/packages => packages}/atom/solid/test/index.test.tsx (100%) rename {repos/effect/packages => packages}/atom/solid/tsconfig.json (100%) rename {repos/effect/packages => packages}/atom/solid/vitest.setup.ts (100%) rename {repos/effect/packages => packages}/atom/vue/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/atom/vue/LICENSE (100%) rename {repos/effect/packages => packages}/atom/vue/README.md (100%) rename {repos/effect/packages => packages}/atom/vue/package.json (98%) rename {repos/effect/packages => packages}/atom/vue/src/index.ts (100%) rename {repos/effect/packages => packages}/atom/vue/test/index.test.ts (100%) rename {repos/effect/packages => packages}/atom/vue/tsconfig.json (100%) delete mode 100644 packages/cli/.gitignore delete mode 100644 packages/cli/.madgerc delete mode 100644 packages/cli/.npmignore delete mode 100644 packages/cli/CHANGELOG.md delete mode 100755 packages/cli/bin.js delete mode 100644 packages/cli/package.json delete mode 100644 packages/cli/src/argv-patch.ts delete mode 100644 packages/cli/src/extract.ts delete mode 100644 packages/cli/src/gist.ts delete mode 100644 packages/cli/src/index.ts delete mode 100644 packages/cli/src/os-command.ts delete mode 100644 packages/cli/src/shared.ts delete mode 100644 packages/cli/src/sync-effect-app-subtree.ts delete mode 100644 packages/cli/src/sync-effect-subtree.ts delete mode 100644 packages/cli/src/sync-shared.ts delete mode 100644 packages/cli/src/update-packages.ts delete mode 100644 packages/cli/test.gists.yaml delete mode 100644 packages/cli/test/argv-patch.test.ts delete mode 100644 packages/cli/test/gist-cache.test.ts delete mode 100644 packages/cli/test/update-packages.test.ts delete mode 100644 packages/cli/tsconfig.json delete mode 100644 packages/cli/vitest.config.ts delete mode 100644 packages/e2e/CHANGELOG.md delete mode 100644 packages/e2e/package.json delete mode 100644 packages/e2e/test/repoInvalidation.e2e.test.ts delete mode 100644 packages/e2e/tsconfig.json delete mode 100644 packages/e2e/vitest.config.ts delete mode 100644 packages/effect-app/.madgerc delete mode 100644 packages/effect-app/.npmignore delete mode 100644 packages/effect-app/CHANGELOG.md delete mode 100644 packages/effect-app/package.json delete mode 100644 packages/effect-app/src/Array.ts delete mode 100644 packages/effect-app/src/Chunk.ts delete mode 100644 packages/effect-app/src/Config.ts delete mode 100644 packages/effect-app/src/Config/SecretURL.ts delete mode 100644 packages/effect-app/src/Config/internal/configSecretURL.ts delete mode 100644 packages/effect-app/src/ConfigProvider.ts delete mode 100644 packages/effect-app/src/Context.ts delete mode 100644 packages/effect-app/src/DataDependencies.ts delete mode 100644 packages/effect-app/src/DateTime.ts delete mode 100644 packages/effect-app/src/Effect.ts delete mode 100644 packages/effect-app/src/Emailer.ts delete mode 100644 packages/effect-app/src/Function.ts delete mode 100644 packages/effect-app/src/Inputify.type.ts delete mode 100644 packages/effect-app/src/Layer.ts delete mode 100644 packages/effect-app/src/Model.ts delete mode 100644 packages/effect-app/src/Model/Repository.ts delete mode 100644 packages/effect-app/src/Model/Repository/Registry.ts delete mode 100644 packages/effect-app/src/Model/Repository/dependency.ts delete mode 100644 packages/effect-app/src/Model/Repository/ext.ts delete mode 100644 packages/effect-app/src/Model/Repository/internal/internal.ts delete mode 100644 packages/effect-app/src/Model/Repository/legacy.ts delete mode 100644 packages/effect-app/src/Model/Repository/makeRepo.ts delete mode 100644 packages/effect-app/src/Model/Repository/service.ts delete mode 100644 packages/effect-app/src/Model/Repository/validation.ts delete mode 100644 packages/effect-app/src/Model/dsl.ts delete mode 100644 packages/effect-app/src/Model/filter/filterApi.ts delete mode 100644 packages/effect-app/src/Model/filter/types.ts delete mode 100644 packages/effect-app/src/Model/filter/types/errors.ts delete mode 100644 packages/effect-app/src/Model/filter/types/fields.ts delete mode 100644 packages/effect-app/src/Model/filter/types/path/common.ts delete mode 100644 packages/effect-app/src/Model/filter/types/path/eager.ts delete mode 100644 packages/effect-app/src/Model/filter/types/path/index.ts delete mode 100644 packages/effect-app/src/Model/filter/types/utils.ts delete mode 100644 packages/effect-app/src/Model/filter/types/validator.ts delete mode 100644 packages/effect-app/src/Model/query.ts delete mode 100644 packages/effect-app/src/Model/query/dsl.ts delete mode 100644 packages/effect-app/src/Model/query/new-kid-interpreter.ts delete mode 100644 packages/effect-app/src/NonEmptySet.ts delete mode 100644 packages/effect-app/src/Option.ts delete mode 100644 packages/effect-app/src/Pure.ts delete mode 100644 packages/effect-app/src/QueueMaker.ts delete mode 100644 packages/effect-app/src/RequestContext.ts delete mode 100644 packages/effect-app/src/RequestScopedDependencies.ts delete mode 100644 packages/effect-app/src/Schema.ts delete mode 100644 packages/effect-app/src/Schema/Class.ts delete mode 100644 packages/effect-app/src/Schema/FastCheck.ts delete mode 100644 packages/effect-app/src/Schema/Methods.ts delete mode 100644 packages/effect-app/src/Schema/SchemaParser.ts delete mode 100644 packages/effect-app/src/Schema/SpecialJsonSchema.ts delete mode 100644 packages/effect-app/src/Schema/SpecialOpenApi.ts delete mode 100644 packages/effect-app/src/Schema/brand.ts delete mode 100644 packages/effect-app/src/Schema/email.ts delete mode 100644 packages/effect-app/src/Schema/ext.ts delete mode 100644 packages/effect-app/src/Schema/moreStrings.ts delete mode 100644 packages/effect-app/src/Schema/numbers.ts delete mode 100644 packages/effect-app/src/Schema/phoneNumber.ts delete mode 100644 packages/effect-app/src/Schema/schema.ts delete mode 100644 packages/effect-app/src/Schema/strings.ts delete mode 100644 packages/effect-app/src/SchemaAST.ts delete mode 100644 packages/effect-app/src/Set.ts delete mode 100644 packages/effect-app/src/Store.ts delete mode 100644 packages/effect-app/src/TypeTest.ts delete mode 100644 packages/effect-app/src/Types.ts delete mode 100644 packages/effect-app/src/Widen.type.ts delete mode 100644 packages/effect-app/src/_ext/Array.ts delete mode 100644 packages/effect-app/src/_ext/ord.ext.ts delete mode 100644 packages/effect-app/src/builtin-json.d.ts delete mode 100644 packages/effect-app/src/builtin.ts delete mode 100644 packages/effect-app/src/client.ts delete mode 100644 packages/effect-app/src/client/InvalidationKeys.ts delete mode 100644 packages/effect-app/src/client/apiClientFactory.ts delete mode 100644 packages/effect-app/src/client/clientFor.ts delete mode 100644 packages/effect-app/src/client/errors.ts delete mode 100644 packages/effect-app/src/client/makeClient.ts delete mode 100644 packages/effect-app/src/faker.ts delete mode 100644 packages/effect-app/src/http.ts delete mode 100644 packages/effect-app/src/http/Request.ts delete mode 100644 packages/effect-app/src/http/internal/lib.ts delete mode 100644 packages/effect-app/src/ids.ts delete mode 100644 packages/effect-app/src/index.ts delete mode 100644 packages/effect-app/src/logger.ts delete mode 100644 packages/effect-app/src/middleware.ts delete mode 100644 packages/effect-app/src/migration/nativeEffect.ts delete mode 100644 packages/effect-app/src/rpc.ts delete mode 100644 packages/effect-app/src/rpc/Invalidation.ts delete mode 100644 packages/effect-app/src/rpc/MiddlewareMaker.ts delete mode 100644 packages/effect-app/src/rpc/README.md delete mode 100644 packages/effect-app/src/rpc/RpcContextMap.ts delete mode 100644 packages/effect-app/src/rpc/RpcMiddleware.ts delete mode 100644 packages/effect-app/src/runtime.ts delete mode 100644 packages/effect-app/src/setupRequest.ts delete mode 100644 packages/effect-app/src/toast.ts delete mode 100644 packages/effect-app/src/transform.ts delete mode 100644 packages/effect-app/src/utils.ts delete mode 100644 packages/effect-app/src/utils/effectify.ts delete mode 100644 packages/effect-app/src/utils/extend.ts delete mode 100644 packages/effect-app/src/utils/gen.ts delete mode 100644 packages/effect-app/src/utils/logLevel.ts delete mode 100644 packages/effect-app/src/utils/logger.ts delete mode 100644 packages/effect-app/src/validation.ts delete mode 100644 packages/effect-app/src/validation/validators.ts delete mode 100644 packages/effect-app/src/withToast.ts delete mode 100644 packages/effect-app/test/dateTimeLocal.test.ts delete mode 100644 packages/effect-app/test/moreStrings.test.ts delete mode 100644 packages/effect-app/test/nativeEffectMigration.test.ts delete mode 100644 packages/effect-app/test/rpc.test.ts delete mode 100644 packages/effect-app/test/rpcMiddlewareContext.test.ts delete mode 100644 packages/effect-app/test/schema.test.ts delete mode 100644 packages/effect-app/test/secretURL.test.ts delete mode 100644 packages/effect-app/test/special.test.ts delete mode 100644 packages/effect-app/test/utils.test.ts delete mode 100644 packages/effect-app/tsconfig.base.json delete mode 100644 packages/effect-app/tsconfig.json delete mode 100644 packages/effect-app/tsconfig.src.json delete mode 100644 packages/effect-app/tsconfig.test.json delete mode 100644 packages/effect-app/vitest.config.ts rename {repos/effect/packages => packages}/effect/ARBITRARY.md (100%) rename {repos/effect/packages => packages}/effect/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/effect/CONFIG.md (100%) rename {repos/effect/packages => packages}/effect/HTTPAPI.md (100%) rename {repos/effect/packages => packages}/effect/LICENSE (100%) rename {repos/effect/packages => packages}/effect/MCP.md (100%) rename {repos/effect/packages => packages}/effect/OPTIC.md (100%) rename {repos/effect/packages => packages}/effect/README.md (100%) rename {repos/effect/packages => packages}/effect/SCHEMA.md (99%) rename {repos/effect/packages => packages}/effect/benchmark/Pool.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/http/multipart.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/http/serverAllocations.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/http/tracer.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/httpapi/sseClientDecoder.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/net/NetAddress.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/rpc/RpcSerialization.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/Optic.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/SchemaBinary.md (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/SchemaBinary.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/schema/SchemaError.ts (100%) rename {repos/effect/packages => packages}/effect/benchmark/stream/splitLines.ts (100%) rename {repos/effect/packages => packages}/effect/package.json (94%) rename {repos/effect/packages => packages}/effect/runtimeperf/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/compare.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/config.json (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/materialize.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/run.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/stats.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/arbitrary/fixtures/native.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/costs.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/resources.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/aot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/data.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/generate.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/jit.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/valibot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/moltar/fixtures/zod.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-checked-sources/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/README.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/RESULTS.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/TO-TYPE.md (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/fixtures.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/profile.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/run.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-construction/worker.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/adapters.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/behavior.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/cold.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/suites/schema/fixtures/comparison.ts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/materialize.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/registry.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/schema-construction.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/stats.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/worker.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/test/worktree-diff.test.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/utils.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/worker.mts (100%) rename {repos/effect/packages => packages}/effect/runtimeperf/worktree-diff.mts (100%) rename {repos/effect/packages => packages}/effect/src/Arbitrary.ts (100%) rename {repos/effect/packages => packages}/effect/src/Array.ts (99%) rename {repos/effect/packages => packages}/effect/src/BigDecimal.ts (100%) rename {repos/effect/packages => packages}/effect/src/BigInt.ts (100%) rename {repos/effect/packages => packages}/effect/src/Boolean.ts (100%) rename {repos/effect/packages => packages}/effect/src/Brand.ts (100%) rename {repos/effect/packages => packages}/effect/src/ByteSize.ts (100%) rename {repos/effect/packages => packages}/effect/src/Cache.ts (100%) rename {repos/effect/packages => packages}/effect/src/Cause.ts (100%) rename {repos/effect/packages => packages}/effect/src/Channel.ts (99%) rename {repos/effect/packages => packages}/effect/src/ChannelSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/Chunk.ts (100%) rename {repos/effect/packages => packages}/effect/src/Clock.ts (100%) rename {repos/effect/packages => packages}/effect/src/Combiner.ts (100%) rename {repos/effect/packages => packages}/effect/src/Config.ts (100%) rename {repos/effect/packages => packages}/effect/src/ConfigProvider.ts (96%) rename {repos/effect/packages => packages}/effect/src/Console.ts (100%) rename {repos/effect/packages => packages}/effect/src/Context.ts (98%) rename {repos/effect/packages => packages}/effect/src/Cron.ts (100%) rename {repos/effect/packages => packages}/effect/src/Crypto.ts (80%) rename {repos/effect/packages => packages}/effect/src/Data.ts (100%) rename {repos/effect/packages => packages}/effect/src/DateTime.ts (99%) rename {repos/effect/packages => packages}/effect/src/Deferred.ts (100%) rename {repos/effect/packages => packages}/effect/src/Differ.ts (100%) rename {repos/effect/packages => packages}/effect/src/Duration.ts (96%) rename {repos/effect/packages => packages}/effect/src/Effect.ts (99%) rename {repos/effect/packages => packages}/effect/src/Effectable.ts (100%) rename {repos/effect/packages => packages}/effect/src/Equal.ts (100%) rename {repos/effect/packages => packages}/effect/src/Equivalence.ts (100%) rename {repos/effect/packages => packages}/effect/src/ErrorReporter.ts (100%) rename {repos/effect/packages => packages}/effect/src/ExecutionPlan.ts (100%) rename {repos/effect/packages => packages}/effect/src/Exit.ts (100%) rename {repos/effect/packages => packages}/effect/src/Fiber.ts (100%) rename {repos/effect/packages => packages}/effect/src/FiberHandle.ts (100%) rename {repos/effect/packages => packages}/effect/src/FiberMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/FiberSet.ts (100%) rename {repos/effect/packages => packages}/effect/src/FileSystem.ts (91%) rename {repos/effect/packages => packages}/effect/src/Filter.ts (100%) rename {repos/effect/packages => packages}/effect/src/Formatter.ts (100%) rename {repos/effect/packages => packages}/effect/src/Function.ts (100%) rename {repos/effect/packages => packages}/effect/src/Graph.ts (100%) rename {repos/effect/packages => packages}/effect/src/HKT.ts (100%) rename {repos/effect/packages => packages}/effect/src/Hash.ts (100%) rename {repos/effect/packages => packages}/effect/src/HashMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/HashRing.ts (100%) rename {repos/effect/packages => packages}/effect/src/HashSet.ts (100%) rename {repos/effect/packages => packages}/effect/src/Inspectable.ts (100%) rename {repos/effect/packages => packages}/effect/src/Iterable.ts (100%) rename {repos/effect/packages => packages}/effect/src/JsonPatch.ts (100%) rename {repos/effect/packages => packages}/effect/src/JsonPointer.ts (100%) rename {repos/effect/packages => packages}/effect/src/JsonSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/Latch.ts (100%) rename {repos/effect/packages => packages}/effect/src/Layer.ts (100%) rename {repos/effect/packages => packages}/effect/src/LayerMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/LayerRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/LogLevel.ts (100%) rename {repos/effect/packages => packages}/effect/src/Logger.ts (100%) rename {repos/effect/packages => packages}/effect/src/ManagedRuntime.ts (100%) rename {repos/effect/packages => packages}/effect/src/Match.ts (96%) rename {repos/effect/packages => packages}/effect/src/Metric.ts (100%) rename {repos/effect/packages => packages}/effect/src/MutableHashMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/MutableHashSet.ts (100%) rename {repos/effect/packages => packages}/effect/src/MutableList.ts (100%) rename {repos/effect/packages => packages}/effect/src/MutableRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/Newtype.ts (100%) rename {repos/effect/packages => packages}/effect/src/NonEmptyIterable.ts (100%) rename {repos/effect/packages => packages}/effect/src/Number.ts (100%) rename {repos/effect/packages => packages}/effect/src/Optic.ts (100%) rename {repos/effect/packages => packages}/effect/src/Option.ts (100%) rename {repos/effect/packages => packages}/effect/src/Order.ts (100%) rename {repos/effect/packages => packages}/effect/src/Ordering.ts (100%) rename {repos/effect/packages => packages}/effect/src/PartitionedSemaphore.ts (100%) rename {repos/effect/packages => packages}/effect/src/Path.ts (92%) rename {repos/effect/packages => packages}/effect/src/Pipeable.ts (100%) rename {repos/effect/packages => packages}/effect/src/PlatformError.ts (100%) rename {repos/effect/packages => packages}/effect/src/Pool.ts (100%) rename {repos/effect/packages => packages}/effect/src/Predicate.ts (100%) rename {repos/effect/packages => packages}/effect/src/PrimaryKey.ts (100%) rename {repos/effect/packages => packages}/effect/src/PubSub.ts (100%) rename {repos/effect/packages => packages}/effect/src/Pull.ts (100%) rename {repos/effect/packages => packages}/effect/src/Queue.ts (100%) rename {repos/effect/packages => packages}/effect/src/Random.ts (100%) rename {repos/effect/packages => packages}/effect/src/RcMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/RcRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/Record.ts (100%) rename {repos/effect/packages => packages}/effect/src/Redactable.ts (100%) rename {repos/effect/packages => packages}/effect/src/Redacted.ts (100%) rename {repos/effect/packages => packages}/effect/src/Reducer.ts (100%) rename {repos/effect/packages => packages}/effect/src/Ref.ts (100%) rename {repos/effect/packages => packages}/effect/src/References.ts (100%) rename {repos/effect/packages => packages}/effect/src/RegExp.ts (100%) rename {repos/effect/packages => packages}/effect/src/Request.ts (100%) rename {repos/effect/packages => packages}/effect/src/RequestResolver.ts (99%) rename {repos/effect/packages => packages}/effect/src/Resource.ts (100%) rename {repos/effect/packages => packages}/effect/src/Result.ts (100%) rename {repos/effect/packages => packages}/effect/src/Runtime.ts (100%) rename {repos/effect/packages => packages}/effect/src/Schedule.ts (100%) rename {repos/effect/packages => packages}/effect/src/Scheduler.ts (94%) rename {repos/effect/packages => packages}/effect/src/Schema.ts (96%) rename {repos/effect/packages => packages}/effect/src/SchemaAST.ts (100%) rename {repos/effect/packages => packages}/effect/src/SchemaGetter.ts (96%) rename {repos/effect/packages => packages}/effect/src/SchemaIssue.ts (100%) rename {repos/effect/packages => packages}/effect/src/SchemaParser.ts (100%) rename {repos/effect/packages => packages}/effect/src/SchemaRepresentation.ts (100%) rename {repos/effect/packages => packages}/effect/src/SchemaTransformation.ts (100%) rename {repos/effect/packages => packages}/effect/src/Scope.ts (100%) rename {repos/effect/packages => packages}/effect/src/ScopedCache.ts (100%) rename {repos/effect/packages => packages}/effect/src/ScopedRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/Semaphore.ts (100%) rename {repos/effect/packages => packages}/effect/src/Sink.ts (97%) rename {repos/effect/packages => packages}/effect/src/StandardSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/Stdio.ts (95%) rename {repos/effect/packages => packages}/effect/src/Stream.ts (99%) rename {repos/effect/packages => packages}/effect/src/String.ts (97%) rename {repos/effect/packages => packages}/effect/src/Struct.ts (100%) rename {repos/effect/packages => packages}/effect/src/SubscriptionRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/Symbol.ts (100%) rename {repos/effect/packages => packages}/effect/src/SynchronizedRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/Take.ts (100%) rename {repos/effect/packages => packages}/effect/src/Terminal.ts (100%) rename {repos/effect/packages => packages}/effect/src/Tracer.ts (100%) rename {repos/effect/packages => packages}/effect/src/Trie.ts (100%) rename {repos/effect/packages => packages}/effect/src/Tuple.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxChunk.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxDeferred.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxHashMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxHashSet.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxPriorityQueue.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxPubSub.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxQueue.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxReentrantLock.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxRef.ts (97%) rename {repos/effect/packages => packages}/effect/src/TxSemaphore.ts (100%) rename {repos/effect/packages => packages}/effect/src/TxSubscriptionRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/Types.ts (100%) rename {repos/effect/packages => packages}/effect/src/UndefinedOr.ts (100%) rename {repos/effect/packages => packages}/effect/src/Unify.ts (100%) rename {repos/effect/packages => packages}/effect/src/Utils.ts (92%) rename {repos/effect/packages => packages}/effect/src/Version.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/AiError.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/AnthropicStructuredOutput.ts (97%) rename {repos/effect/packages => packages}/effect/src/ai/Chat.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/Decision.ts (76%) rename {repos/effect/packages => packages}/effect/src/ai/DecisionModel.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/EmbeddingModel.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/IdGenerator.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/LanguageModel.ts (92%) rename {repos/effect/packages => packages}/effect/src/ai/McpProtocol.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/McpSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/McpServer.ts (87%) rename {repos/effect/packages => packages}/effect/src/ai/Model.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/OpenAiStructuredOutput.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Prompt.ts (97%) rename {repos/effect/packages => packages}/effect/src/ai/Response.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/ResponseIdTracker.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Telemetry.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Tokenizer.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Tool.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/Toolkit.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/codec-transformer.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpCore.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol.ts (98%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2024_11_05.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2025_03_26.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2025_06_18.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2025_11_25.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocol/v2026_07_28.ts (98%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpProtocolRegistry.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpRuntime.ts (99%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2024_11_05.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2025_03_26.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2025_06_18.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2025_11_25.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpSchema/v2026_07_28.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/mcpStatefulRuntime.ts (100%) rename {repos/effect/packages => packages}/effect/src/ai/internal/structured-output.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Argument.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/CliConfig.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/CliError.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/CliOutput.ts (94%) rename {repos/effect/packages => packages}/effect/src/cli/Command.ts (99%) rename {repos/effect/packages => packages}/effect/src/cli/Completions.ts (80%) rename {repos/effect/packages => packages}/effect/src/cli/Flag.ts (96%) rename {repos/effect/packages => packages}/effect/src/cli/GlobalFlag.ts (93%) rename {repos/effect/packages => packages}/effect/src/cli/HelpDoc.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Param.ts (98%) rename {repos/effect/packages => packages}/effect/src/cli/Primitive.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/Prompt.ts (92%) rename {repos/effect/packages => packages}/effect/src/cli/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/ansi.ts (75%) rename {repos/effect/packages => packages}/effect/src/cli/internal/auto-suggest.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/command.ts (98%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/bash.ts (98%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/descriptor.ts (94%) rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/fish.ts (96%) create mode 100644 packages/effect/src/cli/internal/completions/powershell.ts rename {repos/effect/packages => packages}/effect/src/cli/internal/completions/zsh.ts (90%) rename {repos/effect/packages => packages}/effect/src/cli/internal/config.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/help.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/lexer.ts (100%) rename {repos/effect/packages => packages}/effect/src/cli/internal/parser.ts (98%) rename {repos/effect/packages => packages}/effect/src/cli/internal/wizard.ts (82%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterCron.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterError.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterMetrics.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterSchema.ts (79%) rename {repos/effect/packages => packages}/effect/src/cluster/ClusterWorkflowEngine.ts (79%) rename {repos/effect/packages => packages}/effect/src/cluster/DeliverAt.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Entity.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityId.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityProxy.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityProxyServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityResource.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/EntityType.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Envelope.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/HttpRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/K8sHttpClient.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/K8sTypes.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/MachineId.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Message.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/MessageStorage.ts (93%) rename {repos/effect/packages => packages}/effect/src/cluster/Reply.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Runner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerHealth.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/RunnerStorage.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Runners.ts (86%) rename {repos/effect/packages => packages}/effect/src/cluster/ShardId.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Sharding.ts (87%) rename {repos/effect/packages => packages}/effect/src/cluster/ShardingConfig.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/ShardingRegistrationEvent.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SingleRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Singleton.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SingletonAddress.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/Snowflake.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SocketRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/SqlMessageStorage.ts (92%) rename {repos/effect/packages => packages}/effect/src/cluster/SqlRunnerStorage.ts (80%) rename {repos/effect/packages => packages}/effect/src/cluster/TestRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/clusterAbandon.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/entityActivation.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/entityManager.ts (79%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/entityReaper.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/hash.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/interruptors.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/resourceMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/resourceRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/cluster/internal/shardLock.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevTools.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevToolsClient.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevToolsSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/DevToolsServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/devtools/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Base64.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Base64Url.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/EncodingError.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Hex.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Ini.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Ndjson.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/SchemaBinary.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Sse.ts (84%) rename {repos/effect/packages => packages}/effect/src/encoding/Toml.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/Yaml.ts (100%) rename {repos/effect/packages => packages}/effect/src/encoding/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/Event.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventGroup.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventJournal.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLog.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogEncryption.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogMessage.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogRemote.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogServerEncrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogServerUnencrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/EventLogSessionAuth.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/SqlEventJournal.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/SqlEventLogServerEncrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/SqlEventLogServerUnencrypted.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/eventlog/internal/identityRootSecretDerivation.ts (100%) create mode 100644 packages/effect/src/graphql/GraphQL.ts create mode 100644 packages/effect/src/graphql/GraphQLClient.ts create mode 100644 packages/effect/src/graphql/GraphQLClientError.ts create mode 100644 packages/effect/src/graphql/GraphQLGroup.ts create mode 100644 packages/effect/src/graphql/GraphQLMiddleware.ts create mode 100644 packages/effect/src/graphql/GraphQLProtocol.ts create mode 100644 packages/effect/src/graphql/GraphQLResolver.ts create mode 100644 packages/effect/src/graphql/index.ts rename {repos/effect/packages => packages}/effect/src/http-api/HttpApi.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiBuilder.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiClient.ts (99%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiEndpoint.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiError.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiGroup.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiMiddleware.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiScalar.ts (97%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiSecurity.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiSwagger.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/HttpApiTest.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/OpenApi.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/html.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/httpApiScalar.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/httpApiSwagger.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/mediaType.ts (100%) rename {repos/effect/packages => packages}/effect/src/http-api/internal/path.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Cookies.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Etag.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FetchHttpClient.ts (91%) rename {repos/effect/packages => packages}/effect/src/http/FindMyWay.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FindMyWay/internal/queryString.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/FindMyWay/internal/router.ts (99%) rename {repos/effect/packages => packages}/effect/src/http/Headers.ts (96%) rename {repos/effect/packages => packages}/effect/src/http/HttpBody.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpClient.ts (93%) rename {repos/effect/packages => packages}/effect/src/http/HttpClientError.ts (95%) rename {repos/effect/packages => packages}/effect/src/http/HttpClientRequest.ts (89%) rename {repos/effect/packages => packages}/effect/src/http/HttpClientResponse.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpEffect.ts (96%) rename {repos/effect/packages => packages}/effect/src/http/HttpIncomingMessage.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpMethod.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpMiddleware.ts (81%) rename {repos/effect/packages => packages}/effect/src/http/HttpPlatform.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpRouter.ts (92%) rename {repos/effect/packages => packages}/effect/src/http/HttpServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerError.ts (96%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerRequest.ts (99%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerRespondable.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpServerResponse.ts (97%) rename {repos/effect/packages => packages}/effect/src/http/HttpStaticServer.ts (97%) rename {repos/effect/packages => packages}/effect/src/http/HttpStatus.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/HttpTraceContext.ts (89%) rename {repos/effect/packages => packages}/effect/src/http/Mime.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Multipart.ts (97%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/HeadersParser.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/Search.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/contentType.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/headers.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/multipart.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/MultipartParser/internal/search.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Template.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/Url.ts (98%) rename {repos/effect/packages => packages}/effect/src/http/UrlParams.ts (60%) rename {repos/effect/packages => packages}/effect/src/http/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/compression.ts (92%) create mode 100644 packages/effect/src/http/internal/compressionBody.ts create mode 100644 packages/effect/src/http/internal/decompression.ts rename {repos/effect/packages => packages}/effect/src/http/internal/headers.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/httpBody.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/mimeTypes.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/preResponseHandler.ts (100%) rename {repos/effect/packages => packages}/effect/src/http/internal/tracing.ts (62%) create mode 100644 packages/effect/src/http/internal/urlParams.ts rename {repos/effect/packages => packages}/effect/src/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/array.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/bigDecimal.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/bigInt.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/model.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/regexp.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/runner.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/arbitrary/schema.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/array.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/core.ts (96%) rename {repos/effect/packages => packages}/effect/src/internal/count.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/dateTime.ts (98%) rename {repos/effect/packages => packages}/effect/src/internal/doNotation.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/effect.ts (99%) rename {repos/effect/packages => packages}/effect/src/internal/equal.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/errors.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/executionPlan.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/graph.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/graphCsr.ts (100%) create mode 100644 packages/effect/src/internal/graphql.ts create mode 100644 packages/effect/src/internal/graphqlHeaders.ts rename {repos/effect/packages => packages}/effect/src/internal/hash.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/hashMap.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/hashSet.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/layer.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/matcher.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/metric.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/option.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/persistence.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/random.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/rcRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/record.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/redacted.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/references.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/request.ts (86%) rename {repos/effect/packages => packages}/effect/src/internal/result.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schedule.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/annotations.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/cause.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/codegen.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/compilerRegistry.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/fromJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/fromRepresentation.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/interpreter.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/make.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/parser.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/standardSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toCodeDocument.ts (98%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toCodec.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toDifferJsonPatch.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toEncoderXml.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toEquivalence.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toFormatter.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toIso.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toJsonSchemaDocument.ts (94%) rename {repos/effect/packages => packages}/effect/src/internal/schema/toRepresentation.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/schemaError.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/stackTraceLimit.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/stream.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/tracer.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/trie.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/ulid.ts (100%) rename {repos/effect/packages => packages}/effect/src/internal/uuid.ts (100%) create mode 100644 packages/effect/src/internal/version.ts create mode 100644 packages/effect/src/net/AddressResolver.ts create mode 100644 packages/effect/src/net/Dns.ts create mode 100644 packages/effect/src/net/Host.ts rename {repos/effect/packages => packages}/effect/src/net/IpInterface.ts (65%) rename {repos/effect/packages => packages}/effect/src/net/IpNetwork.ts (71%) rename {repos/effect/packages => packages}/effect/src/net/NetAddress.ts (73%) rename {repos/effect/packages => packages}/effect/src/net/index.ts (58%) rename {repos/effect/packages => packages}/effect/src/observability/Otlp.ts (91%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpExporter.ts (91%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpLogger.ts (90%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpMetrics.ts (93%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpResource.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpSerialization.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/OtlpTracer.ts (91%) rename {repos/effect/packages => packages}/effect/src/observability/PrometheusMetrics.ts (99%) rename {repos/effect/packages => packages}/effect/src/observability/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/internal/otlpEnv.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/internal/otlpProtobuf.ts (100%) rename {repos/effect/packages => packages}/effect/src/observability/internal/protobuf.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/KeyValueStore.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/Persistable.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/PersistedCache.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/PersistedQueue.ts (92%) rename {repos/effect/packages => packages}/effect/src/persistence/Persistence.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/RateLimiter.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/Redis.ts (100%) rename {repos/effect/packages => packages}/effect/src/persistence/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/process/ChildProcess.ts (100%) rename {repos/effect/packages => packages}/effect/src/process/ChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/effect/src/process/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/AsyncResult.ts (97%) rename {repos/effect/packages => packages}/effect/src/reactivity/Atom.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomHttpApi.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomRef.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomRegistry.ts (99%) rename {repos/effect/packages => packages}/effect/src/reactivity/AtomRpc.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/Hydration.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/Reactivity.ts (100%) rename {repos/effect/packages => packages}/effect/src/reactivity/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/Rpc.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcClient.ts (95%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcClientError.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcGroup.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcMessage.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcMiddleware.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcSerialization.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcServer.ts (91%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcTest.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/RpcWorker.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/Utils.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/rpc/internal/tracing.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/Model.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaAOTCompiler.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaAOTCompiler/Build.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaCompiler.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaCompiler/runtime.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaJITCompiler.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/SchemaJITCompiler/enable.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/VariantSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/schema/index.ts (100%) create mode 100644 packages/effect/src/socket/DatagramSocket.ts rename {repos/effect/packages => packages}/effect/src/socket/Socket.ts (96%) rename {repos/effect/packages => packages}/effect/src/socket/SocketServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/socket/index.ts (73%) rename {repos/effect/packages => packages}/effect/src/sql/Migrator.ts (94%) rename {repos/effect/packages => packages}/effect/src/sql/SqlClient.ts (89%) rename {repos/effect/packages => packages}/effect/src/sql/SqlConnection.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlError.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlModel.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlResolver.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/SqlStream.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/Statement.ts (100%) rename {repos/effect/packages => packages}/effect/src/sql/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/testing/TestClock.ts (77%) rename {repos/effect/packages => packages}/effect/src/testing/TestConsole.ts (100%) create mode 100644 packages/effect/src/testing/TestCrypto.ts rename {repos/effect/packages => packages}/effect/src/testing/TestSchema.ts (100%) rename {repos/effect/packages => packages}/effect/src/testing/index.ts (80%) rename {repos/effect/packages => packages}/effect/src/workers/Transferable.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/Worker.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/WorkerError.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/WorkerRunner.ts (100%) rename {repos/effect/packages => packages}/effect/src/workers/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/Activity.ts (99%) rename {repos/effect/packages => packages}/effect/src/workflow/DurableClock.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/DurableDeferred.ts (77%) rename {repos/effect/packages => packages}/effect/src/workflow/DurableQueue.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/Workflow.ts (95%) rename {repos/effect/packages => packages}/effect/src/workflow/WorkflowEngine.ts (84%) rename {repos/effect/packages => packages}/effect/src/workflow/WorkflowProxy.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/WorkflowProxyServer.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/index.ts (100%) rename {repos/effect/packages => packages}/effect/src/workflow/internal/crypto.ts (100%) rename {repos/effect/packages => packages}/effect/test/Array.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ArrayNaNIndex.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/AtomRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/BigDecimal.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/BigInt.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Boolean.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Brand.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ByteSize.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Cache.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Cause.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/CauseMapAnnotations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Channel.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/Chunk.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Clock.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Combiner.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Config.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ConfigProvider.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/Context.test.ts (73%) rename {repos/effect/packages => packages}/effect/test/Cron.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Crypto.test.ts (81%) rename {repos/effect/packages => packages}/effect/test/Data.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/DateTime.test.ts (95%) rename {repos/effect/packages => packages}/effect/test/Deferred.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Duration.test.ts (95%) rename {repos/effect/packages => packages}/effect/test/Effect.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/EffectEager.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/EffectKeepAlive.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Effectable.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Equal.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Equivalence.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ExecutionPlan.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Exit.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Fiber.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FiberHandle.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FiberMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FiberSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FiberSetRuntimePropagation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/FileSystem.test-utils.ts (88%) rename {repos/effect/packages => packages}/effect/test/Formatter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Function.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Graph.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Hash.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HashMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HashRing.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HashSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/HttpClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Iterable.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/JsonPatch.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/JsonPointer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/JsonSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Latch.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Layer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/LayerMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/LayerRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/LogLevel.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Logger.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ManagedRuntime.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Match.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Metric.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Migrator.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/MutableHashMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/MutableHashSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/MutableList.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Newtype.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Number.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Optic.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Option.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Order.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Ordering.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/PartitionedSemaphore.test.ts (100%) create mode 100644 packages/effect/test/Path.test.ts rename {repos/effect/packages => packages}/effect/test/Pathfinding.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Pool.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Predicate.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/PubSub.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Pull.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Queue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Random.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/RcMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/RcRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Record.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Redacted.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Reducer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Ref.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Request.test.ts (100%) create mode 100644 packages/effect/test/RequestResolver.test.ts rename {repos/effect/packages => packages}/effect/test/Resource.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Result.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Schedule.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Scheduler.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Scope.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ScopedCache.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ScopedRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Semaphore.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Sink.test.ts (91%) rename {repos/effect/packages => packages}/effect/test/StackCapture.test.ts (50%) rename {repos/effect/packages => packages}/effect/test/StackTraceLimit.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Stdio.test.ts (54%) rename {repos/effect/packages => packages}/effect/test/Stream.test.ts (99%) rename {repos/effect/packages => packages}/effect/test/String.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/Struct.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/SubscriptionRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Symbol.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/SynchronizedRef.modifySomeEffect.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/SynchronizedRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TestClock.test.ts (63%) rename {repos/effect/packages => packages}/effect/test/Tracer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Trie.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Tuple.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxChunk.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxDeferred.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxHashMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxHashSet.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxPriorityQueue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxPubSub.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxQueue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxReentrantLock.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxSemaphore.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/TxSubscriptionRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/UndefinedOr.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/Version.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/AiError.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/ai/AnthropicStructuredOutput.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Chat.test.ts (88%) rename {repos/effect/packages => packages}/effect/test/ai/DecisionModel.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/ai/EmbeddingModel.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/LanguageModel.test.ts (92%) rename {repos/effect/packages => packages}/effect/test/ai/LanguageModelRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/LanguageModelTrackerLifecycle.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpSchema.test.ts (100%) create mode 100644 packages/effect/test/ai/McpServer/LayerHttpRequestScoped.test.ts rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/CompletionTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts (85%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/LoggingTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/McpConformance.ts (98%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts (99%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/PromptsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/RootsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/SamplingTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts (99%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/ToolsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/TransportsTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts (92%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpProtocol.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/McpServer.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/ProtocolAdapters.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts (68%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts (100%) create mode 100644 packages/effect/test/ai/McpServer/TestUtils/McpWorkerdHarness.ts create mode 100644 packages/effect/test/ai/McpServer/Workerd.test.ts create mode 100644 packages/effect/test/ai/McpServer/fixtures/workerd.ts rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2024_11_05.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2025_03_26.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2025_06_18.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2025_11_25.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/McpServer/v2026_07_28.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Model.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/OpenAiStructuredOutput.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Prompt.test.ts (94%) rename {repos/effect/packages => packages}/effect/test/ai/Response.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/ResponseIdTracker.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Tokenizer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/Tool.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/ToolRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/ai/utils.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.array.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.causes.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.composition.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.configuration.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/arbitrary/Arbitrary.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Ansi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Arguments.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Command.test.ts (95%) rename {repos/effect/packages => packages}/effect/test/cli/Errors.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Help.test.ts (94%) rename {repos/effect/packages => packages}/effect/test/cli/Lexer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/LogLevel.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Param.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Primitive.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/Prompt.test.ts (95%) rename {repos/effect/packages => packages}/effect/test/cli/completions/completions.test.ts (84%) rename {repos/effect/packages => packages}/effect/test/cli/completions/descriptor.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/fixtures/ComprehensiveCli.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/services/MockTerminal.ts (100%) rename {repos/effect/packages => packages}/effect/test/cli/services/TestActions.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ClusterWorkflowEngine.test.ts (78%) rename {repos/effect/packages => packages}/effect/test/cluster/Entity.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/EntityProxy.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/Envelope.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/K8sHttpClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/MessageStorage.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ResourceMap.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/ResourceRef.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/RunnerServer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/RunnerStorage.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/Runners.test.ts (81%) rename {repos/effect/packages => packages}/effect/test/cluster/Sharding.test.ts (81%) rename {repos/effect/packages => packages}/effect/test/cluster/ShardingConfig.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/cluster/TestEntity.ts (82%) rename {repos/effect/packages => packages}/effect/test/cluster/interruptors.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/devtools/DevToolsClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Base64.test.ts (69%) rename {repos/effect/packages => packages}/effect/test/encoding/Base64Url.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/EncodingError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Hex.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Ini.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Ndjson.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/SchemaBinary.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Sse.test.ts (69%) rename {repos/effect/packages => packages}/effect/test/encoding/Toml.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/encoding/Yaml.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventJournal.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLog.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogIdentityDerivation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogMessage.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogRemote.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogServerUnencrypted.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/EventLogSessionAuth.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/fixtures/migrator/0001_first.js (100%) rename {repos/effect/packages => packages}/effect/test/fixtures/text.txt (100%) create mode 100644 packages/effect/test/graphql/GraphQL.test.ts create mode 100644 packages/effect/test/graphql/GraphQLClient.makeTest.test.ts create mode 100644 packages/effect/test/graphql/GraphQLClient.pages.test.ts create mode 100644 packages/effect/test/graphql/GraphQLClient.subscriptions.test.ts create mode 100644 packages/effect/test/graphql/GraphQLClient.test.ts create mode 100644 packages/effect/test/graphql/GraphQLClientError.test.ts create mode 100644 packages/effect/test/graphql/GraphQLProtocol.sse.test.ts create mode 100644 packages/effect/test/graphql/GraphQLProtocol.test.ts create mode 100644 packages/effect/test/graphql/GraphQLProtocol.webSocket.test.ts create mode 100644 packages/effect/test/graphql/GraphQLResolver.test.ts create mode 100644 packages/effect/test/graphql/fixtures.ts rename {repos/effect/packages => packages}/effect/test/http/Cookies.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FetchHttpClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/case-insensitive.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/matching-order.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/methods.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/optional-params.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/params-collisions.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/path-params-match.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/querystring.test.ts (73%) rename {repos/effect/packages => packages}/effect/test/http/FindMyWay/regex.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Headers.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpBody.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpClient.test.ts (73%) rename {repos/effect/packages => packages}/effect/test/http/HttpClientRequest.test.ts (61%) rename {repos/effect/packages => packages}/effect/test/http/HttpCompression.test.ts (100%) create mode 100644 packages/effect/test/http/HttpDecompression.test.ts rename {repos/effect/packages => packages}/effect/test/http/HttpEffect.test.ts (86%) rename {repos/effect/packages => packages}/effect/test/http/HttpMiddleware.test.ts (94%) rename {repos/effect/packages => packages}/effect/test/http/HttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpRouter.test.ts (70%) rename {repos/effect/packages => packages}/effect/test/http/HttpServer.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerError.test.ts (89%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerRequest.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerRespondable.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/HttpServerResponse.test.ts (85%) rename {repos/effect/packages => packages}/effect/test/http/HttpStaticServer.test.ts (88%) rename {repos/effect/packages => packages}/effect/test/http/HttpStatus.test.ts (100%) create mode 100644 packages/effect/test/http/HttpTraceContext.test.ts rename {repos/effect/packages => packages}/effect/test/http/Mime.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Multipart.test.ts (93%) rename {repos/effect/packages => packages}/effect/test/http/SchemaRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Template.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/http/Url.test.ts (100%) create mode 100644 packages/effect/test/http/UrlParams.test.ts rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiBuilder.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiClient.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiDocumentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiEndpoint.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/HttpApiSecurity.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/httpapi/OpenApi.test.ts (99%) rename {repos/effect/packages => packages}/effect/test/httpapi/OpenApiRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/internal/Uuid.test.ts (100%) create mode 100644 packages/effect/test/net/AddressResolver.test.ts create mode 100644 packages/effect/test/net/Dns.test.ts create mode 100644 packages/effect/test/net/Host.test.ts rename {repos/effect/packages => packages}/effect/test/net/IpInterface.test.ts (68%) rename {repos/effect/packages => packages}/effect/test/net/IpNetwork.test.ts (88%) rename {repos/effect/packages => packages}/effect/test/net/NetAddress.test.ts (82%) create mode 100644 packages/effect/test/observability/OtlpCompression.test.ts rename {repos/effect/packages => packages}/effect/test/observability/OtlpConfig.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpEnvHeaders.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpExporter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpLogger.test.ts (88%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpMetrics.test.ts (93%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpResource.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/observability/OtlpSerialization.test.ts (100%) create mode 100644 packages/effect/test/observability/OtlpTracer.test.ts rename {repos/effect/packages => packages}/effect/test/observability/PrometheusMetrics.test.ts (94%) rename {repos/effect/packages => packages}/effect/test/persistence/KeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/KeyValueStoreTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedCache.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedCacheTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/PersistedQueue.test.ts (100%) create mode 100644 packages/effect/test/persistence/PersistedQueueSqlTest.ts rename {repos/effect/packages => packages}/effect/test/persistence/PersistedQueueTest.ts (77%) rename {repos/effect/packages => packages}/effect/test/persistence/RateLimiter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/RateLimiterTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/Redis.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/persistence/SqlCleanupTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/process/ChildProcess.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/process/ChildProcessSpawnerTest.ts (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/bash/no-permissions.sh (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/bash/parent-exits-early.sh (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/bash/spawn-children.sh (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/SHOUTING (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/integer (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/nested/config (100%) rename {repos/effect/packages => packages}/effect/test/process/fixtures/config/secret (100%) rename {repos/effect/packages => packages}/effect/test/reactivity/AsyncResult.test.ts (53%) rename {repos/effect/packages => packages}/effect/test/reactivity/Atom.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/reactivity/AtomHttpApi.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/reactivity/AtomRpc.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/rpc/Rpc.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/rpc/RpcClient.test.ts (81%) rename {repos/effect/packages => packages}/effect/test/rpc/RpcSerialization.test.ts (94%) rename {repos/effect/packages => packages}/effect/test/rpc/RpcServer.test.ts (80%) rename {repos/effect/packages => packages}/effect/test/schema/CodePoints.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/Graph.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/HMR.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/Schema.test.ts (99%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaAOTCompiler.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaAOTCompilerBuild.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaAST.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerApi.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerArray.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerConcurrency.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerConstruction.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerRegression.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaCompilerStartup.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaGetter.test.ts (91%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaIssue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompiler.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompilerCheckedSources.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompilerExcessProperties.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaJITCompilerFallback.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaParser.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/SchemaTransformation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/TemplateLiteral.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/VariantSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/HMR-Class.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/HMR-sanity-check.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot-build.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot-import-guard.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot-runner.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/aot.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/fixtures/construction.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/jsonSchemaRoundTrip.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/builtInRevivers.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJson.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJsonMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJsonSchemaDocument.test.ts (96%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromJsonSchemaMultiDocument.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/fromRepresentations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/makeCode.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/schemaToJsonSchemaDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/stringTreeRoundtrip.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toCodeDocument.annotations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toCodeDocument.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJson.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJsonMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJsonSchemaDocument.test.ts (98%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toMultiDocument.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/representation/toRepresentations.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toCodec.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toDifferJsonPatch.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toEquivalence.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toFormatter.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toIso.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toJsonSchemaDocument.test.ts (97%) rename {repos/effect/packages => packages}/effect/test/schema/toStandardJSONSchemaV1.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/toStandardSchemaV1.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/schema/v3-v4.test.ts (100%) create mode 100644 packages/effect/test/socket/DatagramSocket.test.ts rename {repos/effect/packages => packages}/effect/test/socket/Socket.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlClient.test.ts (80%) rename {repos/effect/packages => packages}/effect/test/sql/SqlError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlResolver.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/SqlSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/sql/Statement.test.ts (81%) create mode 100644 packages/effect/test/testing/TestCrypto.test.ts rename {repos/effect/packages => packages}/effect/test/testing/TestSchema.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/assert.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/chunkCoordination.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/counter.ts (100%) rename {repos/effect/packages => packages}/effect/test/utils/gc.ts (100%) rename {repos/effect/packages => packages}/effect/test/workers/Worker.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workers/WorkerError.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workflow/DurableQueue.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workflow/Workflow.test.ts (100%) rename {repos/effect/packages => packages}/effect/test/workflow/WorkflowEngine.test.ts (59%) rename {repos/effect/packages => packages}/effect/tsconfig.json (100%) rename {repos/effect/packages => packages}/effect/typeperf/README.md (100%) rename {repos/effect/packages => packages}/effect/typeperf/compare.mjs (100%) rename {repos/effect/packages => packages}/effect/typeperf/compare/httpapi.mjs (100%) rename {repos/effect/packages => packages}/effect/typeperf/config.json (91%) rename {repos/effect/packages => packages}/effect/typeperf/run.mjs (100%) create mode 100644 packages/effect/typeperf/suites/graphql-selections/baseline.ts create mode 100644 packages/effect/typeperf/suites/graphql-selections/fixtures/opaque-class-count-5.ts create mode 100644 packages/effect/typeperf/suites/graphql-selections/fixtures/struct-interface-count-5.ts create mode 100644 packages/effect/typeperf/suites/graphql-selections/thresholds.json rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/baseline.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/_endpoint-selection-types.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/_grouped-api-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/_grouped-api-types.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-endpoint-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-handle-all-two-batches-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-handlers-count-100.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/builder-raw-handlers-count-100.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-endpoint-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-group-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-groups-100x5-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-methods-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/client-top-level-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/endpoint-class-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/endpoint-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/group-endpoints-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/url-builder-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/fixtures/url-builder-top-level-count-500.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/httpapi/thresholds.json (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/baseline.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/array.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/brand.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/decode-unknown-effect.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/encode-unknown-effect.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/exit.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/non-empty-array.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/record.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-mixed.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-mutable-only.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-optional-only.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-required.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/struct-with-rest.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/tagged-union.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-codec-array-from-single.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-codec-json.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-codec-string-tree.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-encoded.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-standard-json-schema.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/to-type.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/tuple-with-rest.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/tuple.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/fixtures/union.ts (100%) rename {repos/effect/packages => packages}/effect/typeperf/suites/schema/thresholds.json (100%) rename {repos/effect/packages => packages}/effect/typetest/AiError.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Array.tst.ts (97%) rename {repos/effect/packages => packages}/effect/typetest/Brand.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ByteSize.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Channel.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ChannelSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Clock.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Config.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ConfigProvider.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Context.tst.ts (100%) create mode 100644 packages/effect/typetest/Crypto.tst.ts rename {repos/effect/packages => packages}/effect/typetest/Data.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Effect.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Effectable.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Equivalence.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Fiber.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/FileSystem.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Function.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Graph.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Iterable.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Layer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Match.tst.ts (78%) rename {repos/effect/packages => packages}/effect/typetest/MigratedSubpaths.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Newtype.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Optic.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Option.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Order.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Predicate.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Queue.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Random.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Record.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Result.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Schedule.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Scope.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Sink.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Stream.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Struct.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/SynchronizedRef.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/TestSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Tuple.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/Types.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/VariantSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/DecisionModel.tst.ts (84%) rename {repos/effect/packages => packages}/effect/typetest/ai/LanguageModel.tst.ts (85%) rename {repos/effect/packages => packages}/effect/typetest/ai/McpServer.tst.ts (67%) rename {repos/effect/packages => packages}/effect/typetest/ai/Response.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/Tool.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/ai/Toolkit.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/arbitrary/Arbitrary.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cli/Command.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cli/Param.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cli/Prompt.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cluster/EntityProxyServer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cluster/K8sHttpClient.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/cluster/Reply.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/encoding/Ndjson.tst.ts (100%) create mode 100644 packages/effect/typetest/graphql/GraphQL.tst.ts create mode 100644 packages/effect/typetest/graphql/GraphQLClient.tst.ts create mode 100644 packages/effect/typetest/graphql/GraphQLMiddleware.tst.ts create mode 100644 packages/effect/typetest/graphql/GraphQLProtocol.tst.ts create mode 100644 packages/effect/typetest/graphql/GraphQLResolver.tst.ts rename {repos/effect/packages => packages}/effect/typetest/http/HttpClient.tst.ts (83%) rename {repos/effect/packages => packages}/effect/typetest/http/HttpPlatform.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/http/HttpRouter.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/http/HttpServer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApi.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiBuilder.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiClient.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiEndpoint.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiGroup.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiMiddleware.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/HttpApiSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/httpapi/OpenApiRepresentation.tst.ts (100%) create mode 100644 packages/effect/typetest/net/AddressResolver.tst.ts create mode 100644 packages/effect/typetest/net/Dns.tst.ts rename {repos/effect/packages => packages}/effect/typetest/net/IpInterface.tst.ts (78%) rename {repos/effect/packages => packages}/effect/typetest/net/IpNetwork.tst.ts (91%) rename {repos/effect/packages => packages}/effect/typetest/net/NetAddress.tst.ts (90%) rename {repos/effect/packages => packages}/effect/typetest/reactivity/AsyncResult.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/reactivity/Atom.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/reactivity/AtomHttpApi.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/reactivity/AtomRpc.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/Rpc.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcClient.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcGroup.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcMessage.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcMiddleware.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/rpc/RpcServer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Array.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/FromJsonSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Graph.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Literals.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Record.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Schema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaAOTCompiler.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaAOTCompilerBuild.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaBuiltInRevivers.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaCompiler.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaGetter.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaJsonSchemaConsumer.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaRepresentation.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaRepresentationCompilers.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/SchemaRepresentationReviver.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Struct.tst.ts (96%) rename {repos/effect/packages => packages}/effect/typetest/schema/Tuple.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/Union.tst.ts (88%) rename {repos/effect/packages => packages}/effect/typetest/schema/api.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/options.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/schema/toIso.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/socket/Socket.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/sql/SqlSchema.tst.ts (100%) rename {repos/effect/packages => packages}/effect/typetest/workflow/Workflow.tst.ts (57%) delete mode 100644 packages/eslint-codegen-model/CHANGELOG.md delete mode 100644 packages/eslint-codegen-model/examples/model-facades.ts delete mode 100644 packages/eslint-codegen-model/examples/workflow-facades.ts delete mode 100644 packages/eslint-codegen-model/index.js delete mode 100644 packages/eslint-codegen-model/package.json delete mode 100644 packages/eslint-codegen-model/src/cli.ts delete mode 100644 packages/eslint-codegen-model/src/index.ts delete mode 100644 packages/eslint-codegen-model/src/js-yaml.d.ts delete mode 100644 packages/eslint-codegen-model/src/normalise.ts delete mode 100644 packages/eslint-codegen-model/src/oxlint/index.ts delete mode 100644 packages/eslint-codegen-model/src/oxlint/rules/codegen.ts delete mode 100644 packages/eslint-codegen-model/src/presets/barrel.ts delete mode 100644 packages/eslint-codegen-model/src/presets/index.ts delete mode 100644 packages/eslint-codegen-model/src/presets/meta.ts delete mode 100644 packages/eslint-codegen-model/src/presets/model-facade.ts delete mode 100644 packages/eslint-codegen-model/src/presets/model.ts delete mode 100644 packages/eslint-codegen-model/src/shared/codegen-block.ts delete mode 100644 packages/eslint-codegen-model/src/shared/native-type-resolver.ts delete mode 100644 packages/eslint-codegen-model/src/shared/type-resolver.ts delete mode 100644 packages/eslint-codegen-model/tsconfig.json delete mode 100644 packages/eslint-shared-config/CHANGELOG.md delete mode 100644 packages/eslint-shared-config/package.json delete mode 100644 packages/eslint-shared-config/src/dprint.jsonc delete mode 100644 packages/eslint-shared-config/src/eslint.base.config.mjs delete mode 100644 packages/eslint-shared-config/src/eslint.vue.config.mjs delete mode 100644 packages/eslint-shared-config/src/oxlintrc.json delete mode 100644 packages/eslint-shared-config/src/plugin-effect-app.mjs delete mode 100644 packages/eslint-shared-config/test/no-await-effect.test.mts delete mode 100644 packages/eslint-shared-config/vitest.config.ts delete mode 100644 packages/infra/.madgerc delete mode 100644 packages/infra/.npmignore delete mode 100644 packages/infra/CHANGELOG.md delete mode 100644 packages/infra/_check.sh delete mode 100644 packages/infra/docs/cluster-storage.md delete mode 100644 packages/infra/docs/workflow-engine.md delete mode 100644 packages/infra/examples/query.ts delete mode 100644 packages/infra/package.json delete mode 100644 packages/infra/src/CUPS.ts delete mode 100644 packages/infra/src/ClusterCosmos.ts delete mode 100644 packages/infra/src/ClusterServiceBus.ts delete mode 100644 packages/infra/src/ContextProvider.ts delete mode 100644 packages/infra/src/Emailer/Sendgrid.ts delete mode 100644 packages/infra/src/Emailer/fake.ts delete mode 100644 packages/infra/src/MainFiberSet.ts delete mode 100644 packages/infra/src/QueueMaker/errors.ts delete mode 100644 packages/infra/src/SQL.ts delete mode 100644 packages/infra/src/SQL/Model.ts delete mode 100644 packages/infra/src/Store/Cosmos.ts delete mode 100644 packages/infra/src/Store/Cosmos/query.ts delete mode 100644 packages/infra/src/Store/Disk.ts delete mode 100644 packages/infra/src/Store/Memory.ts delete mode 100644 packages/infra/src/Store/SQL.ts delete mode 100644 packages/infra/src/Store/SQL/Pg.ts delete mode 100644 packages/infra/src/Store/SQL/query.ts delete mode 100644 packages/infra/src/Store/codeFilter.ts delete mode 100644 packages/infra/src/Store/index.test.ts.bak delete mode 100644 packages/infra/src/Store/index.ts delete mode 100644 packages/infra/src/Store/jsonDocument.ts delete mode 100644 packages/infra/src/Store/utils.ts delete mode 100644 packages/infra/src/WorkflowEngineCosmos.ts delete mode 100644 packages/infra/src/WorkflowEngineSqlite.ts delete mode 100644 packages/infra/src/arbs.ts delete mode 100644 packages/infra/src/codec.ts delete mode 100644 packages/infra/src/cosmos-client.ts delete mode 100644 packages/infra/src/errorReporter.ts delete mode 100644 packages/infra/src/errors.ts delete mode 100644 packages/infra/src/fileUtil.ts delete mode 100644 packages/infra/src/index.ts delete mode 100644 packages/infra/src/internal/RequestContextMiddleware.ts delete mode 100644 packages/infra/src/internal/auth.ts delete mode 100644 packages/infra/src/internal/events.ts delete mode 100644 packages/infra/src/internal/health.ts delete mode 100644 packages/infra/src/layerUtils.ts delete mode 100644 packages/infra/src/logger.ts delete mode 100644 packages/infra/src/logger/jsonLogger.ts delete mode 100644 packages/infra/src/logger/logFmtLogger.ts delete mode 100644 packages/infra/src/logger/shared.ts delete mode 100644 packages/infra/src/middlewares.ts delete mode 100644 packages/infra/src/mongo-client.ts delete mode 100644 packages/infra/src/otel.ts delete mode 100644 packages/infra/src/rateLimit.ts delete mode 100644 packages/infra/src/redis-client.ts delete mode 100644 packages/infra/src/reportError.ts delete mode 100644 packages/infra/src/routing.ts delete mode 100644 packages/infra/src/routing/middleware.ts delete mode 100644 packages/infra/src/routing/middleware/RouterMiddleware.ts delete mode 100644 packages/infra/src/routing/middleware/middleware.ts delete mode 100644 packages/infra/src/routing/schema/jwt.ts delete mode 100644 packages/infra/src/routing/tsort.ts delete mode 100644 packages/infra/src/routing/utils.ts delete mode 100644 packages/infra/src/test.ts delete mode 100644 packages/infra/src/util.ts delete mode 100644 packages/infra/src/vitest.ts delete mode 100644 packages/infra/test/arbs-magnitude.test.ts delete mode 100644 packages/infra/test/arbs.test.ts delete mode 100644 packages/infra/test/auth.test.ts delete mode 100644 packages/infra/test/cluster-cosmos.test.ts delete mode 100644 packages/infra/test/cluster-servicebus.test.ts delete mode 100644 packages/infra/test/cluster-sqlite.test.ts delete mode 100644 packages/infra/test/contextProvider.test.ts delete mode 100644 packages/infra/test/controller.test.ts delete mode 100644 packages/infra/test/cosmos-container-throughput.test.ts delete mode 100644 packages/infra/test/cosmos-query.test.ts delete mode 100644 packages/infra/test/fixtures.ts delete mode 100644 packages/infra/test/json-lower.test.ts delete mode 100644 packages/infra/test/layerUtils.test.ts delete mode 100644 packages/infra/test/query.test.ts delete mode 100644 packages/infra/test/rawQuery.test.ts delete mode 100644 packages/infra/test/repository-ext.test.ts delete mode 100644 packages/infra/test/repository-legacy-document.test.ts delete mode 100644 packages/infra/test/requires.test.ts delete mode 100644 packages/infra/test/router-generator.test.ts delete mode 100644 packages/infra/test/routing-interruptibility.test.ts delete mode 100644 packages/infra/test/rpc-context-map-streaming.test.ts delete mode 100644 packages/infra/test/rpc-e2e-invalidation.test.ts delete mode 100644 packages/infra/test/rpc-multi-middleware.test.ts delete mode 100644 packages/infra/test/rpc-stream-fullstack.test.ts delete mode 100644 packages/infra/test/sql-store.test.ts delete mode 100644 packages/infra/test/validateSample.test.ts delete mode 100644 packages/infra/test/workflow-engine-cosmos.test.ts delete mode 100644 packages/infra/test/workflow-engine-sqlite.test.ts delete mode 100644 packages/infra/tsconfig.examples.json delete mode 100644 packages/infra/tsconfig.json delete mode 100644 packages/infra/tsconfig.src.json delete mode 100644 packages/infra/tsconfig.test.json delete mode 100644 packages/infra/types/modules.d.ts delete mode 100644 packages/infra/vitest.config.ts rename {repos/effect/packages => packages}/opentelemetry/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/opentelemetry/LICENSE (100%) rename {repos/effect/packages => packages}/opentelemetry/README.md (100%) rename {repos/effect/packages => packages}/opentelemetry/package.json (82%) rename {repos/effect/packages => packages}/opentelemetry/src/NodeSdk.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/OtelLogger.ts (95%) rename {repos/effect/packages => packages}/opentelemetry/src/OtelMetrics.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/OtelTracer.ts (95%) rename {repos/effect/packages => packages}/opentelemetry/src/Resource.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/WebSdk.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/index.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/internal/attributes.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/src/internal/metrics.ts (97%) rename {repos/effect/packages => packages}/opentelemetry/src/internal/utilities.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/test/OtelLogger.test.ts (74%) rename {repos/effect/packages => packages}/opentelemetry/test/OtelMetrics.test.ts (94%) rename {repos/effect/packages => packages}/opentelemetry/test/OtelTracer.test.ts (84%) rename {repos/effect/packages => packages}/opentelemetry/test/Resource.test.ts (100%) rename {repos/effect/packages => packages}/opentelemetry/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/browser/CHANGELOG.md (94%) rename {repos/effect/packages => packages}/platform/browser/LICENSE (100%) rename {repos/effect/packages => packages}/platform/browser/README.md (100%) rename {repos/effect/packages => packages}/platform/browser/package.json (98%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserCrypto.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserHttpClient.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserKeyValueStore.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserPersistence.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserRuntime.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserSocket.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserStream.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserWorker.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/BrowserWorkerRunner.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/Clipboard.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/Geolocation.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDb.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbDatabase.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbQueryBuilder.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbTable.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/IndexedDbVersion.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/Permissions.ts (100%) rename {repos/effect/packages => packages}/platform/browser/src/index.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserCrypto.test.ts (98%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserHttpClient.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserKeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserPersistence.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserPersistencePersistedCache.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/BrowserWorkerRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbDatabase.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbQueryBuilder.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbTable.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/IndexedDbVersion.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/Permissions.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/RpcWorker.test.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/fixtures/rpc-e2e.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/fixtures/rpc-schemas.ts (100%) rename {repos/effect/packages => packages}/platform/browser/test/fixtures/rpc-worker.ts (100%) rename {repos/effect/packages => packages}/platform/browser/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/browser/vitest.setup.ts (100%) rename {repos/effect/packages => packages}/platform/bun/CHANGELOG.md (92%) rename {repos/effect/packages => packages}/platform/bun/LICENSE (100%) rename {repos/effect/packages => packages}/platform/bun/README.md (100%) create mode 100644 packages/platform/bun/benchmark/DatagramSocket.ts rename {repos/effect/packages => packages}/platform/bun/package.json (90%) create mode 100644 packages/platform/bun/src/BunAddressResolver.ts rename {repos/effect/packages => packages}/platform/bun/src/BunChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunClusterHttp.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunClusterSocket.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunCrypto.ts (100%) create mode 100644 packages/platform/bun/src/BunDatagramSocket.ts create mode 100644 packages/platform/bun/src/BunDns.ts rename {repos/effect/packages => packages}/platform/bun/src/BunFileSystem.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpClient.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpPlatform.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpServer.ts (99%) rename {repos/effect/packages => packages}/platform/bun/src/BunHttpServerRequest.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunMultipart.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunPath.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunRedis.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunRuntime.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunServices.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunSink.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunSocket.ts (61%) rename {repos/effect/packages => packages}/platform/bun/src/BunSocketServer.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunStdio.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunStream.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunTerminal.ts (88%) rename {repos/effect/packages => packages}/platform/bun/src/BunWorker.ts (100%) rename {repos/effect/packages => packages}/platform/bun/src/BunWorkerRunner.ts (99%) rename {repos/effect/packages => packages}/platform/bun/src/index.ts (87%) create mode 100644 packages/platform/bun/test/BunAddressResolver.test.ts rename {repos/effect/packages => packages}/platform/bun/test/BunClusterHttp.test.ts (100%) create mode 100644 packages/platform/bun/test/BunDatagramSocket.test.ts create mode 100644 packages/platform/bun/test/BunDns.integration.test.ts rename {repos/effect/packages => packages}/platform/bun/test/BunHttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunHttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunHttpServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/test/BunMultipart.test.ts (100%) create mode 100644 packages/platform/bun/test/BunSocket.test.ts rename {repos/effect/packages => packages}/platform/bun/test/BunStream.test.ts (100%) rename {repos/effect/packages => packages}/platform/bun/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/deno/CHANGELOG.md (86%) rename {repos/effect/packages => packages}/platform/deno/LICENSE (100%) rename {repos/effect/packages => packages}/platform/deno/README.md (100%) create mode 100644 packages/platform/deno/benchmark/DatagramSocket.ts rename {repos/effect/packages => packages}/platform/deno/package.json (90%) create mode 100644 packages/platform/deno/src/DenoAddressResolver.ts rename {repos/effect/packages => packages}/platform/deno/src/DenoChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoClusterHttp.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoClusterSocket.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoCrypto.ts (100%) create mode 100644 packages/platform/deno/src/DenoDatagramSocket.ts create mode 100644 packages/platform/deno/src/DenoDns.ts rename {repos/effect/packages => packages}/platform/deno/src/DenoFileSystem.ts (82%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpClient.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpPlatform.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpServer.ts (99%) rename {repos/effect/packages => packages}/platform/deno/src/DenoHttpServerRequest.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoKeyValueStore.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoMultipart.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoPath.ts (94%) rename {repos/effect/packages => packages}/platform/deno/src/DenoRedis.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoRuntime.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoServices.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoSocket.ts (98%) rename {repos/effect/packages => packages}/platform/deno/src/DenoSocketServer.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoStdio.ts (96%) rename {repos/effect/packages => packages}/platform/deno/src/DenoTerminal.ts (88%) rename {repos/effect/packages => packages}/platform/deno/src/DenoWorker.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/DenoWorkerRunner.ts (100%) rename {repos/effect/packages => packages}/platform/deno/src/index.ts (87%) rename {repos/effect/packages => packages}/platform/deno/src/internal/error.ts (100%) create mode 100644 packages/platform/deno/test/DenoAddressResolver.test.ts rename {repos/effect/packages => packages}/platform/deno/test/DenoChildProcessSpawner.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoCrypto.test.ts (92%) create mode 100644 packages/platform/deno/test/DenoDatagramSocket.test.ts create mode 100644 packages/platform/deno/test/DenoDatagramSocketUnsupported.test.ts create mode 100644 packages/platform/deno/test/DenoDns.integration.test.ts create mode 100644 packages/platform/deno/test/DenoFileSystem.test.ts rename {repos/effect/packages => packages}/platform/deno/test/DenoHttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoHttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoHttpServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoKeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoPath.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoRedis.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoSocket.test.ts (86%) rename {repos/effect/packages => packages}/platform/deno/test/DenoSocketServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoStdio.test.ts (75%) rename {repos/effect/packages => packages}/platform/deno/test/DenoTerminal.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/DenoWorkerRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/RpcWorker.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/cluster/SocketRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/deno-terminal.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/rpc-e2e.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/rpc-schemas.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/rpc-worker.ts (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/text.txt (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/tls/ca.pem (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/tls/cert.pem (100%) rename {repos/effect/packages => packages}/platform/deno/test/fixtures/tls/key.pem (100%) rename {repos/effect/packages => packages}/platform/deno/test/internal/error.test.ts (100%) rename {repos/effect/packages => packages}/platform/deno/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/node-shared/CHANGELOG.md (91%) rename {repos/effect/packages => packages}/platform/node-shared/LICENSE (100%) rename {repos/effect/packages => packages}/platform/node-shared/README.md (100%) create mode 100644 packages/platform/node-shared/benchmark/DatagramSocket.ts rename {repos/effect/packages => packages}/platform/node-shared/benchmark/Socket.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/package.json (91%) create mode 100644 packages/platform/node-shared/src/NodeAddressResolver.ts rename {repos/effect/packages => packages}/platform/node-shared/src/NodeChildProcessSpawner.ts (96%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeClusterSocket.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeCrypto.ts (100%) create mode 100644 packages/platform/node-shared/src/NodeDatagramSocket.ts create mode 100644 packages/platform/node-shared/src/NodeDns.ts rename {repos/effect/packages => packages}/platform/node-shared/src/NodeFileSystem.ts (86%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeHttpCompression.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodePath.ts (92%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeRuntime.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeSink.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeSocket.ts (99%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeSocketServer.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeStdio.ts (96%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeStream.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/NodeTerminal.ts (94%) rename {repos/effect/packages => packages}/platform/node-shared/src/index.ts (81%) rename {repos/effect/packages => packages}/platform/node-shared/src/internal/nodeChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/src/internal/utils.ts (100%) create mode 100644 packages/platform/node-shared/test/AddressResolver.test-utils.ts create mode 100644 packages/platform/node-shared/test/Dns.test-utils.ts rename {repos/effect/packages => packages}/platform/node-shared/test/HttpPlatform.test-utils.ts (97%) create mode 100644 packages/platform/node-shared/test/NodeAddressResolver.test.ts rename {repos/effect/packages => packages}/platform/node-shared/test/NodeChildProcessSpawner.test.ts (81%) create mode 100644 packages/platform/node-shared/test/NodeDatagramSocket.test.ts create mode 100644 packages/platform/node-shared/test/NodeDns.integration.test.ts rename {repos/effect/packages => packages}/platform/node-shared/test/NodeFileSystem.test.ts (75%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodePath.test.ts (57%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeSink.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeStdio.test.ts (71%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeStream.test.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/NodeTerminal.test.ts (55%) create mode 100644 packages/platform/node-shared/test/fixtures/buffered-input.ts rename {repos/effect/packages => packages}/platform/node-shared/test/fixtures/helloworld.tar.gz (100%) rename {repos/effect/packages => packages}/platform/node-shared/test/fixtures/node-terminal.ts (81%) rename {repos/effect/packages => packages}/platform/node-shared/test/fixtures/process-group.ts (100%) rename {repos/effect/packages => packages}/platform/node-shared/tsconfig.json (100%) rename {repos/effect/packages => packages}/platform/node/CHANGELOG.md (92%) rename {repos/effect/packages => packages}/platform/node/LICENSE (100%) rename {repos/effect/packages => packages}/platform/node/README.md (100%) rename {repos/effect/packages => packages}/platform/node/package.json (94%) create mode 100644 packages/platform/node/src/NodeAddressResolver.ts rename {repos/effect/packages => packages}/platform/node/src/NodeChildProcessSpawner.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeClusterHttp.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeClusterSocket.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeCrypto.ts (100%) create mode 100644 packages/platform/node/src/NodeDatagramSocket.ts create mode 100644 packages/platform/node/src/NodeDns.ts rename {repos/effect/packages => packages}/platform/node/src/NodeFileSystem.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpClient.ts (81%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpIncomingMessage.ts (96%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpPlatform.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpServer.ts (99%) rename {repos/effect/packages => packages}/platform/node/src/NodeHttpServerRequest.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeMultipart.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeMultipartParser.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodePath.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeRedis.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeRuntime.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeServices.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeSink.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeSocket.ts (80%) rename {repos/effect/packages => packages}/platform/node/src/NodeSocketServer.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeStdio.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeStream.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeTerminal.ts (87%) rename {repos/effect/packages => packages}/platform/node/src/NodeWorker.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/NodeWorkerRunner.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/Undici.ts (100%) rename {repos/effect/packages => packages}/platform/node/src/index.ts (88%) create mode 100644 packages/platform/node/test/GraphQLProtocol.test.ts rename {repos/effect/packages => packages}/platform/node/test/HttpApi.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/HttpStaticServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/HttpStaticServerConditional.test.ts (99%) rename {repos/effect/packages => packages}/platform/node/test/KeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/MultipartParser.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeCrypto.test.ts (94%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpClient.test.ts (93%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpCompression.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpPlatform.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeHttpServer.test.ts (95%) rename {repos/effect/packages => packages}/platform/node/test/NodeRedis.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/NodeSocket.test.ts (91%) rename {repos/effect/packages => packages}/platform/node/test/NodeWorkerRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/OpenApi.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/RpcServer.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/__snapshots__/HttpApi.test.ts.snap (97%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/ClusterCron.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Entity.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Locks.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Persistence.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/README.md (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Residency.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Smoke.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/StreamDisconnect.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Transport.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/Workflow.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/globalSetup.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster-integration/harness.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/MessageStorageTest.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/SocketRunner.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/SqlMessageStorage.integration.test.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/cluster/SqlRunnerStorage.integration.test.ts (81%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server-outside.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/conditional.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/custom/home.html (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/file.binx (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/guide/index.html (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/hello.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/index.html (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/http-static-server/range.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/mysql2-utils.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/pg-utils.ts (70%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/rpc-e2e.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/rpc-schemas.ts (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/text.txt (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/tls/cert.pem (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/tls/key.pem (100%) rename {repos/effect/packages => packages}/platform/node/test/fixtures/worker-runner.ts (100%) rename {repos/effect/packages => packages}/platform/node/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/clickhouse/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/clickhouse/LICENSE (100%) rename {repos/effect/packages => packages}/sql/clickhouse/README.md (100%) rename {repos/effect/packages => packages}/sql/clickhouse/package.json (96%) rename {repos/effect/packages => packages}/sql/clickhouse/src/ClickhouseClient.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/src/ClickhouseMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/clickhouse/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/d1/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/d1/LICENSE (100%) rename {repos/effect/packages => packages}/sql/d1/README.md (100%) rename {repos/effect/packages => packages}/sql/d1/package.json (95%) rename {repos/effect/packages => packages}/sql/d1/src/D1Client.ts (100%) rename {repos/effect/packages => packages}/sql/d1/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/d1/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/d1/test/Resolver.test.ts (100%) rename {repos/effect/packages => packages}/sql/d1/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/d1/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/libsql/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/libsql/LICENSE (100%) rename {repos/effect/packages => packages}/sql/libsql/README.md (100%) rename {repos/effect/packages => packages}/sql/libsql/package.json (98%) rename {repos/effect/packages => packages}/sql/libsql/src/LibsqlClient.ts (97%) rename {repos/effect/packages => packages}/sql/libsql/src/LibsqlMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/test/Client.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/test/Resolver.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/test/util.ts (100%) rename {repos/effect/packages => packages}/sql/libsql/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/mssql/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/mssql/LICENSE (100%) rename {repos/effect/packages => packages}/sql/mssql/README.md (100%) rename {repos/effect/packages => packages}/sql/mssql/package.json (97%) rename {repos/effect/packages => packages}/sql/mssql/src/MssqlClient.ts (99%) rename {repos/effect/packages => packages}/sql/mssql/src/MssqlMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/src/Parameter.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/src/Procedure.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/Binary.test.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/Client.test.ts (93%) rename {repos/effect/packages => packages}/sql/mssql/test/Persistence.integration.test.ts (81%) rename {repos/effect/packages => packages}/sql/mssql/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/mssql/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/mysql2/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/mysql2/LICENSE (100%) rename {repos/effect/packages => packages}/sql/mysql2/README.md (100%) rename {repos/effect/packages => packages}/sql/mysql2/package.json (98%) rename {repos/effect/packages => packages}/sql/mysql2/src/MysqlClient.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/src/MysqlMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/Client.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/KeyValueStore.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/Model.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/MysqlClient.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/Persistence.integration.test.ts (96%) rename {repos/effect/packages => packages}/sql/mysql2/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/SqlEventLogServerUnencrypted.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/test/utils.ts (100%) rename {repos/effect/packages => packages}/sql/mysql2/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/pg/CHANGELOG.md (96%) rename {repos/effect/packages => packages}/sql/pg/LICENSE (100%) rename {repos/effect/packages => packages}/sql/pg/README.md (100%) rename {repos/effect/packages => packages}/sql/pg/benchmark/PgClient.ts (100%) rename {repos/effect/packages => packages}/sql/pg/benchmark/PgCodec.ts (100%) rename {repos/effect/packages => packages}/sql/pg/benchmark/README.md (100%) rename {repos/effect/packages => packages}/sql/pg/package.json (93%) rename {repos/effect/packages => packages}/sql/pg/src/PgAuth.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/PgClient.ts (99%) rename {repos/effect/packages => packages}/sql/pg/src/PgConnection.ts (95%) rename {repos/effect/packages => packages}/sql/pg/src/PgMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/PgPool.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/PgProtocol.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/PgTypes.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/internal/connection.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/internal/password.ts (100%) rename {repos/effect/packages => packages}/sql/pg/src/internal/sqlError.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/Client.integration.test.ts (96%) rename {repos/effect/packages => packages}/sql/pg/test/KeyValueStore.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/Persistence.integration.test.ts (98%) rename {repos/effect/packages => packages}/sql/pg/test/PgAuth.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgConnection.in-process.test.ts (89%) rename {repos/effect/packages => packages}/sql/pg/test/PgConnection.integration.test.ts (87%) rename {repos/effect/packages => packages}/sql/pg/test/PgConnection.test.ts (88%) create mode 100644 packages/sql/pg/test/PgMigrator.integration.test.ts rename {repos/effect/packages => packages}/sql/pg/test/PgMigrator.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgPool.in-process.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgPool.integration.test.ts (92%) rename {repos/effect/packages => packages}/sql/pg/test/PgProtocol.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/PgTypes.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/SqlEventJournal.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/SqlEventLogServerUnencrypted.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/StartupParameters.integration.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/TransactionAcquire.test.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/fixtures/goldens.ts (100%) rename {repos/effect/packages => packages}/sql/pg/test/fixtures/regenerate.ts (100%) create mode 100644 packages/sql/pg/test/utils.ts rename {repos/effect/packages => packages}/sql/pg/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/pglite/CHANGELOG.md (91%) rename {repos/effect/packages => packages}/sql/pglite/LICENSE (100%) rename {repos/effect/packages => packages}/sql/pglite/README.md (100%) rename {repos/effect/packages => packages}/sql/pglite/package.json (98%) rename {repos/effect/packages => packages}/sql/pglite/src/PgliteClient.ts (96%) rename {repos/effect/packages => packages}/sql/pglite/src/PgliteMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Json.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Migrator.test.ts (73%) rename {repos/effect/packages => packages}/sql/pglite/test/PersistedQueue.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/SqlErrorClassification.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/test/Transaction.test.ts (100%) rename {repos/effect/packages => packages}/sql/pglite/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/sqlite-bun/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/package.json (98%) rename {repos/effect/packages => packages}/sql/sqlite-bun/src/SqliteClient.ts (97%) rename {repos/effect/packages => packages}/sql/sqlite-bun/src/SqliteMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-bun/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/sqlite-do/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/package.json (95%) rename {repos/effect/packages => packages}/sql/sqlite-do/src/SqliteClient.ts (95%) rename {repos/effect/packages => packages}/sql/sqlite-do/src/SqliteMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/test/Client.test.ts (91%) rename {repos/effect/packages => packages}/sql/sqlite-do/test/Miniflare.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/test/fixtures/transaction.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-do/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/CHANGELOG.md (96%) rename {repos/effect/packages => packages}/sql/sqlite-node/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/package.json (96%) rename {repos/effect/packages => packages}/sql/sqlite-node/src/SqliteClient.ts (92%) rename {repos/effect/packages => packages}/sql/sqlite-node/src/SqliteMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/Client.test.ts (88%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/KeyValueStore.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/Persistence.test.ts (98%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/Resolver.test.ts (97%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlEventJournal.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlEventLogServerEncrypted.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlEventLogServerUnencrypted.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqlModelServices.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/test/SqliteMigrator.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-node/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/package.json (98%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/src/SqliteClient.ts (99%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/src/SqliteMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-react-native/tsconfig.json (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/CHANGELOG.md (93%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/LICENSE (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/README.md (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/package.json (98%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/OpfsWorker.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/SqliteClient.ts (97%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/SqliteMigrator.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/index.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/internal/opfsWorker.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/src/sqlite-wasm.d.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/test/Client.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/test/Memory.test.ts (100%) rename {repos/effect/packages => packages}/sql/sqlite-wasm/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/codegen.schema.json (91%) rename {repos/effect/packages => packages}/tools/ai-codegen/package.json (97%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/Config.ts (98%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/Discovery.ts (100%) create mode 100644 packages/tools/ai-codegen/src/Generator.ts rename {repos/effect/packages => packages}/tools/ai-codegen/src/Glob.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/PostProcess.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/SpecFetcher.ts (98%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/src/main.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/test/Generator.test.ts (100%) create mode 100644 packages/tools/ai-codegen/test/OpenAiGeneration.test.ts rename {repos/effect/packages => packages}/tools/ai-codegen/test/OpenRouterTranscriptions.test.ts (100%) rename {repos/effect/packages => packages}/tools/ai-codegen/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/ai-docgen/package.json (97%) rename {repos/effect/packages => packages}/tools/ai-docgen/src/Glob.ts (100%) rename {repos/effect/packages => packages}/tools/ai-docgen/src/main.ts (100%) rename {repos/effect/packages => packages}/tools/ai-docgen/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/api-diff/README.md (100%) rename {repos/effect/packages => packages}/tools/api-diff/package.json (95%) rename {repos/effect/packages => packages}/tools/api-diff/src/Annotations.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/ApiDiff.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Cli.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Diff.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Discovery.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Error.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Json.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/MigrationDoc.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Model.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Report.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Snapshot.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/Worktrees.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Annotations.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Cli.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Diff.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Discovery.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Error.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/MigrationDoc.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Report.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/Snapshot.test.ts (96%) rename {repos/effect/packages => packages}/tools/api-diff/test/Worktrees.test.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/test/utils.ts (100%) rename {repos/effect/packages => packages}/tools/api-diff/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/bundle/README.md (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/arbitrary-combinators.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/basic.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/batching.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/brand.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/cache.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/config.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/differ.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/http-client.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/http-router.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/logger.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/metric.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/optic.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/pubsub.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/queue.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schedule.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-bigdecimal.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-binary.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-class.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-fromJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-representation-roundtrip.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-string-transformation.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-string.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-template-literal.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toArbitrary.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toCodeDocument.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toCodecJson.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toEquivalence.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toFormatter.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toJsonSchemaDocument.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema-toRepresentation.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/schema.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/stm.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/fixtures/stream.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/package.json (94%) rename {repos/effect/packages => packages}/tools/bundle/src/Cli.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Fixtures.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Plugins.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Reporter.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/Rollup.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/test/HttpRouterEsbuild.test.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/test/Plugins.test.ts (100%) rename {repos/effect/packages => packages}/tools/bundle/tsconfig.fixtures.json (100%) rename {repos/effect/packages => packages}/tools/bundle/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/bundle/tsconfig.src.json (100%) rename {repos/effect/packages => packages}/tools/docgen/CHANGELOG.md (89%) rename {repos/effect/packages => packages}/tools/docgen/LICENSE (100%) rename {repos/effect/packages => packages}/tools/docgen/README.md (100%) rename {repos/effect/packages => packages}/tools/docgen/package.json (96%) rename {repos/effect/packages => packages}/tools/docgen/schema.json (100%) rename {repos/effect/packages => packages}/tools/docgen/src/CLI.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Checker.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Configuration.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Core.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Domain.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Parser.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/Printer.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/index.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/src/internal/markdown-toc.d.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Checker.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Configuration.test.ts (99%) rename {repos/effect/packages => packages}/tools/docgen/test/Core.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/Parser.test.ts (100%) rename {repos/effect/packages => packages}/tools/docgen/test/fixtures/invalid-json.txt (100%) rename {repos/effect/packages => packages}/tools/docgen/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/doctest/CHANGELOG.md (89%) rename {repos/effect/packages => packages}/tools/doctest/README.md (100%) rename {repos/effect/packages => packages}/tools/doctest/package.json (93%) rename {repos/effect/packages => packages}/tools/doctest/src/Plugin.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Protocol.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Runner.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Runtime.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Source.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/Transform.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/src/index.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Plugin.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Protocol.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Runtime.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Source.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/Transform.test.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/docs/helper.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/docs/typed.md (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/docs/typed.mdx (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/src/typed.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/test/fixtures/markdown/vitest.config.ts (100%) rename {repos/effect/packages => packages}/tools/doctest/tsconfig.json (100%) create mode 100644 packages/tools/graphql-generator/CHANGELOG.md rename {repos/effect/packages/tools/openapi-generator => packages/tools/graphql-generator}/LICENSE (100%) create mode 100644 packages/tools/graphql-generator/README.md create mode 100644 packages/tools/graphql-generator/package.json create mode 100644 packages/tools/graphql-generator/src/Config.ts create mode 100644 packages/tools/graphql-generator/src/Generator.ts rename {repos/effect/packages/tools/openapi-generator => packages/tools/graphql-generator}/src/bin.ts (100%) create mode 100644 packages/tools/graphql-generator/src/internal/Ast.ts create mode 100644 packages/tools/graphql-generator/src/internal/Cli.ts create mode 100644 packages/tools/graphql-generator/src/internal/Diagnostic.ts create mode 100644 packages/tools/graphql-generator/src/internal/Emitter.ts create mode 100644 packages/tools/graphql-generator/src/internal/Generate.ts create mode 100644 packages/tools/graphql-generator/src/internal/Glob.ts create mode 100644 packages/tools/graphql-generator/src/internal/IntrospectionReader.ts create mode 100644 packages/tools/graphql-generator/src/internal/Lexer.ts create mode 100644 packages/tools/graphql-generator/src/internal/Parser.ts create mode 100644 packages/tools/graphql-generator/src/internal/Printer.ts create mode 100644 packages/tools/graphql-generator/src/internal/SchemaModel.ts create mode 100644 packages/tools/graphql-generator/src/internal/SdlReader.ts create mode 100644 packages/tools/graphql-generator/src/internal/Validate.ts create mode 100644 packages/tools/graphql-generator/src/internal/Watch.ts create mode 100644 packages/tools/graphql-generator/src/main.ts create mode 100644 packages/tools/graphql-generator/test/Config.test.ts create mode 100644 packages/tools/graphql-generator/test/Diagnostic.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.abstract.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.bindings.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.diagnostics.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.directives.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.mapping.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.naming.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.output.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.runtime.abstract.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.runtime.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.snapshots.test.ts create mode 100644 packages/tools/graphql-generator/test/Generator.template.test.ts create mode 100644 packages/tools/graphql-generator/test/Glob.test.ts create mode 100644 packages/tools/graphql-generator/test/GraphQLGeneratorCli.test.ts create mode 100644 packages/tools/graphql-generator/test/Lexical.test.ts create mode 100644 packages/tools/graphql-generator/test/Parser.executable.test.ts create mode 100644 packages/tools/graphql-generator/test/Parser.typeSystem.test.ts create mode 100644 packages/tools/graphql-generator/test/Printer.test.ts create mode 100644 packages/tools/graphql-generator/test/SchemaReaders.fixtures.test.ts create mode 100644 packages/tools/graphql-generator/test/Validate.test.ts create mode 100644 packages/tools/graphql-generator/test/Watch.test.ts create mode 100644 packages/tools/graphql-generator/test/fixtures/github/README.md create mode 100644 packages/tools/graphql-generator/test/fixtures/github/documents/fragments.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/github/documents/issues.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/github/documents/scalars.ts create mode 100644 packages/tools/graphql-generator/test/fixtures/github/documents/timeline.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/github/operations/issue-timeline.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/github/operations/search.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/github/schema.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/introspection/modern.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/introspection/modern.json create mode 100644 packages/tools/graphql-generator/test/fixtures/sdl/extensions.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/sdl/one-of.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/sdl/subscriptions.graphql create mode 100644 packages/tools/graphql-generator/test/fixtures/subscriptions/rooms.graphql create mode 100644 packages/tools/graphql-generator/test/generated/subscriptions/rooms.graphql.ts create mode 100644 packages/tools/graphql-generator/test/generated/subscriptions/shared.graphql.ts create mode 100644 packages/tools/graphql-generator/test/utils/ast.ts create mode 100644 packages/tools/graphql-generator/test/utils/generator.ts create mode 100644 packages/tools/graphql-generator/test/utils/model.ts rename {repos/effect/packages/tools/openapi-generator => packages/tools/graphql-generator}/tsconfig.json (100%) create mode 100644 packages/tools/graphql-generator/typetest/SelectionShape.tst.ts rename {repos/effect/packages => packages}/tools/jsdocs/package.json (97%) rename {repos/effect/packages => packages}/tools/jsdocs/src/Jsdocs.ts (100%) rename {repos/effect/packages => packages}/tools/jsdocs/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/jsdocs/test/jsdocs.test.ts (100%) rename {repos/effect/packages => packages}/tools/jsdocs/tsconfig.json (100%) create mode 100644 packages/tools/npm-promote/README.md create mode 100644 packages/tools/npm-promote/package.json create mode 100644 packages/tools/npm-promote/src/Cli.ts create mode 100644 packages/tools/npm-promote/src/GitHub.ts create mode 100644 packages/tools/npm-promote/src/NpmPromote.ts create mode 100644 packages/tools/npm-promote/src/NpmPromoteError.ts create mode 100644 packages/tools/npm-promote/src/Process.ts create mode 100644 packages/tools/npm-promote/src/Release.ts create mode 100644 packages/tools/npm-promote/src/TrackingIssue.ts create mode 100644 packages/tools/npm-promote/src/WebsiteDeploy.ts create mode 100644 packages/tools/npm-promote/src/Workspace.ts create mode 100644 packages/tools/npm-promote/src/bin.ts create mode 100644 packages/tools/npm-promote/test/NpmPromote.test.ts create mode 100644 packages/tools/npm-promote/test/Release.test.ts create mode 100644 packages/tools/npm-promote/test/utils.ts rename {repos/effect/packages/tools/oxc => packages/tools/npm-promote}/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/CHANGELOG.md (93%) rename {repos/effect/packages/vitest => packages/tools/openapi-generator}/LICENSE (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/README.md (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/package.json (97%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/HttpApiTransformer.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/JsonSchemaGenerator.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/OpenApiGenerator.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/OpenApiPatch.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/OpenApiTransformer.ts (99%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/ParsedOperation.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/src/Utils.ts (100%) create mode 100755 packages/tools/openapi-generator/src/bin.ts rename {repos/effect/packages => packages}/tools/openapi-generator/src/main.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/JsonSchemaGenerator.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/JsonSchemaGeneratorRepresentation.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiGenerator.test.ts (99%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiGeneratorCli.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiPatch.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/OpenApiTransformer.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/Utils.test.ts (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/cli-basic-spec.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/cli-warning-spec.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/invalid-op.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/missing-path.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/missing-value.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-add.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-multiple.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-patch.yaml (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-remove.json (100%) rename {repos/effect/packages => packages}/tools/openapi-generator/test/fixtures/patches/valid-replace.json (100%) rename {repos/effect/packages/tools/utils => packages/tools/openapi-generator}/tsconfig.json (100%) rename {repos/effect/packages => packages}/tools/oxc/oxlintrc.json (100%) rename {repos/effect/packages => packages}/tools/oxc/package.json (96%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/index.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-bigint-literals.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-import-from-barrel-package.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-js-extension-imports.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-opaque-instance-fields.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/src/oxlint/rules/no-unused-internal.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-import-from-barrel-package.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-js-extension-imports.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-opaque-instance-fields.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/no-unused-internal.test.ts (100%) rename {repos/effect/packages => packages}/tools/oxc/test/utils.ts (100%) create mode 100644 packages/tools/oxc/tsconfig.json rename {repos/effect/packages => packages}/tools/utils/package.json (97%) rename {repos/effect/packages => packages}/tools/utils/src/Codegen.ts (100%) rename {repos/effect/packages => packages}/tools/utils/src/Glob.ts (100%) rename {repos/effect/packages => packages}/tools/utils/src/bin.ts (100%) rename {repos/effect/packages => packages}/tools/utils/src/commands/codegen.ts (100%) rename {repos/effect/packages => packages}/tools/utils/test/Codegen.test.ts (100%) create mode 100644 packages/tools/utils/tsconfig.json rename {repos/effect/packages => packages}/vitest/CHANGELOG.md (94%) create mode 100644 packages/vitest/LICENSE rename {repos/effect/packages => packages}/vitest/README.md (100%) rename {repos/effect/packages => packages}/vitest/package.json (96%) rename {repos/effect/packages => packages}/vitest/src/index.ts (100%) rename {repos/effect/packages => packages}/vitest/src/internal/internal.ts (100%) rename {repos/effect/packages => packages}/vitest/src/utils.ts (100%) rename {repos/effect/packages => packages}/vitest/test/arbitrary-configuration.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/fixtures.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/index.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/isolation.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/layer-concurrency.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/nested-isolation.test.ts (100%) rename {repos/effect/packages => packages}/vitest/test/timeout.test.ts (100%) rename {repos/effect/packages => packages}/vitest/tsconfig.json (100%) rename {repos/effect/packages => packages}/vitest/typetest/index.tst.ts (100%) delete mode 100644 packages/vue-components/.prettierrc delete mode 100644 packages/vue-components/.storybook/main.ts delete mode 100644 packages/vue-components/.storybook/preview.ts delete mode 100644 packages/vue-components/.storybook/tsconfig.json delete mode 100644 packages/vue-components/.vscode/extensions.json delete mode 100644 packages/vue-components/CHANGELOG.md delete mode 100644 packages/vue-components/README.md delete mode 100644 packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts delete mode 100644 packages/vue-components/__tests__/IntegerValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/Defaults.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/Meta.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionLegacyWarning.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionNested.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionRequired.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TaggedUnionRoot.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TransformationSchema.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/TransformedStruct.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/UnregisteredFieldError.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/WithDefaultConstructorPersistency.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaForm/createUseFormWithCustomInput.test.ts delete mode 100644 packages/vue-components/__tests__/OmegaFormTest.vue delete mode 100644 packages/vue-components/__tests__/OmegaIntlProvider.vue delete mode 100644 packages/vue-components/__tests__/migration/schema-v4-migration.test.ts delete mode 100644 packages/vue-components/__tests__/setup.ts delete mode 100644 packages/vue-components/__tests__/test-array-meta.test.ts delete mode 100644 packages/vue-components/__tests__/test-nullor-duplicate.test.ts delete mode 100644 packages/vue-components/__tests__/test-union-meta.test.ts delete mode 100644 packages/vue-components/__tests__/tsconfig.json delete mode 100644 packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md delete mode 100644 packages/vue-components/docs/superpowers/specs/2026-04-27-omegaform-refactor-design.md delete mode 100644 packages/vue-components/env.d.ts delete mode 100644 packages/vue-components/eslint.config.mjs delete mode 100644 packages/vue-components/package.json delete mode 100644 packages/vue-components/src/assets/.keep delete mode 100644 packages/vue-components/src/components/CommandButton.vue delete mode 100644 packages/vue-components/src/components/Dialog.vue delete mode 100644 packages/vue-components/src/components/FixedNuxtErrorBoundary.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/InputProps.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaArray.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaAutoGen.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaErrorsInternal.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaFormInput.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaInput.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaInputVuetify.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaInternalInput.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaTaggedUnion.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaTaggedUnionInternal.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/OmegaWrapper.vue delete mode 100644 packages/vue-components/src/components/OmegaForm/blockDialog.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/createUseFormWithCustomInput.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/decimalSeparatorInput.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/errors.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/getOmegaStore.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/hocs.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/index.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/inputs.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/checks.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/createMeta.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/defaults.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/redacted.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/types.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/meta/walker.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/onMountedWithCleanup.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/persistency.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/submit.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/types.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/useOmegaForm.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/useRegisterField.ts delete mode 100644 packages/vue-components/src/components/OmegaForm/validation/localized.ts delete mode 100644 packages/vue-components/src/components/index.ts delete mode 100644 packages/vue-components/src/components/style.css delete mode 100644 packages/vue-components/src/index.ts delete mode 100644 packages/vue-components/src/reset.css delete mode 100644 packages/vue-components/src/utils/index.ts delete mode 100644 packages/vue-components/stories/Commands.stories.ts delete mode 100644 packages/vue-components/stories/Commands/One.vue delete mode 100644 packages/vue-components/stories/Commands/components.ts delete mode 100644 packages/vue-components/stories/Commands/helpers.ts delete mode 100644 packages/vue-components/stories/Commands/useEffectRouter.ts delete mode 100644 packages/vue-components/stories/Dialog.stories.ts delete mode 100644 packages/vue-components/stories/Dialog/AsyncBlocking.vue delete mode 100644 packages/vue-components/stories/Dialog/AsyncBlockingContent.vue delete mode 100644 packages/vue-components/stories/Dialog/SimpleBlocking.vue delete mode 100644 packages/vue-components/stories/Dialog/SimpleBlockingContent.vue delete mode 100644 packages/vue-components/stories/Dialog/UploadBlocking.vue delete mode 100644 packages/vue-components/stories/Dialog/UploadBlockingContent.vue delete mode 100644 packages/vue-components/stories/FixedNuxtErrorBoundary.stories.ts delete mode 100644 packages/vue-components/stories/FixedNuxtErrorBoundary/Demo.vue delete mode 100644 packages/vue-components/stories/OmegaForm.stories.ts delete mode 100644 packages/vue-components/stories/OmegaForm/Array.vue delete mode 100644 packages/vue-components/stories/OmegaForm/AutoGeneration.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Booleans.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Clearable.vue delete mode 100644 packages/vue-components/stories/OmegaForm/ComplexForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/CustomInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/CustomInputClassName.vue delete mode 100644 packages/vue-components/stories/OmegaForm/CustomLabelSlot.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Date.vue delete mode 100644 packages/vue-components/stories/OmegaForm/DecodeTransformFallback.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Defaults.vue delete mode 100644 packages/vue-components/stories/OmegaForm/DialogBlockingExamples.vue delete mode 100644 packages/vue-components/stories/OmegaForm/EmailForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/EnterSubmitRepro.vue delete mode 100644 packages/vue-components/stories/OmegaForm/FormTaggedUnion.vue delete mode 100644 packages/vue-components/stories/OmegaForm/IndexedTable.vue delete mode 100644 packages/vue-components/stories/OmegaForm/InputRegistry.vue delete mode 100644 packages/vue-components/stories/OmegaForm/IntegerValidationGerman.vue delete mode 100644 packages/vue-components/stories/OmegaForm/IntersectionExample.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Meta.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Null.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Nullable.vue delete mode 100644 packages/vue-components/stories/OmegaForm/NullableNestedStruct.vue delete mode 100644 packages/vue-components/stories/OmegaForm/NumberInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/OmitConstructorDefaults.vue delete mode 100644 packages/vue-components/stories/OmegaForm/OptionalKey.vue delete mode 100644 packages/vue-components/stories/OmegaForm/PersistencyForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/ProgrammaticallyHandleSubmitCheckErrors.vue delete mode 100644 packages/vue-components/stories/OmegaForm/RatingInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Redacted.vue delete mode 100644 packages/vue-components/stories/OmegaForm/RootLevelTaggedUnion.vue delete mode 100644 packages/vue-components/stories/OmegaForm/RootLevelTaggedUnionLegacy.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SetErrorOnSubmit.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SimpleForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SimpleFormVuetifyDefault.vue delete mode 100644 packages/vue-components/stories/OmegaForm/SumExample.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Tanstack.vue delete mode 100644 packages/vue-components/stories/OmegaForm/Union.vue delete mode 100644 packages/vue-components/stories/OmegaForm/UsingOmegaForm.vue delete mode 100644 packages/vue-components/stories/OmegaForm/WindowExitPrevention.vue delete mode 100644 packages/vue-components/stories/OmegaForm/WithDefaultConstructor.vue delete mode 100644 packages/vue-components/stories/OmegaForm/createUseFormWIthCustomInput.vue delete mode 100644 packages/vue-components/stories/OmegaForm/form.Input.vue delete mode 100644 packages/vue-components/stories/README.md delete mode 100644 packages/vue-components/stories/tsconfig.json delete mode 100644 packages/vue-components/tsconfig.build.json delete mode 100644 packages/vue-components/tsconfig.json delete mode 100644 packages/vue-components/types/vuetify-shims.d.ts delete mode 100644 packages/vue-components/vite.config.mts delete mode 100644 packages/vue-components/vitest.config.mts delete mode 100644 packages/vue/.madgerc delete mode 100644 packages/vue/.npmignore delete mode 100644 packages/vue/.prettierignore delete mode 100644 packages/vue/CHANGELOG.md delete mode 100644 packages/vue/docs/atom-query-api-redesign.md delete mode 100644 packages/vue/docs/mutation-command-atoms.md delete mode 100644 packages/vue/docs/query-key-invalidation.md delete mode 100644 packages/vue/examples/streamMutation.ts delete mode 100644 packages/vue/package.json delete mode 100644 packages/vue/src/atomQuery.ts delete mode 100644 packages/vue/src/commander.ts delete mode 100644 packages/vue/src/confirm.ts delete mode 100644 packages/vue/src/dependencyMetadata.ts delete mode 100644 packages/vue/src/errorReporter.ts delete mode 100644 packages/vue/src/form.ts delete mode 100644 packages/vue/src/index.ts delete mode 100644 packages/vue/src/internal/tanstackQuery.ts delete mode 100644 packages/vue/src/intl.ts delete mode 100644 packages/vue/src/lib.ts delete mode 100644 packages/vue/src/liveQueryInvalidation.ts delete mode 100644 packages/vue/src/makeClient.ts delete mode 100644 packages/vue/src/makeContext.ts delete mode 100644 packages/vue/src/makeIntl.ts delete mode 100644 packages/vue/src/makeUseCommand.ts delete mode 100644 packages/vue/src/mutate.ts delete mode 100644 packages/vue/src/query.ts delete mode 100644 packages/vue/src/queryLifetime.ts delete mode 100644 packages/vue/src/routeParams.ts delete mode 100644 packages/vue/src/runtime.ts delete mode 100644 packages/vue/src/suspense.ts delete mode 100644 packages/vue/src/toast.ts delete mode 100644 packages/vue/src/withToast.ts delete mode 100644 packages/vue/test/Mutation.test.ts delete mode 100644 packages/vue/test/dependencyInvalidation.test.ts delete mode 100644 packages/vue/test/form-validation-errors.test.ts delete mode 100644 packages/vue/test/form.test.ts delete mode 100644 packages/vue/test/inactiveInvalidation.test.ts delete mode 100644 packages/vue/test/interrupt-refetch-repro.test.ts delete mode 100644 packages/vue/test/lib.test.ts delete mode 100644 packages/vue/test/liveQueryInvalidation.test.ts delete mode 100644 packages/vue/test/makeClient.test.ts delete mode 100644 packages/vue/test/query-span.test.ts delete mode 100644 packages/vue/test/queryOptions.test.ts delete mode 100644 packages/vue/test/streamFinal.test.ts delete mode 100644 packages/vue/test/streamFn.test.ts delete mode 100644 packages/vue/test/stubs.ts delete mode 100644 packages/vue/test/suspense-regression.test.ts delete mode 100644 packages/vue/test/suspense.test.ts delete mode 100644 packages/vue/tsconfig.examples.json delete mode 100644 packages/vue/tsconfig.json delete mode 100644 packages/vue/tsconfig.src.json delete mode 100644 packages/vue/tsconfig.test.json delete mode 100644 packages/vue/vitest.config.ts delete mode 100644 patches/.gitkeep delete mode 100644 patches/@ben_12__eslint-plugin-dprint.patch delete mode 100644 patches/@changesets__cli@2.31.0.patch rename {repos/effect/patches => patches}/@changesets__get-github-info@1.0.1.patch (100%) delete mode 100644 patches/@sendgrid__mail.patch delete mode 100644 patches/@tanstack__query-core.patch delete mode 100644 patches/ts-plugin-sort-import-suggestions.patch delete mode 100644 patches/typescript.patch delete mode 100644 repos/effect/.changeset/config.json delete mode 100644 repos/effect/.envrc delete mode 100644 repos/effect/.github/actions/deploy-website/action.yml delete mode 100644 repos/effect/.gitignore delete mode 100644 repos/effect/.oxlintrc.json delete mode 100644 repos/effect/.vscode/extensions.json delete mode 100644 repos/effect/.vscode/settings.json delete mode 100644 repos/effect/README.md delete mode 100644 repos/effect/flake.lock delete mode 100644 repos/effect/flake.nix delete mode 100644 repos/effect/migration/annotations/effect__ExecutionPlan.yaml delete mode 100644 repos/effect/package.json delete mode 100644 repos/effect/packages/ai/cloudflare/CHANGELOG.md delete mode 100644 repos/effect/packages/ai/openai/src/Generated.ts delete mode 100644 repos/effect/packages/effect/src/internal/version.ts delete mode 100644 repos/effect/packages/effect/test/http/HttpTraceContext.test.ts delete mode 100644 repos/effect/packages/effect/test/http/UrlParams.test.ts delete mode 100644 repos/effect/packages/effect/test/observability/OtlpTracer.test.ts delete mode 100644 repos/effect/packages/platform/deno/test/DenoFileSystem.test.ts delete mode 100644 repos/effect/packages/sql/pg/test/utils.ts delete mode 100644 repos/effect/packages/tools/ai-codegen/src/Generator.ts delete mode 100644 repos/effect/pnpm-lock.yaml delete mode 100644 repos/effect/pnpm-workspace.yaml delete mode 100644 repos/effect/tsconfig.base.json delete mode 100644 repos/effect/vitest.config.ts delete mode 100644 repos/effect/vitest.setup.ts rename {repos/effect/scratchpad => scratchpad}/index.ts (100%) rename {repos/effect/scratchpad => scratchpad}/package.json (100%) rename {repos/effect/scratchpad => scratchpad}/tsconfig.json (100%) delete mode 100644 scripts/agent-gh.mjs delete mode 100644 scripts/agent-pr-ready.mjs delete mode 100644 scripts/agent-pre-push.mjs rename {repos/effect/scripts => scripts}/bundle-analyze.sh (100%) rename {repos/effect/scripts => scripts}/bundle-compare-selected.sh (100%) rename {repos/effect/scripts => scripts}/bundle-compare.sh (100%) rename {repos/effect/scripts => scripts}/check-dist-types.mjs (100%) rename {repos/effect/scripts => scripts}/circular.mjs (100%) delete mode 100644 scripts/clean-dist.sh rename {repos/effect/scripts => scripts}/clean.mjs (100%) rename {repos/effect/scripts => scripts}/codemod.mjs (100%) rename {repos/effect/scripts => scripts}/codemods/jsdoc.ts (100%) delete mode 100644 scripts/convert_patch.js rename {repos/effect/scripts => scripts}/copy-ai-docs.mjs (100%) rename {repos/effect/scripts => scripts}/effect-bot.mjs (100%) delete mode 100644 scripts/extract.sh delete mode 100755 scripts/hooks/post-checkout.test.sh delete mode 100644 scripts/install-git-hooks.mjs delete mode 100644 scripts/lib/agent-env.mjs delete mode 100644 scripts/lib/agent-gh-policy.mjs delete mode 100644 scripts/lib/agent-pr-state.mjs delete mode 100644 scripts/lib/agent-ship-gate-cache.mjs delete mode 100644 scripts/mergeTsConfig.mjs rename {repos/effect/scripts => scripts}/package-scalar.mjs (100%) rename {repos/effect/scripts => scripts}/package-swagger.mjs (100%) rename {repos/effect/scripts => scripts}/package.json (100%) delete mode 100644 scripts/rewrite-dist-declaration-imports.mjs rename {repos/effect/scripts => scripts}/set-strip-internal.mjs (100%) rename {repos/effect/scripts => scripts}/setup-agents.mjs (100%) delete mode 100644 scripts/sort.js delete mode 100644 scripts/stagePublishPackage.mjs delete mode 100644 scripts/sync-effect-subtree.js rename {repos/effect/scripts => scripts}/tsconfig.json (100%) rename {repos/effect/scripts => scripts}/version.mjs (100%) rename {repos/effect/scripts => scripts}/worktree-setup.sh (100%) delete mode 100644 t3.json delete mode 100644 tsconfig.all.json rename repos/effect/tsconfig.json => tsconfig.json (100%) delete mode 100644 tsconfig.lib.json rename repos/effect/tsconfig.packages.json => tsconfig.packages.json (94%) delete mode 100644 tsconfig.plugins.json rename repos/effect/tsconfig.tests.json => tsconfig.tests.json (95%) rename repos/effect/tstyche.json => tstyche.json (100%) delete mode 100644 vite.config.base.ts rename repos/effect/vitest.docs.ts => vitest.docs.ts (100%) delete mode 100644 wiki/Home.md delete mode 100644 wiki/ResourceClients.md delete mode 100644 wiki/bag-of-tricks.md delete mode 100644 "wiki/effect\342\200\220app\342\200\220cli.md" delete mode 100644 wiki/error-reporter-migration.md delete mode 100644 wiki/repository-derived-query-invalidation.md diff --git a/repos/effect/.agents/AGENTS.md b/.agents/AGENTS.md similarity index 100% rename from repos/effect/.agents/AGENTS.md rename to .agents/AGENTS.md diff --git a/repos/effect/.agents/skills/ai-docs/SKILL.md b/.agents/skills/ai-docs/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/ai-docs/SKILL.md rename to .agents/skills/ai-docs/SKILL.md diff --git a/repos/effect/.agents/skills/bundle-analysis/SKILL.md b/.agents/skills/bundle-analysis/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/bundle-analysis/SKILL.md rename to .agents/skills/bundle-analysis/SKILL.md diff --git a/repos/effect/.agents/skills/changesets/SKILL.md b/.agents/skills/changesets/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/changesets/SKILL.md rename to .agents/skills/changesets/SKILL.md diff --git a/repos/effect/.agents/skills/changesets/authoring.md b/.agents/skills/changesets/authoring.md similarity index 100% rename from repos/effect/.agents/skills/changesets/authoring.md rename to .agents/skills/changesets/authoring.md diff --git a/repos/effect/.agents/skills/ci-maintenance/SKILL.md b/.agents/skills/ci-maintenance/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/ci-maintenance/SKILL.md rename to .agents/skills/ci-maintenance/SKILL.md diff --git a/repos/effect/.agents/skills/ci-maintenance/privileged-workflows.md b/.agents/skills/ci-maintenance/privileged-workflows.md similarity index 100% rename from repos/effect/.agents/skills/ci-maintenance/privileged-workflows.md rename to .agents/skills/ci-maintenance/privileged-workflows.md diff --git a/repos/effect/.agents/skills/dependency-maintenance/SKILL.md b/.agents/skills/dependency-maintenance/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/dependency-maintenance/SKILL.md rename to .agents/skills/dependency-maintenance/SKILL.md diff --git a/repos/effect/.agents/skills/dependency-maintenance/coordinated-upgrades.md b/.agents/skills/dependency-maintenance/coordinated-upgrades.md similarity index 100% rename from repos/effect/.agents/skills/dependency-maintenance/coordinated-upgrades.md rename to .agents/skills/dependency-maintenance/coordinated-upgrades.md diff --git a/repos/effect/.agents/skills/dependency-maintenance/manifest-roles.md b/.agents/skills/dependency-maintenance/manifest-roles.md similarity index 100% rename from repos/effect/.agents/skills/dependency-maintenance/manifest-roles.md rename to .agents/skills/dependency-maintenance/manifest-roles.md diff --git a/repos/effect/.agents/skills/effect-development/SKILL.md b/.agents/skills/effect-development/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/effect-development/SKILL.md rename to .agents/skills/effect-development/SKILL.md diff --git a/repos/effect/.agents/skills/jsdocs/SKILL.md b/.agents/skills/jsdocs/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/SKILL.md rename to .agents/skills/jsdocs/SKILL.md diff --git a/repos/effect/.agents/skills/jsdocs/categories.md b/.agents/skills/jsdocs/categories.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/categories.md rename to .agents/skills/jsdocs/categories.md diff --git a/repos/effect/.agents/skills/jsdocs/declarations.md b/.agents/skills/jsdocs/declarations.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/declarations.md rename to .agents/skills/jsdocs/declarations.md diff --git a/repos/effect/.agents/skills/jsdocs/examples.md b/.agents/skills/jsdocs/examples.md similarity index 100% rename from repos/effect/.agents/skills/jsdocs/examples.md rename to .agents/skills/jsdocs/examples.md diff --git a/repos/effect/.agents/skills/migration-guidance/SKILL.md b/.agents/skills/migration-guidance/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/migration-guidance/SKILL.md rename to .agents/skills/migration-guidance/SKILL.md diff --git a/repos/effect/.agents/skills/migration-guidance/annotations.md b/.agents/skills/migration-guidance/annotations.md similarity index 100% rename from repos/effect/.agents/skills/migration-guidance/annotations.md rename to .agents/skills/migration-guidance/annotations.md diff --git a/repos/effect/.agents/skills/migration-guidance/generation.md b/.agents/skills/migration-guidance/generation.md similarity index 100% rename from repos/effect/.agents/skills/migration-guidance/generation.md rename to .agents/skills/migration-guidance/generation.md diff --git a/repos/effect/.agents/skills/package-development/SKILL.md b/.agents/skills/package-development/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/package-development/SKILL.md rename to .agents/skills/package-development/SKILL.md diff --git a/repos/effect/.agents/skills/package-development/dependencies.md b/.agents/skills/package-development/dependencies.md similarity index 100% rename from repos/effect/.agents/skills/package-development/dependencies.md rename to .agents/skills/package-development/dependencies.md diff --git a/repos/effect/.agents/skills/package-development/publishing.md b/.agents/skills/package-development/publishing.md similarity index 100% rename from repos/effect/.agents/skills/package-development/publishing.md rename to .agents/skills/package-development/publishing.md diff --git a/repos/effect/.agents/skills/package-development/registration.md b/.agents/skills/package-development/registration.md similarity index 100% rename from repos/effect/.agents/skills/package-development/registration.md rename to .agents/skills/package-development/registration.md diff --git a/repos/effect/.agents/skills/performance-analysis/SKILL.md b/.agents/skills/performance-analysis/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/performance-analysis/SKILL.md rename to .agents/skills/performance-analysis/SKILL.md diff --git a/repos/effect/.agents/skills/performance-analysis/runtime.md b/.agents/skills/performance-analysis/runtime.md similarity index 100% rename from repos/effect/.agents/skills/performance-analysis/runtime.md rename to .agents/skills/performance-analysis/runtime.md diff --git a/repos/effect/.agents/skills/performance-analysis/types.md b/.agents/skills/performance-analysis/types.md similarity index 100% rename from repos/effect/.agents/skills/performance-analysis/types.md rename to .agents/skills/performance-analysis/types.md diff --git a/.agents/skills/pull-requests/SKILL.md b/.agents/skills/pull-requests/SKILL.md new file mode 100644 index 000000000..5375c38f1 --- /dev/null +++ b/.agents/skills/pull-requests/SKILL.md @@ -0,0 +1,50 @@ +--- +name: pull-requests +description: Pull request finalization. Use when a change works and is about to be opened as a pull request, or when updating or continuing an existing one. +--- + +A pull request is ready when its tests, implementation, comments, and changeset +are each the simplest that cover the intended behavior. Draft PRs can be used +earlier; finishing means making the PR ready for review. + +## Workflow + +Run each step in a fresh-context subagent when available; otherwise run it +yourself. Pass along the task context and prior results so each step can check its gate. + +1. Keep tests focused. Read [test-development](../test-development/SKILL.md) for + test placement and type. Cover each intended behavior once, in the existing + test file for the module. + For changes needing no tests, record why. + Continue when removing any remaining case would leave intended behavior + uncovered, or no tests are needed. +2. Simplify the implementation. With the tests passing, look for a simpler or + more idiomatic version: reuse helpers and patterns from nearby source and + [effect-development](../effect-development/SKILL.md), and remove debugging + code, scaffolding, and changes unrelated to the task. + Continue when applicable targeted tests pass and every hunk in the diff + from the merge base is required by the task. +3. Review the full diff from the merge base as a maintainer would: correctness, + failure and interruption paths, resource cleanup, naming and API + consistency with neighboring modules, and documentation. Fix findings and + review again. + Continue when a review pass produces no findings. +4. Clean up. Remove comments that restate code or narrate the change; keep the + rest short and about why. Finalize the changeset with + [changesets](../changesets/SKILL.md). + Continue when comments explain only what code cannot, and the changeset + describes the final consumer impact or the no-changeset reason is recorded. +5. Run the validation in the [AGENTS.md validation table](../../AGENTS.md#validation) + for each change type in the diff. Fix failures caused by the change; if + fixes alter the diff, repeat the affected passes. + Continue when applicable checks pass or commands that cannot run and their + blockers are recorded. +6. Open or update the pull request. Title: a short imperative summary. + Description: the problem addressed, notable decisions, and `Closes #` for each + GitHub issue resolved. Do not narrate the diff. Mark the PR ready for review. + Continue when the final changes are pushed, the description matches the + final diff, and the PR is ready for review. + +The task is complete when each step's gate holds, validation has passed or +been reported as not runnable with reasons, and the pull request is ready for +review with a description matching the final diff. diff --git a/repos/effect/.agents/skills/test-development/SKILL.md b/.agents/skills/test-development/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/test-development/SKILL.md rename to .agents/skills/test-development/SKILL.md diff --git a/repos/effect/.agents/skills/test-development/displayed-types.md b/.agents/skills/test-development/displayed-types.md similarity index 100% rename from repos/effect/.agents/skills/test-development/displayed-types.md rename to .agents/skills/test-development/displayed-types.md diff --git a/repos/effect/.agents/skills/test-development/runtime.md b/.agents/skills/test-development/runtime.md similarity index 100% rename from repos/effect/.agents/skills/test-development/runtime.md rename to .agents/skills/test-development/runtime.md diff --git a/repos/effect/.agents/skills/test-development/types.md b/.agents/skills/test-development/types.md similarity index 100% rename from repos/effect/.agents/skills/test-development/types.md rename to .agents/skills/test-development/types.md diff --git a/repos/effect/.agents/skills/vendored-assets/SKILL.md b/.agents/skills/vendored-assets/SKILL.md similarity index 100% rename from repos/effect/.agents/skills/vendored-assets/SKILL.md rename to .agents/skills/vendored-assets/SKILL.md diff --git a/repos/effect/.agents/skills/vendored-assets/browser-assets.md b/.agents/skills/vendored-assets/browser-assets.md similarity index 100% rename from repos/effect/.agents/skills/vendored-assets/browser-assets.md rename to .agents/skills/vendored-assets/browser-assets.md diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index e5b6d8d6a..000000000 --- a/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json index f246cc8e0..b776b5a75 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,17 +1,56 @@ { - "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json", - "changelog": "@changesets/cli/changelog", + "$schema": "https://unpkg.com/@changesets/config@4.0.0-next.8/schema.json", + "changelog": ["@changesets/changelog-github", { "repo": "Effect-TS/effect" }], "commit": false, + "format": false, + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [ + "scratchpad", + "scripts" + ], + "privatePackages": false, "fixed": [ [ - "effect-app", - "@effect-app/infra", - "@effect-app/vue", - "@effect-app/vue-components" + "effect", + "@effect/ai-anthropic", + "@effect/ai-cloudflare", + "@effect/ai-openai", + "@effect/ai-typesafe", + "@effect/ai-openai-compat", + "@effect/ai-openrouter", + "@effect/atom-react", + "@effect/atom-solid", + "@effect/atom-vue", + "@effect/docgen", + "@effect/doctest", + "@effect/graphql-generator", + "@effect/openapi-generator", + "@effect/opentelemetry", + "@effect/platform-browser", + "@effect/platform-bun", + "@effect/platform-deno", + "@effect/platform-node", + "@effect/platform-node-shared", + "@effect/sql-clickhouse", + "@effect/sql-d1", + "@effect/sql-libsql", + "@effect/sql-mssql", + "@effect/sql-mysql2", + "@effect/sql-pg", + "@effect/sql-pglite", + "@effect/sql-sqlite-bun", + "@effect/sql-sqlite-do", + "@effect/sql-sqlite-node", + "@effect/sql-sqlite-react-native", + "@effect/sql-sqlite-wasm", + "@effect/vitest" ] ], - "linked": [], - "access": "public", - "baseBranch": "main", - "updateInternalDependencies": "patch" + "snapshot": { + "useCalculatedVersion": false, + "prereleaseTemplate": "{tag}-{commit}" + } } diff --git a/.changeset/update-effect-4.0.2.md b/.changeset/update-effect-4.0.2.md deleted file mode 100644 index 844a1c4ba..000000000 --- a/.changeset/update-effect-4.0.2.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"effect-app": patch -"@effect-app/cli": patch ---- - -Upgrade Effect packages to npm `4.0.2`. diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 824fd22d2..000000000 --- a/.claude/settings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(git commit -m ':*)", - "Bash(pnpm -r check)", - "Bash(pnpm lint-fix:*)", - "Bash(git commit -m ' *)", - "Read(//home/patroza/pj/effect-app/**)", - "Bash(git checkout *)", - "Bash(pnpm --filter @effect-app/vue check)", - "Bash(git -C /home/patroza/pj/effect-app/libs status)", - "Bash(git -C /home/patroza/pj/effect-app/libs diff --stat)", - "Bash(git -C /home/patroza/pj/effect-app/libs log -5 --oneline)", - "Bash(git -C /home/patroza/pj/effect-app/libs add packages/infra/src/Model/Repository/ext.ts packages/infra/src/Model/Repository/internal/internal.ts packages/infra/src/Model/Repository/service.ts .changeset/repo-mutations-accept-empty-arrays.md)", - "Bash(git -C /home/patroza/pj/effect-app/libs commit -m ' *)" - ] - } -} diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 6786e9b55..000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -# Specifies which operating system image to use. -FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu - -WORKDIR /home/vscode - -# curl and ca-certificates are needed for volta installation -RUN apt-get update \ - && apt-get install -y \ - curl \ - ca-certificates \ - --no-install-recommends - -# Changes user to vscode and the SHELL to bash -USER vscode -SHELL ["/bin/bash", "-c"] - -# since we're starting non-interactive shell, we wil need to tell bash to load .bashrc manually -ENV BASH_ENV ~/.bashrc -# needed by volta() function -ENV VOLTA_HOME /home/vscode/.volta -# make sure packages managed by volta will be in PATH -ENV PATH $VOLTA_HOME/bin:$PATH - -# download volta -RUN curl https://get.volta.sh | bash -# change the working directory to the one where the project lives -WORKDIR /workspaces/macs-configurator - -# and install node and pnpm -RUN volta install node@24 -RUN npm i -g pnpm - -RUN echo - -ENV SHELL=/bin/bash - -RUN echo - -# prevent `npm or yarn` usage in the shell now that pnpm is installed -WORKDIR /home/vscode -COPY usepnpm.sh /tmp/ -RUN cat /tmp/usepnpm.sh >> ~/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 6a267802a..000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "Effect App Libs Codespace", - "build": { - "dockerfile": "Dockerfile" - }, - // see https://containers.dev/implementors/json_reference/#remoteUser - // you can change the user name to whatever you want, this is used to avoid installing stuff in the image as root - "remoteUser": "vscode", - "postCreateCommand": "pnpm i && pnpm build", - "hostRequirements": { - "cpus": 4, - "memory": "8gb" - } -} diff --git a/.devcontainer/humanlog.sh b/.devcontainer/humanlog.sh deleted file mode 100644 index d792a7551..000000000 --- a/.devcontainer/humanlog.sh +++ /dev/null @@ -1,2 +0,0 @@ - export HUMANLOG_INSTALL="/home/vscode/.humanlog" - export PATH="$HUMANLOG_INSTALL/bin:$PATH" \ No newline at end of file diff --git a/.devcontainer/usepnpm.sh b/.devcontainer/usepnpm.sh deleted file mode 100644 index 2c34d4098..000000000 --- a/.devcontainer/usepnpm.sh +++ /dev/null @@ -1,26 +0,0 @@ -NPM_PATH=$(which npm) -npm () { - if [ -e pnpm-lock.yaml ] - then - echo "Please use PNPM with this project" - elif [ -e yarn.lock ] - then - echo "Please use Yarn with this project" - else - $NPM_PATH "$@" - fi -} - - -YARN_PATH=$(which yarn) -yarn () { - if [ -e pnpm-lock.yaml ] - then - echo "Please use PNPM with this project" - elif [ -e package-lock.json ] - then - echo "Please use NPM with this project" - else - $YARN_PATH "$@" - fi -} \ No newline at end of file diff --git a/.envrc b/.envrc index 03113d3db..3550a30f2 100644 --- a/.envrc +++ b/.envrc @@ -1,5 +1 @@ use flake - -# Repo `gh` policy shim: publishing runs the ship gate first (see AGENTS.md). -# Installed by scripts/install-git-hooks.mjs; run `direnv allow` once after cloning. -PATH_add .tools/bin diff --git a/.githooks/README.md b/.githooks/README.md deleted file mode 100644 index 147308b75..000000000 --- a/.githooks/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Native Git hooks - -`pnpm install` configures `core.hooksPath=.githooks` via -`scripts/install-git-hooks.mjs`. Tracked hooks exist in every linked worktree. - -## `post-checkout` - -### 1. Clear TypeScript project emit (always on branch / worktree checkout) - -`pnpm check` is `tsgo --build` with composite + incremental. Package outputs -(`.tsbuildinfo` + `.d.ts` / `.js`) live under `packages/*/dist` and are -gitignored. - -After a branch switch, **sources** follow HEAD but **dist** can still belong to -another tree. tsgo may treat projects as up to date and typecheck new sources -against old declarations → phantom type errors that disappear on -`pnpm rbuild` (`clean` + check). Same class of failure as -[scanner#1495](https://github.com/macs-holding/scanner/pull/1495) / -[scanner#2166](https://github.com/macs-holding/scanner/pull/2166). - -On every **branch** checkout (`post-checkout` flag `1`), this hook deletes: - -- `packages/*/dist`, `packages/*/build`, root `dist` if present -- any remaining `*.tsbuildinfo` outside `node_modules` / `repos` / `.git` - -File-only checkouts (flag `0`) are left alone. - -**This is a workaround.** Root cause belongs in tsgo incremental invalidation -(e.g. [typescript-go#2666](https://github.com/microsoft/typescript-go/issues/2666), -[#4664](https://github.com/microsoft/typescript-go/issues/4664), -[#4262](https://github.com/microsoft/typescript-go/issues/4262)). Remove when -upstream is reliable. Do not re-enable cross-tree dist reuse without an exact -source-identity key. - -### 2. Reconcile pnpm (when package/lock changed) - -Records package/lockfile state under `node_modules` so T3 handoff does not -repeat an install Git already completed. - -When T3 launches the configured worktree setup script, it sets -`T3CODE_DEFER_DEPENDENCY_INSTALL=1`. Cleanup still runs synchronously, while the -setup terminal installs dependencies asynchronously with hardlink imports from -the shared pnpm store. Raw Git worktrees still install during checkout. Their -install failure is best-effort; T3 can request the real exit status with -`T3CODE_WORKTREE_PREPARATION_STRICT=1` without removing the created worktree. - -## `pre-commit` / `pre-push` - -lint-staged on commit; agent ship gate on push (see repo AGENTS.md). diff --git a/.githooks/post-checkout b/.githooks/post-checkout deleted file mode 100755 index e938e7375..000000000 --- a/.githooks/post-checkout +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh -# 1) After a branch/worktree checkout: drop TypeScript project-reference emit so -# tsgo --build never typechecks against foreign-tree .tsbuildinfo / .d.ts. -# 2) Reconcile pnpm when package.json / lockfile changed. - -PATH="$HOME/.local/bin:$HOME/.local/share/pnpm:$HOME/.local/share/mise/shims:/run/current-system/sw/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:$PATH" -export PATH - -# post-checkout also runs after file-only checkouts; those are not a tree switch. -[ "${3:-0}" = "1" ] || exit 0 - -wt_root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 -[ -f "$wt_root/package.json" ] || exit 0 - -# --- TS build-cache wipe (every branch / worktree checkout) ----------------- -# -# Why: `pnpm check` is `tsgo --build` with composite + incremental. Package -# outputs live in packages/*/dist (gitignored). After checkout/rebase, sources -# belong to HEAD but dist may still be from another branch. tsgo can treat -# projects as up-to-date and typecheck new sources against old .d.ts → phantom -# errors that vanish on `pnpm rbuild` (clean+check). -# -# Same class as macs-holding/scanner#1495 / #2166 and upstream typescript-go -# incremental gaps (#2666, #4664, #4262, #3787). -# -# Workaround only — remove when upstream invalidation is reliable. Do not -# re-introduce foreign dist reuse without an exact source-identity key. -# -clean_ts_project_emit() { - root=$1 - removed=0 - - for d in \ - "$root"/packages/*/dist \ - "$root"/packages/*/build \ - "$root"/dist - do - [ -e "$d" ] || continue - rm -rf "$d" - removed=1 - done - - if [ -d "$root" ]; then - tsbuild_files=$( - find "$root" \ - \( -path "$root/node_modules" -o -path "$root/repos" -o -path "$root/.git" -o -path "$root/.run" -o -path "$root/build" \) -prune -o \ - -type f -name '*.tsbuildinfo' -print 2>/dev/null || true - ) - if [ -n "$tsbuild_files" ]; then - printf '%s\n' "$tsbuild_files" | while IFS= read -r f; do - [ -n "$f" ] || continue - rm -f "$f" - done - removed=1 - fi - fi - - if [ "$removed" = 1 ]; then - echo "post-checkout: cleared TypeScript project emit (dist / tsbuildinfo) under $root" >&2 - echo "post-checkout: workaround for tsgo stale incremental cache after tree switch — see .githooks/README.md (remove when upstream fixed)" >&2 - fi -} - -clean_ts_project_emit "$wt_root" - -# T3 launches the configured worktree setup command in a visible terminal. Keep -# cleanup synchronous, but leave dependency installation to that async setup. -if [ "${T3CODE_DEFER_DEPENDENCY_INSTALL:-0}" = "1" ]; then - echo "post-checkout: dependency installation deferred to T3 workspace initialization" >&2 - exit 0 -fi - -# --- pnpm install when lockfile / package.json changed ----------------------- -[ -f "$wt_root/pnpm-lock.yaml" ] || exit 0 -state_file="$wt_root/node_modules/.pnpm-checkout-state" -state=$(cksum "$wt_root/package.json" "$wt_root/pnpm-lock.yaml" | cksum | awk '{print $1 ":" $2}') -if [ -f "$wt_root/node_modules/.modules.yaml" ] && [ "$(cat "$state_file" 2>/dev/null)" = "$state" ]; then - exit 0 -fi -command -v pnpm >/dev/null 2>&1 || { - echo "post-checkout: pnpm not on PATH; node_modules not installed" >&2 - exit 1 -} - -export CI=1 -echo "post-checkout: pnpm install in $wt_root" >&2 -cd "$wt_root" || exit 1 -if pnpm install --frozen-lockfile --prefer-offline \ - --config.package-import-method=hardlink \ - --config.confirmModulesPurge=false -then - printf '%s\n' "$state" >"$state_file" -else - status=$? - echo "post-checkout: dependency preparation failed (exit $status); worktree remains usable but degraded" >&2 - [ "${T3CODE_WORKTREE_PREPARATION_STRICT:-0}" = "1" ] && exit "$status" - exit 0 -fi diff --git a/.githooks/pre-commit b/.githooks/pre-commit deleted file mode 100755 index 8fd0ddf8c..000000000 --- a/.githooks/pre-commit +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 1 -if [ ! -x "$root/node_modules/.bin/lint-staged" ]; then - echo "pre-commit: worktree preparation is incomplete (lint-staged is missing)" >&2 - echo "pre-commit: run pnpm install before committing" >&2 - exit 1 -fi -# The binary directly, not `pnpm lint-staged`: pnpm runs a dependency-status -# check first and will try to purge node_modules when the lockfile moved, which -# aborts without a TTY — the commit then fails with a pnpm error that says -# nothing about linting. The guard above already established this path exists. -exec "$root/node_modules/.bin/lint-staged" diff --git a/.githooks/pre-push b/.githooks/pre-push deleted file mode 100755 index 6ba4a4cba..000000000 --- a/.githooks/pre-push +++ /dev/null @@ -1,17 +0,0 @@ -# Agent-only ship gate (humans: no-op). See scripts/agent-pre-push.mjs. -# Every push (draft included): check + lint + test. No browser/API suite exists -# here, so there is nothing a draft could usefully defer. -# Publish: pnpm pr:ready, or raw `gh pr ready` — the shim runs the same gate first. -# -# Humans: SKIP_AGENT_PREPUSH=1 git push -# Agents: never SKIP_AGENT_PREPUSH / never --no-verify -# Nested Git commands must discover their own repositories, not inherit this -# hook's GIT_DIR/GIT_WORK_TREE from the outer push. -for name in $(git rev-parse --local-env-vars); do - unset "$name" -done -# Plain `node`, not `pnpm exec node`: pnpm runs a dependency-status check first -# and will try to purge node_modules when the lockfile moved (a rebase does it), -# which aborts without a TTY — the hook then fails with a pnpm error instead of -# anything about the gate. The gate puts node_modules/.bin on PATH itself. -node scripts/agent-pre-push.mjs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 65e15abd8..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -github: [patroza] diff --git a/repos/effect/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml similarity index 100% rename from repos/effect/.github/actions/setup/action.yaml rename to .github/actions/setup/action.yaml diff --git a/.github/npm-trusted-publishing.md b/.github/npm-trusted-publishing.md deleted file mode 100644 index 1d8ef5874..000000000 --- a/.github/npm-trusted-publishing.md +++ /dev/null @@ -1,85 +0,0 @@ -# npm Trusted Publishing (OIDC) - -Publish from GitHub Actions without a long-lived `NPM_TOKEN`. -npm mints a short-lived credential per workflow run via OIDC. - -Official docs: https://docs.npmjs.com/trusted-publishers/ - -## Prerequisites - -- You must be an **owner** (or have settings access) on each package below. -- Workflow that publishes: `.github/workflows/release.yaml` (filename is `release.yaml`). -- GitHub repo: `effect-app/libs` (public, GitHub-hosted runners). - -## Packages to configure - -Configure **each** publishable package (one trusted publisher per package): - -| Package | npm settings URL | -| --- | --- | -| `effect-app` | https://www.npmjs.com/package/effect-app/access | -| `@effect-app/infra` | https://www.npmjs.com/package/@effect-app/infra/access | -| `@effect-app/vue` | https://www.npmjs.com/package/@effect-app/vue/access | -| `@effect-app/vue-components` | https://www.npmjs.com/package/@effect-app/vue-components/access | -| `@effect-app/cli` | https://www.npmjs.com/package/@effect-app/cli/access | -| `@effect-app/eslint-codegen-model` | https://www.npmjs.com/package/@effect-app/eslint-codegen-model/access | -| `@effect-app/eslint-shared-config` | https://www.npmjs.com/package/@effect-app/eslint-shared-config/access | - -If the UI shows **Settings** / **Trusted Publisher** instead of `/access`, use that page for the same package. - -## Exact steps on npmjs.com (per package) - -Repeat for every package in the table: - -1. Open the package page → **Settings** (or the access URL above). -2. Find **Trusted Publisher**. -3. Under **Select your publisher**, choose **GitHub Actions**. -4. Fill in exactly: - - **Organization or user:** `effect-app` - - **Repository:** `libs` - - **Workflow filename:** `release.yaml` - (filename only, including `.yaml` — not `.yml`, not a path) - - **Environment name:** leave **empty** (unless you later add a GitHub Environment to the job) - - **Allowed actions:** select **`npm publish`** (required) -5. Save. - -npm does **not** validate the config on save — mismatches only show up at publish time (often as `E404`). - -## Order of operations (important) - -1. **Configure trusted publishers** for all packages above (npm side). -2. **Merge** the PR that enables OIDC in `.github/workflows/release.yaml` (`id-token: write`, no `NPM_TOKEN` on publish). -3. On push to `main`, the Changesets workflow publishes any unpublished versions. -4. Confirm success in the Actions log: look for - `No NPM_TOKEN found, but OIDC is available - using npm trusted publishing`. -5. After a successful OIDC publish, optionally harden each package: - - **Publishing access** → **Require two-factor authentication and disallow tokens** - - Revoke the old GitHub secret `NPM_TOKEN` and any automation tokens on npm. - -Do **not** set `NPM_TOKEN` on the publish step once OIDC is intended — `changesets/action` will use the long-lived token instead of OIDC when that env var is present. - -## GitHub workflow - -Live workflow: **`.github/workflows/release.yaml`**. - -It includes: - -- `permissions.id-token: write` -- Node `24` (latest 24.x; npm 12 needs Node ≥ `24.15` — pin the major, not a stale patch like `24.14`) -- Workflow upgrades to `npm@latest` after setup-node (OIDC needs npm ≥ `11.5.1`) -- **No** `NPM_TOKEN` on the publish step -- Package `repository.url` points at `https://github.com/effect-app/libs.git` (set in package.json) - -## Troubleshooting - -| Symptom | Likely cause | -| --- | --- | -| `E404` / “could not be found or you do not have permission” | Trusted publisher mismatch (org/repo/workflow filename), or OIDC not used because `NPM_TOKEN` is still set | -| `ENEEDAUTH` / unable to authenticate | Missing `id-token: write`, old npm CLI, or self-hosted runner (not supported) | -| Only some packages publish | Trusted publisher missing on those packages | -| `EBADENGINE` installing `npm@latest` | Node pin too old (e.g. `24.14`); use major `24` so runners are ≥ `24.15` | - -## Optional hardening - -- GitHub **Environment** (e.g. `npm`) on the `version` job with required reviewers; if used, put the same environment name in every package’s trusted publisher config. -- Keep a **read-only** granular npm token only if install needs private packages (not required for this public monorepo today). diff --git a/repos/effect/.github/workflows/ai-codegen.yml b/.github/workflows/ai-codegen.yml similarity index 100% rename from repos/effect/.github/workflows/ai-codegen.yml rename to .github/workflows/ai-codegen.yml diff --git a/repos/effect/.github/workflows/bundle-comment.yml b/.github/workflows/bundle-comment.yml similarity index 100% rename from repos/effect/.github/workflows/bundle-comment.yml rename to .github/workflows/bundle-comment.yml diff --git a/repos/effect/.github/workflows/check.yml b/.github/workflows/check.yml similarity index 75% rename from repos/effect/.github/workflows/check.yml rename to .github/workflows/check.yml index b52e80d53..fbb21ced9 100644 --- a/repos/effect/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -50,7 +50,7 @@ jobs: - name: Install dependencies uses: ./.github/actions/setup with: - deno-version: v2.9.4 + deno-version: v2.9.7 - run: pnpm check env: # Leave memory headroom for the runner by collecting the Go heap earlier. @@ -130,7 +130,7 @@ jobs: bun: "" command: pnpm test --max-concurrency=10 - runtime: Deno - deno: v2.9.4 + deno: v2.9.7 bun: "" command: deno task test --max-concurrency=10 - runtime: Bun @@ -148,6 +148,7 @@ jobs: docker pull mcr.microsoft.com/mssql/server:2022-latest & docker pull vitess/vttestserver:mysql80 & docker pull redis:alpine & + docker pull coredns/coredns:1.14.7 & wait - name: Install dependencies @@ -156,7 +157,39 @@ jobs: deno-version: ${{ matrix.deno }} bun-version: ${{ matrix.bun }} - name: Test + if: matrix.runtime != 'Deno' run: ${{ matrix.command }} + - name: Test with PostgreSQL lifecycle capture + if: matrix.runtime == 'Deno' + shell: bash + env: + PG_LIFECYCLE_DIAGNOSTICS: "1" + PG_LIFECYCLE_LOG: ${{ runner.temp }}/pg-lifecycle/lifecycle.log + run: | + capture_dir="${{ runner.temp }}/pg-lifecycle" + mkdir -p "$capture_dir" + started_at=$(date --iso-8601=seconds) + docker events --since "$started_at" --format '{{json .}}' > "$capture_dir/docker-events.jsonl" 2> "$capture_dir/docker-events.stderr" & + events_pid=$! + cleanup() { + # Reap only the capture process started here; preserve the test exit code. + kill "$events_pid" 2>/dev/null || true + wait "$events_pid" 2>/dev/null || true + # The runner owns the output file; only journal access needs sudo. + # shellcheck disable=SC2024 + sudo -n journalctl -u docker.service --since "$started_at" --no-pager -o short-iso-precise > "$capture_dir/docker-journal.log" 2>&1 || true + } + trap cleanup EXIT + trap 'exit 130' INT + trap 'exit 143' TERM + ${{ matrix.command }} 2>&1 | tee "$capture_dir/tests.log" + - name: Upload PostgreSQL lifecycle diagnostics + if: always() && matrix.runtime == 'Deno' && (failure() || cancelled()) + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: deno-pg-lifecycle-${{ github.run_attempt }} + path: ${{ runner.temp }}/pg-lifecycle + if-no-files-found: warn doctest: name: Test (Documentation) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 6161e56f7..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: CI - -on: - # push: - # branches: [main] # CI on main branch is handled by release.yaml workflow - pull_request: - branches: [main] - workflow_call: - inputs: - publish_snapshot: - description: Run snapshot publish inside this reusable workflow - type: boolean - default: false - workflow_dispatch: - -env: - CI: true - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - pull-requests: write - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - - - name: Run lint - run: pnpm lint - - build: - name: Build - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - - - name: Run build - run: pnpm build - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-output - path: packages/*/dist - retention-days: 1 - if-no-files-found: error - - test: - name: Test - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - - - name: Run tests - run: pnpm test - - # On main, Changesets calls this workflow and then runs `version` with - # `needs: [ci]`. Keep snapshot out of that reusable path so version does - # not wait for pkg.pr.new. Main snapshot runs as a sibling in release.yaml. - snapshot: - name: Publish Snapshot - needs: [build] - if: | - inputs.publish_snapshot || - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || - github.event_name == 'workflow_dispatch' - uses: ./.github/workflows/publish-snapshot.yml - permissions: - contents: read - pull-requests: write - secrets: inherit diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index eb9719ecc..000000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@v1 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. - # prompt: 'Update the pull request description to include a summary of changes.' - - # Optional: Add claude_args to customize behavior and configuration - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options - # claude_args: '--allowed-tools Bash(gh pr *)' diff --git a/repos/effect/.github/workflows/cluster.yml b/.github/workflows/cluster.yml similarity index 100% rename from repos/effect/.github/workflows/cluster.yml rename to .github/workflows/cluster.yml diff --git a/repos/effect/.github/workflows/effect-bot.yml b/.github/workflows/effect-bot.yml similarity index 100% rename from repos/effect/.github/workflows/effect-bot.yml rename to .github/workflows/effect-bot.yml diff --git a/.github/workflows/npm-promote.yml b/.github/workflows/npm-promote.yml new file mode 100644 index 000000000..18c9ab071 --- /dev/null +++ b/.github/workflows/npm-promote.yml @@ -0,0 +1,67 @@ +name: npm promote +on: + schedule: + - cron: "*/10 * * * *" + workflow_run: + workflows: [Release] + types: [completed] + branches: [main] + workflow_dispatch: + inputs: + dry-run: + description: Print what would change without moving dist-tags or touching issues + type: boolean + default: false + +# Promotions never overlap. A running promotion is never cancelled; a newer +# queued run replaces an older queued one, which is safe because every run +# re-reads the registry. +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +permissions: {} + +jobs: + promote: + name: Promote + # NPM_PROMOTE_PAUSED is the kill switch for a bad release still on `staging`. + if: >- + github.repository_owner == 'Effect-Ts' + && github.ref == 'refs/heads/main' + && vars.NPM_PROMOTE_PAUSED != 'true' + runs-on: ubuntu-latest + timeout-minutes: 15 + # The npm trusted publisher configuration for dist-tags pins this + # workflow file and environment. + environment: npm-promote + permissions: + contents: read + # Records website deploys, so a failed or interrupted deploy is retried. + deployments: write + # npm trusted publishing: dist-tag writes authenticate over OIDC. + id-token: write + issues: write + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: main + # The overdue check reads the commit that bumped the version. + fetch-depth: 0 + persist-credentials: false + - name: Install dependencies + uses: ./.github/actions/setup + - name: Upgrade npm for OIDC dist-tag support + run: npm install -g npm@^11.21.0 + # Promotes the release, deploys the website for it and keeps the tracking issue up to date. + - name: Promote staged release + env: + DRY_RUN: ${{ inputs.dry-run }} + GITHUB_TOKEN: ${{ github.token }} + WEBSITE_DISPATCH_TOKEN: ${{ secrets.WEBSITE_DISPATCH_TOKEN }} + run: | + args=() + if [[ "$DRY_RUN" == "true" ]]; then + args+=(--dry-run) + fi + node packages/tools/npm-promote/src/bin.ts "${args[@]}" diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml deleted file mode 100644 index 33852f531..000000000 --- a/.github/workflows/publish-snapshot.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: Publish Snapshot - -on: - workflow_call: - -permissions: - contents: read - pull-requests: write - -env: - CI: true - -jobs: - snapshot: - name: Publish Snapshot - timeout-minutes: 20 - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: Install dependencies - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: build-output - path: packages/ - - - name: Publish snapshots to pkg.pr.new - id: publish - run: pnpm exec pkg-pr-new publish --pnpm ./packages/* - - - name: Write snapshot usage summary - id: usage - shell: bash - run: | - PACKAGE_LOCATORS="${{ steps.publish.outputs.packages }}" - PACKAGE_URLS="${{ steps.publish.outputs.urls }}" - PUBLISHED_SHA="${{ steps.publish.outputs.sha }}" - if [ -z "$PACKAGE_LOCATORS" ] || [ -z "$PACKAGE_URLS" ] || [ -z "$PUBLISHED_SHA" ]; then - echo "pkg-pr-new did not return expected outputs (packages/urls/sha)" >&2 - exit 1 - fi - - { - echo "## Snapshot publish complete" - echo - echo "- SHA: \`${PUBLISHED_SHA}\`" - echo - echo "Published via pkg.pr.new." - echo - echo "### Install package locators" - echo "\`\`\`bash" - echo "$PACKAGE_LOCATORS" - echo "\`\`\`" - echo - echo "### Package URLs" - echo "\`\`\`text" - echo "$PACKAGE_URLS" - echo "\`\`\`" - } >> "$GITHUB_STEP_SUMMARY" - - { - echo "package_locators<> "$GITHUB_OUTPUT" diff --git a/.github/workflows/publish-wiki.yml b/.github/workflows/publish-wiki.yml deleted file mode 100644 index c66cc7707..000000000 --- a/.github/workflows/publish-wiki.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Publish wiki -on: - push: - branches: [main] - paths: - - wiki/** - - .github/workflows/publish-wiki.yml -concurrency: - group: publish-wiki - cancel-in-progress: true -permissions: - contents: write -jobs: - publish-wiki: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: Andrew-Chen-Wang/github-wiki-action@v4 diff --git a/repos/effect/.github/workflows/release-queue.yml b/.github/workflows/release-queue.yml similarity index 56% rename from repos/effect/.github/workflows/release-queue.yml rename to .github/workflows/release-queue.yml index 1235f5adf..c23e57485 100644 --- a/repos/effect/.github/workflows/release-queue.yml +++ b/.github/workflows/release-queue.yml @@ -10,16 +10,7 @@ on: permissions: {} jobs: - approval-gate: - if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository - runs-on: ubuntu-latest - environment: fork - steps: - - run: echo "Fork PR approved by maintainer." - update: - needs: [approval-gate] - if: always() && (needs.approval-gate.result == 'success' || needs.approval-gate.result == 'skipped') name: Update runs-on: ubuntu-latest timeout-minutes: 10 @@ -31,6 +22,15 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.EFFECT_BOT_GH }} + # Non-cone mode excludes root files too. Git preserves this restriction across gh pr checkout. + # Keep full checkouts for same-repo PRs, pushes, and comments used by release/rebase paths. + - name: Limit fork checkout to changesets + if: github.event.pull_request && github.event.pull_request.head.repo.full_name != github.repository + run: git sparse-checkout set --no-cone '/.changeset/' + # This checks out untrusted fork code with privileged bot credentials available to subsequent steps. + # Sparse checkout reduces exposed files; it is not a security boundary. + # No step may execute code from the working tree (including installs, builds, or repo scripts). + # The pinned release action only reads changeset files on the fork PR path. - run: gh pr checkout ${{ github.event.pull_request.number }} if: github.event.pull_request env: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 6e38f2e11..000000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,94 +0,0 @@ -name: Changesets -on: - push: - branches: - - main -# id-token: write enables npm Trusted Publishing (OIDC). -# Do not pass NPM_TOKEN on the publish step — changesets/action prefers a -# long-lived token when set and will skip OIDC. -# See .github/npm-trusted-publishing.md for npmjs.com setup. -permissions: - contents: write - pull-requests: write - id-token: write -env: - CI: true - PNPM_CACHE_FOLDER: .pnpm-store -jobs: - ci: - uses: ./.github/workflows/ci.yml - - snapshot: - name: Publish Snapshot - needs: [ci] - if: | - github.actor != 'github-actions[bot]' && - !contains(github.event.head_commit.message, 'chore: update versions') - uses: ./.github/workflows/publish-snapshot.yml - permissions: - contents: read - pull-requests: write - secrets: inherit - - version: - needs: [ci] - timeout-minutes: 15 - runs-on: ubuntu-latest - steps: - - name: checkout code repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - # Major pin = latest 24.x (CI uses `latest`). npm@12 needs Node >= 24.15. - node-version: 24 - registry-url: https://registry.npmjs.org - - # Trusted publishing requires npm CLI >= 11.5.1; keep CLI current on this Node line. - - name: Ensure npm supports OIDC trusted publishing - run: npm install -g npm@latest && npm --version - - - uses: pnpm/action-setup@v3 - name: Install pnpm - id: pnpm-install - with: - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-v2-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store-v2- - - - name: install packages - run: | - pnpm install --frozen-lockfile || { - echo "::warning::pnpm install failed; wiping store and retrying" - rm -rf "$(pnpm store path)" - pnpm install --frozen-lockfile - } - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: create and publish versions - uses: changesets/action@v1 - with: - version: pnpm ci:version - commit: "chore: update versions" - title: "chore: update versions" - publish: pnpm ci:publish - createGithubReleases: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/repos/effect/.github/workflows/release.yml b/.github/workflows/release.yml similarity index 85% rename from repos/effect/.github/workflows/release.yml rename to .github/workflows/release.yml index 43931cad5..ee71d6912 100644 --- a/repos/effect/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,10 +41,6 @@ jobs: # automatically to ensure workflows get triggered on the changesets # release branch. github-token: ${{ secrets.CHANGESET_GITHUB_TOKEN }} - - name: Deploy website - if: steps.changesets.outputs.published == 'true' - uses: ./.github/actions/deploy-website - with: - channel: v4 - dispatch-token: ${{ secrets.WEBSITE_DISPATCH_TOKEN }} - revision: ${{ github.sha }} + # Packages are published under the `staging` dist-tag. The npm promote + # workflow moves `latest` once npm serves every package, then deploys the + # website. diff --git a/repos/effect/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml similarity index 100% rename from repos/effect/.github/workflows/snapshot.yml rename to .github/workflows/snapshot.yml diff --git a/.gitignore b/.gitignore index e98229014..26bba5ae5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,64 +1,41 @@ -.claude/worktrees -.DS_Store -node_modules/ -*.log -**/*/.next/ -packages/**/dist/ -!**/.eslintrc*.js -.build/ -dist/ -**/bin/*.js -**/bin/*.js.map -**/bin/*.d.ts -**/bin/*.d.ts.map -tsconfig.tsbuildinfo -.tsbuildinfo -.ultra.cache.json -.jest-cache -.next -.data/ -.npmrc -.cache/ -.opencode/ -.env.local +# Generated by Direnv .env -# CMake -cmake-build-*/ - - -# File-based project format -*.iws - -# IntelliJ -out/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# End of https://www.toptal.com/developers/gitignore/api/intellij - -.infra/*_rev.yaml +# Dot files and directories +**/.* +!/.agents/ +!/.changeset/ +!/.envrc +!/.github/ +!/.gitignore +!/.oxlintrc.json +!/.vscode/ + +# Generated by TypeScript +dist/ +build/ +lib/ +**/*.tsbuildinfo -# vitest? -*.timestamp-*.mjs +# Generated by Pnpm +node_modules/ -# nix -.direnv +# Generated by scripts +coverage/ +tmp/ +stats.txt +# Generated by MacOS +.DS_Store -storybook-static/ -run.sh +# Scratchpad files +scratchpad/**/* -# Generated publish staging directories -packages/*/.publish/ +# Agent instructions +/AGENTS.md +/packages/**/AGENTS.md +/packages/**/CLAUDE.md +/packages/**/ai-docs/ -# agent gh shim (generated) + local ship-gate state -.tools/ -.run/ +# Agent config +opencode.json diff --git a/.ncurc.json b/.ncurc.json deleted file mode 100644 index be9722c1c..000000000 --- a/.ncurc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "reject": [ - "glob", - "@types/glob", - "jwks-rsa", - "applicationinsights", - "@faker-js/faker", - "redis", - "@types/redis", - "react-hook-form", - "eslint-plugin-codegen", - "@tanstack/*" - ], - "dep": [ - "prod", - "dev", - "optional", - "peer" - ] -} diff --git a/.npmrc b/.npmrc deleted file mode 100644 index ad2a213cd..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -# @effect-app:registry=https://npm.pkg.github.com diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index b6f5ce1e9..000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v22.23 diff --git a/.oxlintrc.json b/.oxlintrc.json index 78db57e54..3fcaaf516 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,40 +1,37 @@ { - "$schema": "./node_modules/@effect/tsgo/oxlint-schema.json", - "extends": ["./packages/eslint-shared-config/src/oxlintrc.json"], - "options": { - "typeAware": true - }, - "plugins": ["effecttsgo"], - "rules": { - "effecttsgo/any-unknown-in-error-context": "off", - "effecttsgo/effect-fn-iife": "off", - "effecttsgo/global-error-in-effect-catch": "off", - "effecttsgo/layer-merge-all-with-dependencies": "off", - "effecttsgo/lazy-effect": "off", - "effecttsgo/return-effect-in-gen": "off", - "effecttsgo/run-effect-inside-effect": "off", - "effecttsgo/schema-sync-in-effect": "off", - "effecttsgo/strict-effect-provide": "off", - "effecttsgo/unsafe-effect-type-assertion": "off", - "effecttsgo/unstable-api-usage": "off", - "effecttsgo/floating-effect": "off", - "effecttsgo/multiple-effect-provide": "off", - "effecttsgo/unknown-in-effect-catch": "off", - "effecttsgo/missing-effect-service-dependency": "error", - "effecttsgo/effect-fn-opportunity": "warn", - "effecttsgo/global-error-in-effect-failure": "warn", - "effecttsgo/prefer-schema-over-json": "warn" - }, - // ignorePatterns overwrites (not merges) the base — all standard patterns must be repeated here. + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "extends": ["./packages/tools/oxc/oxlintrc.json"], "ignorePatterns": [ - "**/dist/**", - "**/node_modules/**", - "**/*.d.ts", - "**/*.js", - "**/*.jsx", - "**/vitest.config.ts", - "**/eslint.*.mjs", + "LLMS.md", + "**/dist", + "**/build", + "**/docs", + "**/*.tsbuildinfo", "**/CHANGELOG.md", - "repos/**" - ] + "packages/effect/typeperf/**/*", + "packages/effect/src/StandardSchema.ts", + "packages/tools/graphql-generator/test/generated/**/*.graphql.ts", + "!scratchpad/**/*", + "**/.*", + "!.agents", + "!.agents/**/*", + "!.changeset", + "!.changeset/**/*", + "!.envrc", + "!.github", + "!.github/**/*", + "!.gitignore", + "!.oxlintrc.json", + "!.vscode", + "!.vscode/**/*" + ], + "jsPlugins": ["@effect/oxc/oxlint"], + "overrides": [{ + "files": ["**/{test,typetest,examples,ai-docs,benchmark,bundle,scripts,scratchpad}/**"], + "rules": { + "effect/no-bigint-literals": "off", + "eslint/no-console": "off", + "effect/no-import-from-barrel-package": "off" + } + }] } diff --git a/.specs/Update-to-latest-Effect.md b/.specs/Update-to-latest-Effect.md deleted file mode 100644 index 88edfa6d0..000000000 --- a/.specs/Update-to-latest-Effect.md +++ /dev/null @@ -1,23 +0,0 @@ -# Update to latest effect packages - -## Rules - -- only include root `package.json` and packages under the `packages` folders - -## Steps - -1. run `pnpm test` and `pnpm lint-fix` to compare later -2. update package.json files -3. run `pnpm i` -4. commit the dep bump (package.json files + `pnpm-lock.yaml`) — `pnpm subtree:effect` runs `git subtree pull` which **requires a clean working tree** (`fatal: working tree has modifications. Cannot add.` otherwise). Commit before syncing. -5. run `pnpm subtree:effect` to sync the `repos/effect` subtree to the same version (creates its own squash-merge commit), then run `pnpm i` inside it. -6. run test and lint again, compare to from before the update. -7. create a changeset describing the changes -8. prepare commit (changeset + any test/lint fallout) - -If new errors occur, first describe the problem, propose solutions and wait for answers. - -## Notes - -- subtree url: `https://github.com/Effect-TS/effect.git`, prefix `repos/effect`, tag `effect@`. -- v4 currently lives under the npm `rc` dist-tag (`beta` is frozen at `4.0.0-beta.107`; npm `latest` is still v3). Find target with `npm view effect dist-tags` — prefer `rc` if present, else `beta`. diff --git a/.specs/changeset-publishing.md b/.specs/changeset-publishing.md deleted file mode 100644 index 7bd8ac4a6..000000000 --- a/.specs/changeset-publishing.md +++ /dev/null @@ -1,138 +0,0 @@ -# Changesets Publishing - -## Summary - -Publish `effect-app`, `infra`, `vue` and `vue-components` under unified -versions using changesets' `fixed` groups. - -## Background - -All public packages must move in lockstep under a single version number (`4.0.0-beta.0`, `4.0.0-beta.1`, ...) -so consumers can install a coherent set of packages at any beta iteration. -Changesets supports this via **fixed version groups** (all packages in a group share the same -version). - -## Goals - -- All 4 public library packages publish the same version on every release. -- Developers continue using the standard `pnpm changeset` workflow. - -## Non-goals - -- No changes to the build system, CI pipeline scripts, or package contents. -- No independent versioning — all packages move together. -- No canary/nightly publishing strategy. - -## How it works - -### Fixed groups - -The `fixed` field in `.changeset/config.json` takes an array of package-name -arrays. All packages in a group are bumped to the **same version** whenever any -one of them is included in a changeset. By placing all 24 public packages in one -group, a single changeset touching any package triggers a version bump for every -package. - -### Version stability during pre-release - -A common concern: won't a `minor` or `patch` changeset during the beta change -the base version (e.g., `4.0.0-beta.1` → `4.1.0-beta.0`)? No — changesets -prevents this via `pre.json`. - -`pre.json` stores two critical fields: - -- **`initialVersions`**: the version of each package when pre-release mode was - entered (e.g., `3.0.0`). -- **`changesets`**: the IDs of every changeset applied since entering - pre-release mode. - -When `changeset version` runs, it computes the target version relative to -`initialVersions` using the **cumulative** bump of all tracked changesets (both -already-applied ones in `pre.json.changesets` and new pending ones). Because the -initial major changeset is always in the tracked set, the highest bump type is -always `major`, and the base version stays at `4.0.0` regardless of subsequent -patch or minor changesets. Only the beta counter increments. - -Example: - -1. Enter pre-release, `initialVersions` = `3.0.0` -2. Major changeset → `changeset version` → `4.0.0-beta.0` (major from `3.0.0`) -3. Developer adds a patch changeset → `changeset version` → `4.0.0-beta.1` - (major still highest → base unchanged, counter increments) -4. Developer adds a minor changeset → `changeset version` → `4.0.0-beta.2` - (major still highest → base unchanged, counter increments) - -The base semver never drifts during the beta period. - -### Combined behavior - -With both features active, every `changeset version` + `changeset publish` cycle -produces a release where all 24 packages share `4.0.0-beta.`. - -## Setup steps - -### 1. Update `.changeset/config.json` - -Add the `fixed` group and change `access` to `"public"`: - -## Ongoing workflow - -### Adding changesets - -Developers continue using `pnpm changeset` as usual. Because of the `fixed` -group, the bump type (patch/minor/major) of any individual changeset is -effectively irrelevant to the version number — all packages are already on a -major bump track. However, the changeset message still serves as the changelog -entry, so meaningful descriptions are important. - -### Publishing a new version - -```bash -npx changeset version # bumps to 4.0.0-beta.(n+1) -npx changeset publish # publishes all packages under "beta" tag -``` - -The counter auto-increments. No manual version editing is needed. - -### CI integration - -The existing CI publish workflow (typically `changeset version` + -`changeset publish` in a GitHub Action) works without modification. The -pre-release mode and fixed group are configuration-only — no script changes -required. - -## Graduating to stable - -When ready to release `4.0.0`: - -```bash -npx changeset pre exit # removes pre-release mode -npx changeset version # sets all packages to 4.0.0 -npx changeset publish # publishes to "latest" dist-tag -``` - -After exiting, the `fixed` group can remain to keep packages in lockstep for -future `4.x` releases, or be removed if independent versioning is desired. - -## Edge cases - -### Workspace dependency ranges - -`updateInternalDependencies: "patch"` ensures workspace `dependencies` and -`peerDependencies` are updated whenever a dependency's version changes. Because -all packages share the same version, cross-references will always point to the -current beta. Verify that dependency ranges use `workspace:^` (pnpm protocol) so -they resolve correctly during development and are rewritten to concrete ranges on -publish. - -### npm version collision safety - -Changesets' pre-release counter prevents collisions — `4.0.0-beta.0`, -`4.0.0-beta.1`, etc. are distinct versions. npm's immutability guarantees that a -published version can never be overwritten. - -## Files to modify - -| File | Change | -| ------------------------ | --------------------------------------------- | -| `.changeset/config.json` | Add `fixed` array, set `access` to `"public"` | diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6f2a533fc..1a118e9e7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,6 @@ { "recommendations": [ "oxc.oxc-vscode", - "dprint.dprint", - "dbaeumer.vscode-eslint", - "editorconfig.editorconfig", - "Vue.volar" + "dprint.dprint" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 3a998c7db..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "type": "node", - "request": "launch", - "name": "Debug TSC", - "program": "${workspaceFolder}/node_modules/tsc.js", - "args": [ - "--build tsconfig.all.json" - ], - "sourceMaps": false, - "protocol": "inspector", - "configurations": [ - { - "name": "Attach by Process ID", - "processId": "${command:PickProcess}", - "request": "attach", - "skipFiles": [ - "/**" - ], - "type": "node" - } - ] -} diff --git a/.vscode/model.code-snippets b/.vscode/model.code-snippets deleted file mode 100644 index 3802d7720..000000000 --- a/.vscode/model.code-snippets +++ /dev/null @@ -1,77 +0,0 @@ -{ - "ModelCodegen": { - "prefix": "genmod", - "body": [ - "// codegen:start {preset: model}" - ] - }, - "ResourceCodegen": { - "prefix": "genrsc", - "body": [ - "// codegen:start {preset: meta, sourcePrefix: src/resources/}" - ] - }, - "ModelComplete": { - "prefix": "moc", - "body": [ - "export class $1 extends S.ExtendedClass<$1, $1.Encoded>()({", - "$2", - "}) {}" - ], - "description": "Defines a Model signature" - }, - "Model": { - "prefix": "mo", - "body": [ - "export class $1 extends S.Class<$1>()({$2}) {}", - "" - ], - "description": "Defines a Model signature" - }, - "UnionOpaque": { - "prefix": "un", - "body": [ - "const $1__ = union({ $2 })", - "const $1_ = enhanceClassUnion(OpaqueSchema<$1, $1.Encoded>()($1__))", - "export type $1 = To & UnionBrand", - "export interface $1Schema extends Identity {}", - "export const $1: $1Schema = $1_", - "export namespace $1 {", - " export type Encoded = Encoded & UnionBrand", - "}" - ] - }, - "Request": { - "prefix": "req", - "body": [ - "export class $1 extends S.Req<$1>()(\"$1\", {", - " $2", - "}, { success: $3 }) {}", - "" - ], - "description": "Defines a Request signature" - }, - "Response": { - "prefix": "res", - "body": [ - "export class Response extends S.Class", - " Effect.gen(function*() {", - " //const userRepo = yield* UserRepo", - "", - " return {", - " $2: $2($3)", - " }", - " })" - ] - } -} \ No newline at end of file diff --git a/.vscode/service.code-snippets b/.vscode/service.code-snippets deleted file mode 100644 index b3314afcb..000000000 --- a/.vscode/service.code-snippets +++ /dev/null @@ -1,14 +0,0 @@ -{ - "ServiceConstructor": { - "prefix": "svc", - "body": [ - "export class $1 extends Effect.Service<$1>()(\"$1\", {", - " dependencies: [$3],", - " effect: Effect.gen(function*() {", - " $2", - " return {}", - " })", - "}) {}", - ] - } -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 362ed5f03..672eea785 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,69 +1,51 @@ { - "files.exclude": { - "**/.git": false - }, - "explorer.sortOrderLexicographicOptions": "upper", - "typescript.preferences.includePackageJsonAutoImports": "on", - "typescript.experimental.expandableHover": true, - "js/ts.hover.maximumLength": 5000, - "typescript.preferences.autoImportFileExcludePatterns": [ - "node_modules/@sentry/node", - "node_modules/vitest/dist", - "node_modules/@azure/cosmos", - "node_modules/effect-app/Schema", - "node_modules/lodash", - "node_modules/@types/lodash" - ], - "typescript.tsdk": "node_modules/typescript/lib", - "eslint.workingDirectories": [ - { - "pattern": "apps/*" - }, - { - "pattern": "packages/*" - } - ], + "dprint.path": "node_modules/.bin/dprint", "editor.formatOnSave": true, + "editor.defaultFormatter": "dprint.dprint", "editor.formatOnSaveMode": "file", "editor.codeActionsOnSave": { - "source.fixAll.oxc": "explicit", - "source.fixAll.eslint": "explicit", - "source.addMissingImports": "explicit" + "source.fixAll.oxc": "explicit" + }, + "editor.quickSuggestions": { + "other": true, + "comments": false, + "strings": false + }, + "editor.acceptSuggestionOnCommitCharacter": true, + "editor.acceptSuggestionOnEnter": "on", + "editor.quickSuggestionsDelay": 10, + "editor.suggestOnTriggerCharacters": true, + "editor.tabCompletion": "off", + "editor.suggest.localityBonus": true, + "editor.suggestSelection": "recentlyUsed", + "editor.wordBasedSuggestions": "matchingDocuments", + "editor.parameterHints.enabled": true, + "files.insertFinalNewline": true, + "[typescript]": { + "editor.defaultFormatter": "dprint.dprint" }, - "editor.defaultFormatter": "dprint.dprint", - "[vue]": { + "[typescriptreact]": { "editor.defaultFormatter": "dprint.dprint" }, "[javascript]": { "editor.defaultFormatter": "dprint.dprint" }, - "[javascriptreact]": { + "[json]": { "editor.defaultFormatter": "dprint.dprint" }, - "[typescript]": { + "[jsonc]": { "editor.defaultFormatter": "dprint.dprint" }, - "[typescriptreact]": { + "[markdown]": { "editor.defaultFormatter": "dprint.dprint" }, - "eslint.validate": [ - "javascript", - "typescript", - "vue" - ], - "eslint.format.enable": false, - "csv-preview.separator": ";", - "cSpell.words": [ - "codegen", - "composables", - "pipeable", - "tsplus" + "deno.enable": true, + "deno.lint": false, + "deno.enablePaths": [ + "./packages/platform/deno" ], - "files.watcherExclude": { - "**/target": true - }, - "chat.tools.terminal.autoApprove": { - "git add": true, - "git commit": true - } + "js/ts.tsdk.path": "./node_modules/typescript/bin", + "js/ts.tsdk.additionalLocations": ["./node_modules/typescript/bin"], + "js/ts.tsdk.promptToUseWorkspaceVersion": true, + "js/ts.experimental.useTsgo": true } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 2d9f2e8a6..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "tasks": [ - { - "label": "build", - "type": "shell", - "command": "pnpm watch", - "options": { - "cwd": "${workspaceRoot}" - }, - "group": { - "kind": "build", - "isDefault": true - }, - "isBackground": true, - "presentation": { - "group": "watch-build" - }, - "problemMatcher": [ - { - "base": "$tsc-watch", - "fileLocation": [ - "relative", - "${workspaceRoot}" - ] - } - ] - }, - { - "label": "Run Storybook OmegaForm", - "type": "shell", - "command": "pnpm storybook", - "options": { - "cwd": "${workspaceRoot}/packages/vue-components" - }, - "problemMatcher": [] - } - ] -} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 0ca05556b..000000000 --- a/AGENTS.md +++ /dev/null @@ -1,217 +0,0 @@ -# Agent Instructions - -This is the Effect App library repository, focusing on functional programming patterns and effect systems in TypeScript, wrapping and extending the Effect library. - -## Development Workflow - -- The git base branch is `main` -- Use `pnpm` as the package manager - -### Pull requests - -Always open a PR for agent work that changes the repo — do not leave finished work only on a local branch. - -- **Draft early**: open a draft PR as soon as there is a meaningful commit (or when starting multi-step work that will land), so review/CI can track progress while the rest of the work continues. Pushes to a draft run the same gate as any other push — the draft is about review status, not about skipping checks. -- **Keep the PR current**: push commits as you go; update the PR body if scope shifts. -- **Ready when done**: publish with `pnpm pr:ready`, or with plain `gh pr ready` — both run the ship gate first and undraft only if it passes. Do not hand-run the checks beforehand and do not leave a finished, validated change as draft. -- **Base**: target `main` unless the work is explicitly stacked on another branch. - -### The gate runs the checks — you do not - -`.githooks/pre-push` is the agent ship gate. It is a **no-op for humans** and -fires only for coding agents (`GROK_AGENT` / `T3_AGENT` / `AI_AGENT` / Claude / -Cursor / Codex env markers). - -It runs on **every** agent push — draft, ready, or no PR at all. There is no -browser or API suite here that a draft could usefully defer, and a pushed commit -that does not compile or whose tests fail is worth nothing to a reviewer whatever -the PR says. - -The gate is `pnpm check` → `pnpm lint` → `pnpm test`, which is what CI runs and -nothing more. This is a library monorepo — no application to stand up, no browser -suite — so the unit run _is_ the gate. It caches the validated HEAD SHA in -`.run/agent-ship-gate.json`, so **one commit is validated once** however many -times you push or publish it. Force a re-run with `AGENT_SHIP_GATE_FORCE=1`. - -**Do not hand-run `pnpm check` / `lint` / `test` as routine verification.** -Validating the same commit repeatedly costs the same each time and proves nothing -the first run did not. While iterating, narrow proof is the right tool — the one -test you are fixing, or a typecheck of the package you touched. Whole-gate runs -belong to the push. - -`.envrc` puts the `gh` shim on `PATH`; run **`direnv allow`** once after cloning -(`command -v gh` should print `.tools/bin/gh`). Never `--no-verify`, and never set -`SKIP_AGENT_PREPUSH` — that is the human escape hatch. - -### Core Principles - -- **Zero Tolerance for Errors**: All automated checks must pass -- **Root causes, not workarounds**: When something fails — a type error, a runtime crash, a failing test, a hanging fiber — diagnose the invariant that was violated and fix it there. Do **not** mask the symptom with a wrapper, a revive helper, a `try/catch`, a `JSON.parse(JSON.stringify(...))`, or any other shape-coercion. Each of these is a tell that you skipped the diagnosis. -- **No `as any` / `as unknown` casts**: A specific case of the rule above. Casts hide the type system telling you the shape is wrong. Understand the actual types and fix the root cause. If a type mismatch exists, find the correct v4 API, update the type signatures, or restructure the code. -- **No hand-rolled deserialization**: Anything stored as JSON and read back must round-trip through a Schema codec (`S.fromJsonString(S.toCodecJson(...))`, `S.encodeEffect` / `S.decodeEffect`). Class prototypes — `Exit`, `Cause`, `Workflow.Result`, `Schema.Class` instances — do not survive `JSON.parse`. The schema layer is the canonical reconstruction path; writing a custom `reviveX` helper is a smell that the codec is missing. -- **Search prior art before writing helpers**: Before introducing a new utility for a cross-boundary concern (serialization, encoding, retries, context propagation, OCC), grep the repo and `repos/effect` for the established pattern. The cluster engine, the existing Cosmos adapter, the SQL stores — they have already solved most of these problems. Copying the established pattern beats inventing a parallel one. -- **Check newer branches before resuming work**: A new session may resume on `main` with no checkout of relevant feature branches. Before extending a topic, run `git branch -a | grep ` and inspect the latest version with `git show :`. Session-local memory of "how X works" may be from an older draft that has since been replaced. -- **Clarity over Cleverness**: Choose clear, maintainable solutions -- **Conciseness**: Keep code and any wording concise and to the point. Sacrifice grammar for the sake of concision. -- **Reduce comments**: Avoid comments unless absolutely required to explain unusual or complex logic. Comments in jsdocs are acceptable. -- **Look for effect sources inside `repos/effect`** -- **Never import local `repos` files**: Always use the latest online versions of packages instead. -- **Never webfetch from the effect repos**: just use the locally included under `repos` - -### When you hit an error, before writing any fix - -Errors are signals, not nuisances. Run this checklist: - -1. **State the failure precisely.** Quote the exact error or behavior. `"object is not iterable"` is not the same as `"wrong type"`. The message names the violated invariant. -2. **Name the boundary.** Where in the data flow did the value lose the property the consumer needs? At the storage write, the storage read, the network hop, the schema decode, the context provision? -3. **Search for prior art at that boundary.** `grep` the repo + `repos/effect` for how similar values cross the same boundary. If a pattern exists, use it. -4. **Only then write code.** If your fix re-implements something the searched-for pattern already does (revive prototypes, retry on OCC, encode JSON), stop — use the existing pattern instead. - -Anti-patterns that mean you skipped the checklist: - -- `JSON.parse` of a value that contains tagged classes, followed by manual prototype reconstruction. -- `try/catch` around a yield that swallows the error and returns a fabricated value. -- Adding a second `as any` to make a first `as any` typecheck. -- Adding a sleep / retry to "give it time to work" instead of finding the missing wake signal. -- Disabling a hook (`--no-verify`) or a check to make the diff land. - -### Validation - -The ship gate owns validation — see _The gate runs the checks — you do not_. It -runs `pnpm check` → `pnpm lint` → `pnpm test` on every push and on publish, once -per commit. - -`pnpm lint-fix` is the one thing worth running by hand, because it _writes_: it -formats and auto-fixes across packages, and the gate only reports what it would -have fixed. Run it when you are done editing, stage what it changes, then push. - -If type checking fails in a way that makes no sense against the diff, `pnpm clean` -clears the caches — stale incremental state produces phantom errors. - - - - -## Code Style Guidelines - -**Always** look at existing code in the repository to learn and follow -established patterns before writing new code. - -Do not worry about getting code formatting perfect while writing. Use `pnpm lint-fix` -to automatically format code according to the project's style guidelines. - -## Prefer `Effect.fnUntraced` over functions that return `Effect.gen` - -Instead of writing: - -```ts -const fn = (param: string) => - Effect.gen(function*() { - // ... - }) -``` - -Prefer: - -```ts -const fn = Effect.fnUntraced(function*(param: string) { - // ... -}) -``` - -## Using `Context.Service` - -Prefer the class syntax when working with `Context.Service`. For example: - -```ts -import { Context } from "effect-app" - -class MyService extends Context.Service number -}>()("MyService") {} -``` - -## Checking Array is not empty - -Avoid `.length > 0` or `.length === 0` or `!.length` or `!!.length` checks, use `Array.isArrayNonEmpty` for type narrowing by default. - - - - - - - -## Per-request `Effect.provide(layer)` must isolate its MemoMap - -`Effect.provide(self, layer)` resolves its `MemoMap` from the ambient fiber -context. On an HTTP server, that MemoMap lives on the server fiber and is -shared by every request that server handles. The first request to build a -stateful layer (anything using `Layer.effect` / `Effect.acquireRelease`) -memoizes the resulting value onto the server fiber; every subsequent request -then receives the _same instance_ — including its `clear()` / dispose -finalizer, which now fires at the wrong time. - -When you call `Effect.provide(layer)` (or `Stream.provide(layer)`) inside a -per-request hot path: - -- Pass `{ local: true }` if the layer is pure / stateless or you genuinely - want it scoped to _this_ effect only, or -- Build it explicitly against the request scope with a fresh `MemoMap` - (`Layer.makeMemoMap` + `Layer.buildWithMemoMap(layer, memoMap, requestScope)`) - — see `provideOnRequestScope` in `packages/infra/src/setupRequest.ts`. - -If neither option is taken and the layer carries state, the state leaks -across requests. Concrete repro lives in -`packages/infra/test/rpc-context-map-streaming.test.ts` (the overlapping -requests case). - -## Changesets - -All pull requests must include a changeset. You can create changesets in the -`.changeset/` directory. - -The have the following format: - -```md ---- -"package-name": patch | minor | major ---- - -A description of the change. -``` diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 8359239d2..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,3 +0,0 @@ -# CLAUDE.md - -Strictly follow the rules in ./AGENTS.md diff --git a/repos/effect/LICENSE b/LICENSE similarity index 100% rename from repos/effect/LICENSE rename to LICENSE diff --git a/repos/effect/LLMS.md b/LLMS.md similarity index 92% rename from repos/effect/LLMS.md rename to LLMS.md index 963a7a5bd..eaaabca6d 100644 --- a/repos/effect/LLMS.md +++ b/LLMS.md @@ -370,6 +370,30 @@ Build http clients with the `HttpClient` module. Test handlers through an in-memory typed client with `HttpApiTest`, without starting an HTTP server or touching a real database. +## Typed GraphQL clients with `effect/graphql` + +Use the `effect/graphql` modules to call GraphQL APIs with typed +operations. Run `graphqlgen` from `@effect/graphql-generator` to generate +operations from `.graphql` documents, then build a client with +`GraphQLClient.make` and provide a transport from `GraphQLProtocol`. + +- **[Getting started with GraphQL clients](./ai-docs/src/52_graphql/10_basics.ts)**: + Define a query, build a typed client service over HTTP with an auth + middleware, and page through a cursor connection. +- **[Testing code that uses a GraphQL client](./ai-docs/src/52_graphql/20_testing.ts)**: + Replace a client service with `GraphQLClient.layerTest`, answering each + operation from a handler instead of a server. +- **[Batching lookups with GraphQLResolver](./ai-docs/src/52_graphql/30_batching.ts)**: + Collect concurrent lookups by id into one `nodes(ids:)` request with + `GraphQLResolver`, instead of one request per id. +- **[Batching single-item lookups with aliases](./ai-docs/src/52_graphql/40_aliased-batching.ts)**: + When an API has no field that takes a list of keys, `GraphQLResolver.aliased` + repeats a generated operation under aliases, so concurrent lookups share one + request and keep their generated types. +- **[Handling GraphQL errors, retries and rate limits](./ai-docs/src/52_graphql/50_errors-and-retries.ts)**: + Read rate-limit headers in a middleware, tell GraphQL errors apart by their + fields, and retry timeouts and rate limits with `Effect.retry`. + ## Working with child processes Use the `effect/process` modules to define child processes and run them with `ChildProcessSpawner`. diff --git a/repos/effect/MIGRATION.md b/MIGRATION.md similarity index 97% rename from repos/effect/MIGRATION.md rename to MIGRATION.md index 4df8d9eaf..22d1555b5 100644 --- a/repos/effect/MIGRATION.md +++ b/MIGRATION.md @@ -71,9 +71,8 @@ Moving these modules does not stabilize their APIs. ### Performance and Bundle Size The fiber runtime has been rewritten for reduced memory overhead and faster -execution. The core `effect` package supports aggressive tree-shaking — a -minimal Effect program bundles to ~6.3 KB (minified + gzipped). With Schema, -~15 KB. +execution. The core `effect` package supports aggressive tree-shaking. A +minimal Effect program bundles to 7.1 kB (minified + gzipped). --- diff --git a/README.md b/README.md index 96d052283..4d9effea2 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,98 @@ -# Practical use of Effect-TS + -This is an opinionated library for full-stack [Effect-TS](https://github.com/Effect-TS/core). -(See repositories for more info and discord link, articles, youtube videos, etc). +[![pkg.pr.new](https://img.shields.io/badge/pkg.pr.new-Effect--TS%2Feffect-black)](https://pkg.pr.new/~/Effect-TS/effect) -WIP [docs](https://github.com/effect-ts-app/docs) +# Effect -See https://github.com/effect-ts-app/boilerplate for a sample app use. +Effect is a library for building robust, maintainable, type-safe, and production grade applications in TypeScript. It helps you handle the hard problems at scale: typed errors, dependency injection, structured concurrency, scheduling, tracing, and unified schema validation. -## Package boundaries +> **Effect 4.x is a long-term support (LTS) release.** If you are upgrading from Effect 3.x, follow the [migration guide](MIGRATION.md). -- `effect-app`: service contracts and runtime-agnostic base logic. -- `@effect-app/infra`: backend / Node adapters. -- `@effect-app/vue`: Vue / browser adapters. - -Migration targets introduced in this repo include: - -- `@effect-app/infra/Emailer/service` -> `effect-app/Emailer` -- `@effect-app/infra/QueueMaker/service` -> `effect-app/QueueMaker` -- `@effect-app/infra/Store/service` -> `effect-app/Store` -- `@effect-app/infra/Model/*` -> `effect-app/Model/*` -- `@effect-app/vue/runtime` -> `effect-app/runtime` -- `@effect-app/vue/toast` -> `effect-app/toast` -- `@effect-app/vue/withToast` -> `effect-app/withToast` - -## Update Effect Subtree - -`repos/effect` is a git subtree of `https://github.com/Effect-TS/effect-smol.git`. - -Recommended (uses the currently pinned `effect` / `@effect/*` version from package.json): - -```sh -pnpm subtree:effect -``` - -Or directly via the CLI: - -```sh -effa sync-effect -``` - -Use a different repository URL: - -```sh -effa sync-effect --url https://github.com/Effect-TS/effect-smol.git -``` - -Manual update to a specific ref: - -```sh -git subtree pull --prefix=repos/effect https://github.com/Effect-TS/effect-smol.git --squash -``` - -Example: - -```sh -git subtree pull --prefix=repos/effect https://github.com/Effect-TS/effect-smol.git @effect/ai-anthropic@4.0.0-beta.47 --squash -``` - -## Update Effect App Libs Subtree - -Apps that vendor this repository as `repos/libs` can sync it to the pinned -`effect-app` / `@effect-app/*` version in their package.json: - -```sh -effa sync-effect-app -``` - -The command first tries Changesets package tags such as -`effect-app@4.0.0-beta.271` or `@effect-app/infra@4.0.0-beta.271`. If no -matching tag exists, it fetches `https://github.com/effect-app/libs.git` into a -local git cache and selects the latest commit on the default branch whose -package manifest still has the pinned version. - -Escape hatch for manual refs: +## Installation ```sh -effa sync-effect-app --ref main -effa sync-effect-app --ref latest # latest main -``` - -Both `sync-effect` and `sync-effect-app` fetch selected commits with -`--no-tags`, so syncing subtrees does not import upstream package tags into the -app repository. - -The clean release convention is to publish git tags for package versions -(`effect-app@`, `@effect-app/infra@`, etc.) from the release -workflow. The history scan exists because this repo still has older `v*` tags -but not current package-version tags. - -## Deployment - -Uses [Changesets](https://github.com/changesets/changesets/blob/main/README.md) - -1. make changes -2. generate and include changeset `pnpm changeset` -3. wait for build which creates a PR -4. inspect the PR, merge when alright -5. await new build and new package deployments - -## Thanks - -- All contributors -- Michael Arnaldi, Max Brown and the Effect-TS contributors for Effect - - ZIO Contributors for the excellent ZIO -- Anyone else we're forgetting.. - -## How to view OmegaForm docs - -On Mac: - -1. `Shift + Command + P` -2. Select `Run Task` -3. Select `Run Storybook OmegaForm` -4. Will automatically open browser to http://localhost:6006/ Enjoy! - -On Windows: - -1. Select "Run Task" - -```bash -cd packages/vue-components -``` - -2. run storybook - -```bash -pnpm storybook +npm install effect ``` -3. Will automatically open browser to http://localhost:6006/ Enjoy! +## Requirements + +- **TypeScript 5.9 or newer.** TypeScript 7 is recommended for the best performance and compatibility with [Effect's TypeScript tooling](https://github.com/Effect-TS/tsgo#installation). +- **Node.js 18 or newer** is the general minimum for running Effect on Node.js. Some integration packages require newer runtimes; for example, `@effect/sql-sqlite-node` requires Node.js 22.16 or newer. +- **Strict type-checking:** the `strict` flag must be enabled in your `tsconfig.json`. + +## Links + +- [Website](https://effect.website): documentation, guides, and news +- [Discord](https://discord.gg/effect-ts): ask questions, share what you're building, and talk to the core team +- [Community](https://effect.website/community-hub): meetups and events, or bring Effect to your own +- [Issues](https://github.com/Effect-TS/effect/issues): bug reports and feature requests +- [Jobs](https://effect.website/effect-jobs): companies hiring Effect developers +- Follow us on [X](https://x.com/EffectTS_), [Bluesky](https://bsky.app/profile/effect-ts.bsky.social), and [LinkedIn](https://www.linkedin.com/company/effect-ts) + +## Let's talk + +Whether your team is considering Effect, rolling it out, or already running it in production, we'd love to hear from you: what you're building, what works, and what you need from Effect next. + +- **Talk to the maintainers.** Introduce your team on [Discord](https://discord.gg/effect-ts) or email [contact@effectful.co](mailto:contact@effectful.co). We're happy to connect privately on Slack or Discord for feedback and help with adoption. +- **Production support.** We're exploring how to better support teams running Effect in production. If your organization has specific support needs, let's discuss them. +- **Adoption help.** Our [adoption partners](https://effect.website/adoption-partners) offer implementation, consulting, team extension, training, and commercial support. + +Feedback from teams using and evaluating Effect directly shapes what we stabilize and build next. + +## Long-term support + +Teams depend on Effect for systems they expect to run for years. Effect 4.x is a long-term support (LTS) release with the following guarantees: + +- At least three years of support, including bug and security fixes. +- Bug fixes for one year after the next major version is released. +- Security fixes for two years after the next major version is released. + +Stable APIs reserve breaking changes for major releases. APIs marked unstable may change in minor releases, and experimental APIs may change in patch releases. + +## Effect v3 + +The Effect v3 source code is available on the [`v3`](https://github.com/Effect-TS/effect/tree/v3) branch, which is also where issues and pull requests meant for Effect v3 should be targeted. To upgrade, see the [migration guide](MIGRATION.md). + +## Packages + +This monorepo contains the core `effect` package alongside integration packages that extend it. All packages listed below are released together with synchronized versions. + +| Package | Description | API Reference | +| --------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------ | +| [`effect`](packages/effect) | The core package | [docs](https://effect.website/docs/v4/api/effect) | +| [`@effect/platform-browser`](packages/platform/browser) | Platform services for the browser | [docs](https://effect.website/docs/v4/api/platform-browser) | +| [`@effect/platform-bun`](packages/platform/bun) | Platform services for [Bun](https://bun.sh) | [docs](https://effect.website/docs/v4/api/platform-bun) | +| [`@effect/platform-deno`](packages/platform/deno) | Platform services for [Deno](https://deno.com) | [docs](https://effect.website/docs/v4/api/platform-deno) | +| [`@effect/platform-node`](packages/platform/node) | Platform services for [Node.js](https://nodejs.org) | [docs](https://effect.website/docs/v4/api/platform-node) | +| [`@effect/platform-node-shared`](packages/platform/node-shared) | Shared services for Node.js-compatible runtimes | [docs](https://effect.website/docs/v4/api/platform-node-shared) | +| [`@effect/sql-clickhouse`](packages/sql/clickhouse) | SQL client for [ClickHouse](https://clickhouse.com) | [docs](https://effect.website/docs/v4/api/sql-clickhouse) | +| [`@effect/sql-d1`](packages/sql/d1) | SQL client for Cloudflare D1 | [docs](https://effect.website/docs/v4/api/sql-d1) | +| [`@effect/sql-libsql`](packages/sql/libsql) | SQL client for libSQL | [docs](https://effect.website/docs/v4/api/sql-libsql) | +| [`@effect/sql-mssql`](packages/sql/mssql) | SQL client for Microsoft SQL Server | [docs](https://effect.website/docs/v4/api/sql-mssql) | +| [`@effect/sql-mysql2`](packages/sql/mysql2) | SQL client for MySQL | [docs](https://effect.website/docs/v4/api/sql-mysql2) | +| [`@effect/sql-pg`](packages/sql/pg) | SQL client for PostgreSQL | [docs](https://effect.website/docs/v4/api/sql-pg) | +| [`@effect/sql-pglite`](packages/sql/pglite) | SQL client for [PGlite](https://pglite.dev) | [docs](https://effect.website/docs/v4/api/sql-pglite) | +| [`@effect/sql-sqlite-bun`](packages/sql/sqlite-bun) | SQL client for SQLite via `bun:sqlite` | [docs](https://effect.website/docs/v4/api/sql-sqlite-bun) | +| [`@effect/sql-sqlite-do`](packages/sql/sqlite-do) | SQL client for Cloudflare Durable Objects SQLite | [docs](https://effect.website/docs/v4/api/sql-sqlite-do) | +| [`@effect/sql-sqlite-node`](packages/sql/sqlite-node) | SQL client for SQLite via `node:sqlite` | [docs](https://effect.website/docs/v4/api/sql-sqlite-node) | +| [`@effect/sql-sqlite-react-native`](packages/sql/sqlite-react-native) | SQL client for SQLite in React Native | [docs](https://effect.website/docs/v4/api/sql-sqlite-react-native) | +| [`@effect/sql-sqlite-wasm`](packages/sql/sqlite-wasm) | SQL client for SQLite compiled to WebAssembly | [docs](https://effect.website/docs/v4/api/sql-sqlite-wasm) | +| [`@effect/ai-anthropic`](packages/ai/anthropic) | Anthropic provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-anthropic) | +| [`@effect/ai-openai`](packages/ai/openai) | OpenAI provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openai) | +| [`@effect/ai-cloudflare`](packages/ai/cloudflare) | Cloudflare decision provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-cloudflare) | +| [`@effect/ai-typesafe`](packages/ai/typesafe) | TypeSafe decision provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-typesafe) | +| [`@effect/ai-openai-compat`](packages/ai/openai-compat) | OpenAI-compatible API provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openai-compat) | +| [`@effect/ai-openrouter`](packages/ai/openrouter) | OpenRouter provider for the Effect AI modules | [docs](https://effect.website/docs/v4/api/ai-openrouter) | +| [`@effect/atom-react`](packages/atom/react) | React bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-react) | +| [`@effect/atom-solid`](packages/atom/solid) | SolidJS bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-solid) | +| [`@effect/atom-vue`](packages/atom/vue) | Vue bindings for Effect Atom | [docs](https://effect.website/docs/v4/api/atom-vue) | +| [`@effect/opentelemetry`](packages/opentelemetry) | [OpenTelemetry](https://opentelemetry.io) integration | [docs](https://effect.website/docs/v4/api/opentelemetry) | +| [`@effect/vitest`](packages/vitest) | Helpers for testing with [Vitest](https://vitest.dev) | [docs](https://effect.website/docs/v4/api/vitest) | +| [`@effect/docgen`](packages/tools/docgen) | Documentation generator for Effect projects | [docs](https://effect.website/docs/v4/api/docgen) | +| [`@effect/doctest`](packages/tools/doctest) | Runs JSDoc examples as Vitest tests | [docs](https://effect.website/docs/v4/api/doctest) | +| [`@effect/openapi-generator`](packages/tools/openapi-generator) | Generate Effect code from OpenAPI specifications | [docs](https://effect.website/docs/v4/api/openapi-generator) | +| [`@effect/graphql-generator`](packages/tools/graphql-generator) | Generate Effect code from GraphQL documents | [docs](https://effect.website/docs/v4/api/graphql-generator) | + +## License + +MIT diff --git a/repos/effect/ai-docs/README.md b/ai-docs/README.md similarity index 100% rename from repos/effect/ai-docs/README.md rename to ai-docs/README.md diff --git a/repos/effect/ai-docs/package.json b/ai-docs/package.json similarity index 95% rename from repos/effect/ai-docs/package.json rename to ai-docs/package.json index 1c21dce10..13ef4319f 100644 --- a/repos/effect/ai-docs/package.json +++ b/ai-docs/package.json @@ -27,8 +27,8 @@ "@effect/sql-sqlite-wasm": "workspace:*", "@effect/vitest": "workspace:*", "effect": "workspace:*", - "hono": "^4.13.12", - "nodemailer": "^10.0.14" + "hono": "^4.13.13", + "nodemailer": "^10.1.0" }, "devDependencies": { "@types/nodemailer": "^8.0.2" diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/01_effect-gen.ts b/ai-docs/src/01_effect/01_basics/01_effect-gen.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/01_effect-gen.ts rename to ai-docs/src/01_effect/01_basics/01_effect-gen.ts diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/02_effect-fn.ts b/ai-docs/src/01_effect/01_basics/02_effect-fn.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/02_effect-fn.ts rename to ai-docs/src/01_effect/01_basics/02_effect-fn.ts diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/10_creating-effects.ts b/ai-docs/src/01_effect/01_basics/10_creating-effects.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/10_creating-effects.ts rename to ai-docs/src/01_effect/01_basics/10_creating-effects.ts diff --git a/repos/effect/ai-docs/src/01_effect/01_basics/index.md b/ai-docs/src/01_effect/01_basics/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/01_basics/index.md rename to ai-docs/src/01_effect/01_basics/index.md diff --git a/repos/effect/ai-docs/src/01_effect/02_schema/10_schema-basics.ts b/ai-docs/src/01_effect/02_schema/10_schema-basics.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/02_schema/10_schema-basics.ts rename to ai-docs/src/01_effect/02_schema/10_schema-basics.ts diff --git a/repos/effect/ai-docs/src/01_effect/02_schema/index.md b/ai-docs/src/01_effect/02_schema/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/02_schema/index.md rename to ai-docs/src/01_effect/02_schema/index.md diff --git a/repos/effect/ai-docs/src/01_effect/03_services/01_service.ts b/ai-docs/src/01_effect/03_services/01_service.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/01_service.ts rename to ai-docs/src/01_effect/03_services/01_service.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/10_reference.ts b/ai-docs/src/01_effect/03_services/10_reference.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/10_reference.ts rename to ai-docs/src/01_effect/03_services/10_reference.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts b/ai-docs/src/01_effect/03_services/20_layer-composition.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/20_layer-composition.ts rename to ai-docs/src/01_effect/03_services/20_layer-composition.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts b/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts rename to ai-docs/src/01_effect/03_services/20_layer-unwrap.ts diff --git a/repos/effect/ai-docs/src/01_effect/03_services/index.md b/ai-docs/src/01_effect/03_services/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/03_services/index.md rename to ai-docs/src/01_effect/03_services/index.md diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/01_error-handling.ts b/ai-docs/src/01_effect/04_errors/01_error-handling.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/01_error-handling.ts rename to ai-docs/src/01_effect/04_errors/01_error-handling.ts diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/10_catch-tags.ts b/ai-docs/src/01_effect/04_errors/10_catch-tags.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/10_catch-tags.ts rename to ai-docs/src/01_effect/04_errors/10_catch-tags.ts diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/20_reason-errors.ts b/ai-docs/src/01_effect/04_errors/20_reason-errors.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/20_reason-errors.ts rename to ai-docs/src/01_effect/04_errors/20_reason-errors.ts diff --git a/repos/effect/ai-docs/src/01_effect/04_errors/index.md b/ai-docs/src/01_effect/04_errors/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/04_errors/index.md rename to ai-docs/src/01_effect/04_errors/index.md diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/10_acquire-release.ts b/ai-docs/src/01_effect/05_resources/10_acquire-release.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/10_acquire-release.ts rename to ai-docs/src/01_effect/05_resources/10_acquire-release.ts diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts b/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts rename to ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/30_layer-map.ts b/ai-docs/src/01_effect/05_resources/30_layer-map.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/30_layer-map.ts rename to ai-docs/src/01_effect/05_resources/30_layer-map.ts diff --git a/repos/effect/ai-docs/src/01_effect/05_resources/index.md b/ai-docs/src/01_effect/05_resources/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/05_resources/index.md rename to ai-docs/src/01_effect/05_resources/index.md diff --git a/repos/effect/ai-docs/src/01_effect/06_running/10_run-main.ts b/ai-docs/src/01_effect/06_running/10_run-main.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/06_running/10_run-main.ts rename to ai-docs/src/01_effect/06_running/10_run-main.ts diff --git a/repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts b/ai-docs/src/01_effect/06_running/20_layer-launch.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/06_running/20_layer-launch.ts rename to ai-docs/src/01_effect/06_running/20_layer-launch.ts diff --git a/repos/effect/ai-docs/src/01_effect/06_running/index.md b/ai-docs/src/01_effect/06_running/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/06_running/index.md rename to ai-docs/src/01_effect/06_running/index.md diff --git a/repos/effect/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts b/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts similarity index 100% rename from repos/effect/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts rename to ai-docs/src/01_effect/07_pubsub/10_pubsub.ts diff --git a/repos/effect/ai-docs/src/01_effect/07_pubsub/index.md b/ai-docs/src/01_effect/07_pubsub/index.md similarity index 100% rename from repos/effect/ai-docs/src/01_effect/07_pubsub/index.md rename to ai-docs/src/01_effect/07_pubsub/index.md diff --git a/repos/effect/ai-docs/src/03_stream/10_creating-streams.ts b/ai-docs/src/03_stream/10_creating-streams.ts similarity index 100% rename from repos/effect/ai-docs/src/03_stream/10_creating-streams.ts rename to ai-docs/src/03_stream/10_creating-streams.ts diff --git a/repos/effect/ai-docs/src/03_stream/20_consuming-streams.ts b/ai-docs/src/03_stream/20_consuming-streams.ts similarity index 100% rename from repos/effect/ai-docs/src/03_stream/20_consuming-streams.ts rename to ai-docs/src/03_stream/20_consuming-streams.ts diff --git a/repos/effect/ai-docs/src/03_stream/30_encoding.ts b/ai-docs/src/03_stream/30_encoding.ts similarity index 100% rename from repos/effect/ai-docs/src/03_stream/30_encoding.ts rename to ai-docs/src/03_stream/30_encoding.ts diff --git a/repos/effect/ai-docs/src/03_stream/index.md b/ai-docs/src/03_stream/index.md similarity index 100% rename from repos/effect/ai-docs/src/03_stream/index.md rename to ai-docs/src/03_stream/index.md diff --git a/repos/effect/ai-docs/src/04_integration/10_managed-runtime.ts b/ai-docs/src/04_integration/10_managed-runtime.ts similarity index 100% rename from repos/effect/ai-docs/src/04_integration/10_managed-runtime.ts rename to ai-docs/src/04_integration/10_managed-runtime.ts diff --git a/repos/effect/ai-docs/src/04_integration/index.md b/ai-docs/src/04_integration/index.md similarity index 100% rename from repos/effect/ai-docs/src/04_integration/index.md rename to ai-docs/src/04_integration/index.md diff --git a/repos/effect/ai-docs/src/05_batching/10_request-resolver.ts b/ai-docs/src/05_batching/10_request-resolver.ts similarity index 100% rename from repos/effect/ai-docs/src/05_batching/10_request-resolver.ts rename to ai-docs/src/05_batching/10_request-resolver.ts diff --git a/repos/effect/ai-docs/src/05_batching/index.md b/ai-docs/src/05_batching/index.md similarity index 100% rename from repos/effect/ai-docs/src/05_batching/index.md rename to ai-docs/src/05_batching/index.md diff --git a/repos/effect/ai-docs/src/06_schedule/10_schedules.ts b/ai-docs/src/06_schedule/10_schedules.ts similarity index 100% rename from repos/effect/ai-docs/src/06_schedule/10_schedules.ts rename to ai-docs/src/06_schedule/10_schedules.ts diff --git a/repos/effect/ai-docs/src/06_schedule/index.md b/ai-docs/src/06_schedule/index.md similarity index 100% rename from repos/effect/ai-docs/src/06_schedule/index.md rename to ai-docs/src/06_schedule/index.md diff --git a/repos/effect/ai-docs/src/07_datetime/10_creating-and-formatting.ts b/ai-docs/src/07_datetime/10_creating-and-formatting.ts similarity index 100% rename from repos/effect/ai-docs/src/07_datetime/10_creating-and-formatting.ts rename to ai-docs/src/07_datetime/10_creating-and-formatting.ts diff --git a/repos/effect/ai-docs/src/07_datetime/20_time-zones.ts b/ai-docs/src/07_datetime/20_time-zones.ts similarity index 100% rename from repos/effect/ai-docs/src/07_datetime/20_time-zones.ts rename to ai-docs/src/07_datetime/20_time-zones.ts diff --git a/repos/effect/ai-docs/src/07_datetime/index.md b/ai-docs/src/07_datetime/index.md similarity index 100% rename from repos/effect/ai-docs/src/07_datetime/index.md rename to ai-docs/src/07_datetime/index.md diff --git a/repos/effect/ai-docs/src/08_observability/10_logging.ts b/ai-docs/src/08_observability/10_logging.ts similarity index 100% rename from repos/effect/ai-docs/src/08_observability/10_logging.ts rename to ai-docs/src/08_observability/10_logging.ts diff --git a/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts b/ai-docs/src/08_observability/20_otlp-tracing.ts similarity index 100% rename from repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts rename to ai-docs/src/08_observability/20_otlp-tracing.ts diff --git a/repos/effect/ai-docs/src/08_observability/index.md b/ai-docs/src/08_observability/index.md similarity index 100% rename from repos/effect/ai-docs/src/08_observability/index.md rename to ai-docs/src/08_observability/index.md diff --git a/repos/effect/ai-docs/src/09_testing/10_effect-tests.ts b/ai-docs/src/09_testing/10_effect-tests.ts similarity index 100% rename from repos/effect/ai-docs/src/09_testing/10_effect-tests.ts rename to ai-docs/src/09_testing/10_effect-tests.ts diff --git a/repos/effect/ai-docs/src/09_testing/20_layer-tests.ts b/ai-docs/src/09_testing/20_layer-tests.ts similarity index 100% rename from repos/effect/ai-docs/src/09_testing/20_layer-tests.ts rename to ai-docs/src/09_testing/20_layer-tests.ts diff --git a/repos/effect/ai-docs/src/09_testing/index.md b/ai-docs/src/09_testing/index.md similarity index 100% rename from repos/effect/ai-docs/src/09_testing/index.md rename to ai-docs/src/09_testing/index.md diff --git a/repos/effect/ai-docs/src/10_predicate/01_basics.ts b/ai-docs/src/10_predicate/01_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/10_predicate/01_basics.ts rename to ai-docs/src/10_predicate/01_basics.ts diff --git a/repos/effect/ai-docs/src/10_predicate/index.md b/ai-docs/src/10_predicate/index.md similarity index 100% rename from repos/effect/ai-docs/src/10_predicate/index.md rename to ai-docs/src/10_predicate/index.md diff --git a/repos/effect/ai-docs/src/40_sql/10_basics.ts b/ai-docs/src/40_sql/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/40_sql/10_basics.ts rename to ai-docs/src/40_sql/10_basics.ts diff --git a/repos/effect/ai-docs/src/40_sql/index.md b/ai-docs/src/40_sql/index.md similarity index 100% rename from repos/effect/ai-docs/src/40_sql/index.md rename to ai-docs/src/40_sql/index.md diff --git a/repos/effect/ai-docs/src/50_http-client/10_basics.ts b/ai-docs/src/50_http-client/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/50_http-client/10_basics.ts rename to ai-docs/src/50_http-client/10_basics.ts diff --git a/repos/effect/ai-docs/src/50_http-client/index.md b/ai-docs/src/50_http-client/index.md similarity index 100% rename from repos/effect/ai-docs/src/50_http-client/index.md rename to ai-docs/src/50_http-client/index.md diff --git a/repos/effect/ai-docs/src/51_http-server/10_basics.ts b/ai-docs/src/51_http-server/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/10_basics.ts rename to ai-docs/src/51_http-server/10_basics.ts diff --git a/repos/effect/ai-docs/src/51_http-server/20_testing.ts b/ai-docs/src/51_http-server/20_testing.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/20_testing.ts rename to ai-docs/src/51_http-server/20_testing.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts b/ai-docs/src/51_http-server/fixtures/api/Api.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts rename to ai-docs/src/51_http-server/fixtures/api/Api.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts b/ai-docs/src/51_http-server/fixtures/api/Authorization.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts rename to ai-docs/src/51_http-server/fixtures/api/Authorization.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts b/ai-docs/src/51_http-server/fixtures/api/System.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts rename to ai-docs/src/51_http-server/fixtures/api/System.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts b/ai-docs/src/51_http-server/fixtures/api/Users.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts rename to ai-docs/src/51_http-server/fixtures/api/Users.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts b/ai-docs/src/51_http-server/fixtures/domain/User.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts rename to ai-docs/src/51_http-server/fixtures/domain/User.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts b/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts rename to ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Authorization.ts b/ai-docs/src/51_http-server/fixtures/server/Authorization.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/server/Authorization.ts rename to ai-docs/src/51_http-server/fixtures/server/Authorization.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts b/ai-docs/src/51_http-server/fixtures/server/Users.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts rename to ai-docs/src/51_http-server/fixtures/server/Users.ts diff --git a/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts b/ai-docs/src/51_http-server/fixtures/server/Users/http.ts similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts rename to ai-docs/src/51_http-server/fixtures/server/Users/http.ts diff --git a/repos/effect/ai-docs/src/51_http-server/index.md b/ai-docs/src/51_http-server/index.md similarity index 100% rename from repos/effect/ai-docs/src/51_http-server/index.md rename to ai-docs/src/51_http-server/index.md diff --git a/ai-docs/src/52_graphql/10_basics.ts b/ai-docs/src/52_graphql/10_basics.ts new file mode 100644 index 000000000..1c4ff992d --- /dev/null +++ b/ai-docs/src/52_graphql/10_basics.ts @@ -0,0 +1,117 @@ +/** + * @title Getting started with GraphQL clients + * + * Define a query, build a typed client service over HTTP with an auth + * middleware, and page through a cursor connection. + */ +import { Config, Context, Effect, Layer, Redacted, Schema, Stream } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLMiddleware, GraphQLProtocol } from "effect/graphql" +import { FetchHttpClient } from "effect/http" + +// Operations are usually generated: write them in `.graphql` files and run +// `graphqlgen` from `@effect/graphql-generator`, which writes a `.graphql.ts` +// module like the one below next to each document. +// +// query RepoIssues($owner: String!, $name: String!, $after: String) { +// repository(owner: $owner, name: $name) { +// issues(first: 50, after: $after) { +// pageInfo { hasNextPage endCursor } +// nodes { number title } +// } +// } +// } +// +// Each nested object selection has a Schema.Struct const and a same-named +// interface for its decoded type. The const handles decoding, encoding and +// `make`; use the interface in explicit type annotations when a named type +// is useful. Inferred types and editor hovers can expand structurally rather +// than showing the names, unlike the previous Schema.Opaque classes. +export const PageInfo = Schema.Struct({ + hasNextPage: Schema.Boolean, + endCursor: Schema.NullOr(Schema.String) +}) +export interface PageInfo extends Schema.Struct.Type {} + +export const Issue = Schema.Struct({ number: Schema.Int, title: Schema.String }) +export interface Issue extends Schema.Struct.Type {} + +export const IssueConnection = Schema.Struct({ + pageInfo: PageInfo, + nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Issue))) +}) +export interface IssueConnection extends Schema.Struct.Type {} + +export const Repository = Schema.Struct({ issues: IssueConnection }) +export interface Repository extends Schema.Struct.Type {} + +export const RepoIssues = GraphQL.query("RepoIssues", { + document: + "query RepoIssues($owner:String!,$name:String!,$after:String){repository(owner:$owner,name:$name){issues(first:50,after:$after){pageInfo{hasNextPage endCursor}nodes{number title}}}}", + variables: { + owner: Schema.String, + name: Schema.String, + after: Schema.optional(Schema.NullOr(Schema.String)) + }, + result: Schema.Struct({ + repository: Schema.NullOr(Repository) + }) +}) + +// A middleware is a service that wraps every operation it is attached to. +// `mapRequest` builds one from a single request transform. +export class GitHubAuth extends GraphQLMiddleware.Service()("app/GitHubAuth") { + static readonly layer = Layer.effect( + GitHubAuth, + Effect.gen(function*() { + const token = yield* Config.Redacted("GITHUB_TOKEN") + return GraphQLMiddleware.mapRequest((request) => + Effect.succeed({ + ...request, + headers: { ...request.headers, authorization: `Bearer ${Redacted.value(token)}` } + }) + ) + }) + ) +} + +// A group is the set of operations a client exposes; the generator emits one +// per file. Attaching the middleware makes `GraphQLClient.make` require it. +export const GitHubGraphQLGroup = GraphQLGroup.make(RepoIssues).middleware(GitHubAuth) + +// The client has one method per operation, named after it. +export class GitHub extends Context.Service< + GitHub, + GraphQLClient.FromGroup +>()("app/GitHub") { + static readonly layerTest = GraphQLClient.layerTest(GitHub, GitHubGraphQLGroup) + + static readonly layer = Layer.effect(GitHub, GraphQLClient.make(GitHubGraphQLGroup)).pipe( + Layer.provide(GitHubAuth.layer), + // Queries and mutations are sent as `POST`. Subscriptions use graphql-sse + // here, or graphql-ws with `GraphQLProtocol.layerWebSocket`. + Layer.provide(GraphQLProtocol.layerHttp({ url: "https://api.github.com/graphql" })), + Layer.provide(FetchHttpClient.layer) + ) +} + +export const program = Effect.gen(function*() { + const github = yield* GitHub + + // Variables are encoded and the result decoded through the Schemas. Any + // `errors` in the response fail with a `GraphQLClientError`; pass + // `{ partial: true }` as the second argument to get them with the data. + const { repository } = yield* github.RepoIssues({ owner: "Effect-TS", name: "effect" }) + yield* Effect.log(`First page has ${repository?.issues.nodes?.length ?? 0} issues`) + + // `items` pages through the connection by driving `$after`, one request at + // a time and only as the stream is pulled. + const titles = yield* GraphQLClient.items(github.RepoIssues, { + variables: { owner: "Effect-TS", name: "effect" }, + connection: (result) => result.repository?.issues + }).pipe( + Stream.take(120), + Stream.map((issue) => issue?.title), + Stream.runCollect + ) + yield* Effect.log(`Fetched ${titles.length} issues`) +}).pipe(Effect.provide(GitHub.layer)) diff --git a/ai-docs/src/52_graphql/20_testing.ts b/ai-docs/src/52_graphql/20_testing.ts new file mode 100644 index 000000000..0ea9c6e25 --- /dev/null +++ b/ai-docs/src/52_graphql/20_testing.ts @@ -0,0 +1,45 @@ +/** + * @title Testing code that uses a GraphQL client + * + * Replace a client service with `GraphQLClient.layerTest`, answering each + * operation from a handler instead of a server. + */ +import { assert, describe, it } from "@effect/vitest" +import { Effect } from "effect" +import { GitHub } from "./10_basics.ts" + +// Code under test that depends on the `GitHub` client service. +const issueTitles = (owner: string, name: string) => + GitHub.use((github) => github.RepoIssues({ owner, name })).pipe( + Effect.map(({ repository }) => repository?.issues.nodes?.map((issue) => issue?.title) ?? []) + ) + +// The service binds `GraphQLClient.layerTest(GitHub, GitHubGraphQLGroup)` once +// as a static helper. Handlers are keyed by operation name, receive the +// caller's variables and options, and return results in their decoded form. +// Nothing is encoded or decoded and no middleware runs. An operation without +// a handler dies with an `UnimplementedError` defect. +const GitHubTest = GitHub.layerTest({ + RepoIssues: ({ owner, name }) => + Effect.succeed({ + repository: { + issues: { + pageInfo: { hasNextPage: false, endCursor: null }, + nodes: [{ number: 1, title: `Hello from ${owner}/${name}` }] + } + } + }) +}) + +describe("issueTitles", () => { + it.effect("returns the titles of the first page", () => + Effect.gen(function*() { + const titles = yield* issueTitles("Effect-TS", "effect") + assert.deepStrictEqual(titles, ["Hello from Effect-TS/effect"]) + }).pipe(Effect.provide(GitHubTest))) +}) + +// `layerTest` uses `makeTest`, which skips the protocol and cannot return +// partial data together with `errors`. To test that, or the encode and decode +// path, build the real client with `GraphQLClient.make` over a scripted +// `GraphQLProtocol` layer (see `GraphQLProtocol`). diff --git a/ai-docs/src/52_graphql/30_batching.ts b/ai-docs/src/52_graphql/30_batching.ts new file mode 100644 index 000000000..44242c1ce --- /dev/null +++ b/ai-docs/src/52_graphql/30_batching.ts @@ -0,0 +1,118 @@ +/** + * @title Batching lookups with GraphQLResolver + * + * Collect concurrent lookups by id into one `nodes(ids:)` request with + * `GraphQLResolver`, instead of one request per id. + */ +import { type Cause, Context, Effect, Layer, RequestResolver, Schema } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol, GraphQLResolver } from "effect/graphql" +import { FetchHttpClient } from "effect/http" +import { GitHubAuth } from "./10_basics.ts" + +// The generator's output for `repo-nodes.graphql`: +// +// query RepoNodes($ids: [ID!]!) { +// nodes(ids: $ids) { __typename ... on Repository { nameWithOwner } } +// } +// +// Generated modules import shared codecs and the possible `__typename`s of +// each interface from the module the generator writes next to the schema +// (`import * as Shared from "../schema.graphql.ts"`); here `Shared` is +// declared inline. +const Shared = { ID: Schema.String } +declare namespace Shared { + export namespace Typename { + export type Node = "Issue" | "PullRequest" | "Repository" | "User" + } +} + +export const Repository = Schema.Struct({ + __typename: Schema.Literal("Repository"), + nameWithOwner: Schema.String +}) +export interface Repository extends Schema.Struct.Type {} + +// Unselected node types are represented by `otherTypename`. +export const NodeOther = Schema.Struct({ + __typename: GraphQL.otherTypename()(["Repository"]) +}) +export interface NodeOther extends Schema.Struct.Type {} + +/** `query RepoNodes` */ +export const RepoNodes = GraphQL.query("RepoNodes", { + document: { + variables: [ + { name: "ids", body: "[ID!]!" } + ], + selections: [ + { name: "nodes", body: ["(ids:$", "ids", "){__typename...on Repository{nameWithOwner}}"] } + ], + fragments: [] + }, + variables: { + ids: Schema.Array(Shared.ID) + }, + result: Schema.Struct({ + nodes: Schema.Array(Schema.NullOr(Schema.Union([ + Repository, + NodeOther + ]))) + }) +}) +export declare namespace RepoNodes { + export type Variables = typeof RepoNodes.variables.Type + export type Result = typeof RepoNodes.result.Type +} + +/** Every operation in repo-nodes.graphql. */ +export const RepoNodesGraphQLGroup = GraphQLGroup.make(RepoNodes) + +// Your code: attach the middleware to the generated group. +export const GitHubGraphQLGroup = RepoNodesGraphQLGroup.middleware(GitHubAuth) + +export class GitHub extends Context.Service< + GitHub, + GraphQLClient.FromGroup & { + getRepo(id: string): Effect.Effect< + Repository, + GraphQL.Error | Cause.NoSuchElementError + > + } +>()("app/GitHub") { + static readonly layer = Layer.effect( + GitHub, + Effect.gen(function*() { + const client = yield* GraphQLClient.make(GitHubGraphQLGroup) + + // Null entries and non-Repository nodes fail with NoSuchElementError. + // GitHub accepts at most 100 ids per request. + // + // GitHubAuth reads one token from config, so every lookup can share a + // batch. If a middleware `requires` a per-request service, such as the + // signed-in user, `batchKey` is required and keeps lookups for different + // users in separate calls: + // + // batchKey: (context) => Context.get(context, CurrentUser).id + const RepoById = GraphQLResolver.ordered(client.RepoNodes, { + variables: (ids: ReadonlyArray) => ({ ids }), + path: ["nodes"], + typename: "Repository" + }).pipe(RequestResolver.batchN(100)) + return { ...client, getRepo: GraphQLResolver.request(RepoById) } + }) + ).pipe( + Layer.provide(GitHubAuth.layer), + Layer.provide(GraphQLProtocol.layerHttp({ url: "https://api.github.com/graphql" })), + Layer.provide(FetchHttpClient.layer) + ) +} + +export const repoNames = Effect.fn("repoNames")(function*(ids: ReadonlyArray) { + const github = yield* GitHub + const repos = yield* Effect.forEach(ids, github.getRepo, { concurrency: "unbounded" }) + return repos.map((repo) => repo.nameWithOwner) +}) + +export const program = repoNames(["R_1", "R_2"]).pipe( + Effect.provide(GitHub.layer) +) diff --git a/ai-docs/src/52_graphql/40_aliased-batching.ts b/ai-docs/src/52_graphql/40_aliased-batching.ts new file mode 100644 index 000000000..45e0325a9 --- /dev/null +++ b/ai-docs/src/52_graphql/40_aliased-batching.ts @@ -0,0 +1,124 @@ +/** + * @title Batching single-item lookups with aliases + * + * When an API has no field that takes a list of keys, `GraphQLResolver.aliased` + * repeats a generated operation under aliases, so concurrent lookups share one + * request and keep their generated types. + */ +import { Context, Effect, Layer, Schema } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol, GraphQLResolver } from "effect/graphql" +import { FetchHttpClient } from "effect/http" +import { GitHubAuth } from "./10_basics.ts" + +// The generator's output for `pr.graphql`: +// +// query PullRequest($owner: String!, $name: String!, $number: Int!) { +// repository(owner: $owner, name: $name) { +// pullRequest(number: $number) { number title } +// } +// } +// +// Generated modules import shared codecs, such as `Int`, from the module the +// generator writes next to the schema (`import * as Shared from +// "../schema.graphql.ts"`); here `Shared` is declared inline. +const Shared = { Int: Schema.Number.check(Schema.isInt32()) } + +export const PullRequestPullRequest = Schema.Struct({ + number: Shared.Int, + title: Schema.String +}) +export interface PullRequestPullRequest extends Schema.Struct.Type {} + +export const Repository = Schema.Struct({ + pullRequest: Schema.NullOr(PullRequestPullRequest) +}) +export interface Repository extends Schema.Struct.Type {} + +// The generator gives queries and mutations their document as a +// `GraphQL.DocumentTemplate`. The odd entries of each `body` are the names +// `aliased` renames per slice; joined together they are the printed document. +/** `query PullRequest` */ +export const PullRequest = GraphQL.query("PullRequest", { + document: { + variables: [ + { name: "owner", body: "String!" }, + { name: "name", body: "String!" }, + { name: "number", body: "Int!" } + ], + selections: [ + { + name: "repository", + body: ["(owner:$", "owner", " name:$", "name", "){pullRequest(number:$", "number", "){number title}}"] + } + ], + fragments: [] + }, + variables: { + owner: Schema.String, + name: Schema.String, + number: Shared.Int + }, + result: Schema.Struct({ + repository: Schema.NullOr(Repository) + }) +}) +export declare namespace PullRequest { + export type Variables = typeof PullRequest.variables.Type + export type Result = typeof PullRequest.result.Type +} + +/** Every operation in pr.graphql. */ +export const PrGraphQLGroup = GraphQLGroup.make(PullRequest) + +// Your code: attach the middleware to the generated group. +export const GitHubGraphQLGroup = PrGraphQLGroup.middleware(GitHubAuth) + +export class GitHub extends Context.Service< + GitHub, + GraphQLClient.FromGroup & { + readonly getPullRequest: ( + variables: GraphQL.Variables + ) => Effect.Effect, GraphQL.Error> + } +>()("app/GitHub") { + static readonly layer = Layer.effect( + GitHub, + Effect.gen(function*() { + const client = yield* GraphQLClient.make(GitHubGraphQLGroup) + + // Concurrent calls go out as one POST whose root fields are aliased + // `_0_repository`, `_1_repository`, and so on. Each slice is decoded + // with the generated result Schema, and a GraphQL error under + // `_1_repository` fails only that call. + // + // A batch holds 10 slices by default, because GitHub's query cost and + // timeouts grow with the size of the query. Pipe + // `RequestResolver.batchN` to change it. + const PullRequests = GraphQLResolver.aliased(client.PullRequest) + + // `request` turns the resolver into a lookup by the operation's + // variables, as for `ordered` and `findById`. + return { ...client, getPullRequest: GraphQLResolver.request(PullRequests) } + }) + ).pipe( + Layer.provide(GitHubAuth.layer), + Layer.provide(GraphQLProtocol.layerHttp({ url: "https://api.github.com/graphql" })), + Layer.provide(FetchHttpClient.layer) + ) +} + +export const pullRequestTitles = Effect.fn("pullRequestTitles")(function*(numbers: ReadonlyArray) { + const github = yield* GitHub + const results = yield* Effect.forEach( + numbers, + (number) => github.getPullRequest({ owner: "Effect-TS", name: "effect", number }), + { concurrency: "unbounded" } + ) + // `repository` and `pullRequest` are nullable, so a missing pull request is + // `null` rather than an error that would fail every slice. + return results.map((result) => result.repository?.pullRequest?.title ?? null) +}) + +export const program = pullRequestTitles([1, 2, 3]).pipe( + Effect.provide(GitHub.layer) +) diff --git a/ai-docs/src/52_graphql/50_errors-and-retries.ts b/ai-docs/src/52_graphql/50_errors-and-retries.ts new file mode 100644 index 000000000..72f6cdfa1 --- /dev/null +++ b/ai-docs/src/52_graphql/50_errors-and-retries.ts @@ -0,0 +1,74 @@ +/** + * @title Handling GraphQL errors, retries and rate limits + * + * Read rate-limit headers in a middleware, tell GraphQL errors apart by their + * fields, and retry timeouts and rate limits with `Effect.retry`. + */ +import { Duration, Effect, Layer, Schedule } from "effect" +import { type GraphQLClientError, GraphQLMiddleware } from "effect/graphql" +import { GitHub } from "./10_basics.ts" + +// Middleware see each response's HTTP `status` and `headers` on the result +// `next` returns, including responses with GraphQL `errors`. A failed call +// keeps them, and `GraphQLClientError` exposes them as `headers` and +// `status`. Attach it with `GraphQLGroup.make(...).middleware(RateLimit)`. +export class RateLimit extends GraphQLMiddleware.Service()("app/RateLimit") { + static readonly layer = Layer.succeed(RateLimit, { + execute: ({ next, request }) => + Effect.tap(next(request), (result) => { + const remaining = Number(result.headers?.["x-ratelimit-remaining"]) + return remaining < 500 + ? Effect.logWarning(`GitHub rate limit: ${remaining} points left`) + : Effect.void + }), + subscribe: ({ next, request }) => next(request) + }) +} + +// GraphQL errors keep fields the specification doesn't define as JSON, such +// as GitHub's top-level `type` (`NOT_FOUND`, `FORBIDDEN`, `RATE_LIMITED`). +export const hasErrorType = (error: GraphQLClientError.GraphQLClientError, type: string) => + error.reason._tag === "ResponseError" && error.reason.errors.every((entry) => entry.type === type) + +// `isRetryable` is true for network failures, for 429 or 5xx responses, and +// for any error with a `Retry-After` header, whatever its status. That covers +// GitHub's 502 and 504 query timeouts, even with a GraphQL `errors` body, and +// its secondary rate limits (403 or 429 with `retry-after`). A primary rate +// limit can come without `retry-after`, with `x-ratelimit-remaining: 0`, so +// check that header too. +const rateLimited = (error: GraphQLClientError.GraphQLClientError) => error.headers?.["x-ratelimit-remaining"] === "0" + +// Wait for `retryAfter` when it is set, and until `x-ratelimit-reset` (epoch +// seconds) on a primary rate limit. GitHub may ban an integration that keeps +// retrying while rate limited, so don't retry with a plain backoff. +const gitHubRetry = Schedule.exponential("1 second").pipe( + Schedule.setInputType(), + Schedule.modifyDelay(({ duration, input, now }) => { + if (input.retryAfter !== undefined) return Effect.succeed(input.retryAfter) + if (!rateLimited(input)) return Effect.succeed(duration) + const reset = Number(input.headers?.["x-ratelimit-reset"]) * 1000 + return Effect.succeed(Number.isFinite(reset) ? Duration.millis(Math.max(0, reset - now)) : Duration.minutes(1)) + }) +) + +// The client never retries queries or mutations on its own. Retry only +// mutations that are safe to send twice, and with `GraphQLClient.pages`, +// retry inside a middleware, because `Stream.retry` starts again from the +// first page. Subscriptions reconnect on their own after a retryable failure, +// on `GraphQLClient.defaultSubscriptionRetry`, which waits for `retryAfter`. +// A primary rate limit without `retry-after` isn't retryable, so it ends the +// subscription; wait until `x-ratelimit-reset` before subscribing again. +export const retryGitHub = (effect: Effect.Effect) => + Effect.retry(effect, { + schedule: gitHubRetry, + while: (error) => error.isRetryable || rateLimited(error), + times: 5 + }) + +export const program = Effect.gen(function*() { + const github = yield* GitHub + const result = yield* retryGitHub(github.RepoIssues({ owner: "Effect-TS", name: "missing" })).pipe( + Effect.catchIf((error) => hasErrorType(error, "NOT_FOUND"), () => Effect.succeed(null)) + ) + yield* Effect.log(result === null ? "No such repository" : `${result.repository?.issues.nodes?.length} issues`) +}).pipe(Effect.provide(GitHub.layer)) diff --git a/ai-docs/src/52_graphql/index.md b/ai-docs/src/52_graphql/index.md new file mode 100644 index 000000000..d811a6604 --- /dev/null +++ b/ai-docs/src/52_graphql/index.md @@ -0,0 +1,6 @@ +## Typed GraphQL clients with `effect/graphql` + +Use the `effect/graphql` modules to call GraphQL APIs with typed +operations. Run `graphqlgen` from `@effect/graphql-generator` to generate +operations from `.graphql` documents, then build a client with +`GraphQLClient.make` and provide a transport from `GraphQLProtocol`. diff --git a/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts b/ai-docs/src/60_child-process/10_working-with-child-processes.ts similarity index 100% rename from repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts rename to ai-docs/src/60_child-process/10_working-with-child-processes.ts diff --git a/repos/effect/ai-docs/src/60_child-process/index.md b/ai-docs/src/60_child-process/index.md similarity index 100% rename from repos/effect/ai-docs/src/60_child-process/index.md rename to ai-docs/src/60_child-process/index.md diff --git a/repos/effect/ai-docs/src/70_cli/10_basics.ts b/ai-docs/src/70_cli/10_basics.ts similarity index 100% rename from repos/effect/ai-docs/src/70_cli/10_basics.ts rename to ai-docs/src/70_cli/10_basics.ts diff --git a/repos/effect/ai-docs/src/70_cli/index.md b/ai-docs/src/70_cli/index.md similarity index 100% rename from repos/effect/ai-docs/src/70_cli/index.md rename to ai-docs/src/70_cli/index.md diff --git a/repos/effect/ai-docs/src/71_ai/10_language-model.ts b/ai-docs/src/71_ai/10_language-model.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/10_language-model.ts rename to ai-docs/src/71_ai/10_language-model.ts diff --git a/repos/effect/ai-docs/src/71_ai/20_tools.ts b/ai-docs/src/71_ai/20_tools.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/20_tools.ts rename to ai-docs/src/71_ai/20_tools.ts diff --git a/repos/effect/ai-docs/src/71_ai/30_chat.ts b/ai-docs/src/71_ai/30_chat.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/30_chat.ts rename to ai-docs/src/71_ai/30_chat.ts diff --git a/repos/effect/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts b/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts similarity index 100% rename from repos/effect/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts rename to ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts diff --git a/repos/effect/ai-docs/src/71_ai/index.md b/ai-docs/src/71_ai/index.md similarity index 100% rename from repos/effect/ai-docs/src/71_ai/index.md rename to ai-docs/src/71_ai/index.md diff --git a/repos/effect/ai-docs/src/80_cluster/10_entities.ts b/ai-docs/src/80_cluster/10_entities.ts similarity index 100% rename from repos/effect/ai-docs/src/80_cluster/10_entities.ts rename to ai-docs/src/80_cluster/10_entities.ts diff --git a/repos/effect/ai-docs/src/80_cluster/index.md b/ai-docs/src/80_cluster/index.md similarity index 100% rename from repos/effect/ai-docs/src/80_cluster/index.md rename to ai-docs/src/80_cluster/index.md diff --git a/repos/effect/ai-docs/src/index.md b/ai-docs/src/index.md similarity index 100% rename from repos/effect/ai-docs/src/index.md rename to ai-docs/src/index.md diff --git a/repos/effect/ai-docs/tsconfig.json b/ai-docs/tsconfig.json similarity index 100% rename from repos/effect/ai-docs/tsconfig.json rename to ai-docs/tsconfig.json diff --git a/assets/dev/effect-app-apple-touch-180.png b/assets/dev/effect-app-apple-touch-180.png deleted file mode 100644 index 906d1621a05e8b5d53409bf02c9dd50b315b4681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11603 zcmYkicQl*t|37ZV3c8Hg9cmL|)GB?^s#TQQBZ^Rp5_=^ms#di|lp3}7-a=42R_(ni z_TKv&@6Y+2^E)}oegAQt=kxJg*L~f2JVG_!C{a@~Q4$akP%FPye2c$U+ARyo%P*!}Q%UGIf=6B8fG;G?$lo021A_@CiDTC=AIM`sjFU@Jh)sWZI>)1gyAl->MiKU5sSM|z`@B3y{oUdTq(9wTc(UY~>W(-;U;SJAw65Pjb3YCaCK^f$NY;vs%CzQo)q6?(3Yr9jHh-U3 zQ}_(JyZMzuUPDj78}s5;=Ba`3X~?V zbLObcnbMQv8SgD`hw0tEH$R_HlX=~J@4|MnF5TuTq^a_YN@O;Vkz8}b`2#(cz^zU3 z4nDeuBtQ$xdXO!F%aS=DZTS6TBH3Rfn_iqB`}wV*VB+&`4tWi0TJn0NQOw3{jos0x zwBx@do24-mCcxqEAErevE^&fdE$=Wazw-D#>KHBpK z8icLba@@KE$dr_Ie;~l$hGkc$b5&_TsZ|=KN7qizD)TiqvIyV0r!ugjb1SFb#@BZ0 z-A!QIreN+M%O3>=jM$)p!2bs1CCFg@;Nb0oS8ihTuxJn9ukINR6lvPk|z`)f$#&o%@w6)y2k=MDm2yUPG9-D;qZS$xl*vUeeR%qzaQATNx~m_fHad zmTc&p3aUu4aZDDr`88~1=0`QjzepMKP{oi!UOUv{$_r z626SloL51f6^)!ZeR{Q^8CK>z4o!g42fHX%6Pke|dcEYTDEUGT!k7vqUv*3_`H-fS z|D1+}4P}5a#u)3N0t*9K!$bO0;AYIk-~5QLs<{f}+0C z2Ww+kD!CG>&%nf~F+QsSlJ3dM!5K(Xm%}VwEn{trnu&Q|nXHvYMG_INLcO$MJ;a*b zLqYXFWu{d@ZC|Z#2De@x)4_y#lD>-_|NZ>?*??Ey1iB1aOAdMU4C|$^ccjtV*mQ4% zp197+dn5bq@lHw$5zfC^EhCf?6#^`eTNA4j{AvqCnto=SB`&`qMz3l-HOg!`J)D+-@9NV+#pX;2__i|SGxgKnXO8n=?e z6$N)CfXL#cl19?Z=Z1J|?=VSel?brXR?mnQ846tpzvPi2PW{J}jg)?9W=;qvgg+YV z^ap+0gXJEf2}X;HqI3Birkr0Du795aoPk$(HmG3v@7F2A5^~e@$hBXdu)}U%LZnKy z4zP+pU(29%8}P%q*<|ySHsV#Y@&4?1On&+t*m^3l(kgy)2;mwbV^8o*%u}x%Ix6SA zRsd*bacsjiC-G)0gSk$`sBq_=xR2fkRko1_4g{_v(h@t?!?uY$vqY06U<=eN5mSRr z))b=Jky(G+w2{P6cAC5j5AB#9;9jut0YsH6jDpG5efE^@{afiTDV zUd^I<4pkFE`1COmF8toB2Uw0AaaGMBN))6Uv<2omY&tUseER_S>eNaEFKWKcd0+s@ z-zoS@c#rP?YNypg9t4taen!lgs5>r?;_RQl9mnR@P5Vel&XPL;73%)+18~Qo1S1St zriEdtMk6KDKMxW@IA*<8iEu0%cy#x%1ns-cN&I!lTBIZoaUcl84ecvyM0$$00CP{A zMTv2bu-s4wAodRN?UDqYiwx&OW?Y6S8?2fA0X_J>SOZd0se?QOg!#g%d)raj_=4PK zOGC;Y3=U*)egL-K;ej4ky`sRoGNQ)uPiY@3_V7U8OvMbyy@dh>4+d^ya|ECs-czuK zdEdV+7gDm)v7!|i`k}rWfHiN&gJn&B0OLri;)4o8*|u)&Hi$uKsD+??gTmeBfIXOG zz}&5wbOzW%CItrY4};>*TM5hIQ2azLuY3H!0H5RCSdQ|Ud&4C0g9LyP^awIb)|U-i zGfI%7!m5!lWo;9rj<6HKk;eT=_Kvf<$+Epj z5J~>zvLyqDCtyqnYjbQQf5qDCM`e>*-Q`Glr2X@fet@H}K3#f0e-ErMd!J7oS!yS)%4C_Tg75QyNf-nEop5nqTG z7Xim@{)qe*Ae0V#LR$W>T4Pt+Djs5qGEp=piFZf*lYf3Z;`s%$%_=*MXf=%42oTZa z#=N}Pd@bo3XdtqGy2kt1u?&U7@FVdPtsPlI@R_~7Cr~m-3)enx?)D7#Z_RjrOF@fG zn&EP$*<~|t|Bnf?Ox6Qla`}cIcAa2VoUY*$Cn@VOxyF29u!3mt8+siK3xk$?0ZbF7(SkZ2p!q%mN)1YVx0K9kPO5u$v|8ZCkI^2ak_K>$D#u7{7Wdsilgv z|Euk&P9vcfySCDATi-Z*1~ODAUj0e}-jTCr(YuofpRH(M+ z^}^Wr(W{VSY&W?frtIh^DBCbh!n_!))KF2=&~WbhM++OiwwqemQ4rRR33JvT`vuwUu?NbH5EKDs$vf*?tzGB)3D zpC)T_?zhHomee-V`j2J1pM4FEF}hMGt415YU3SeU=sB1Q{Je1biQWjI!f60Ej!i_l zwV>gg5A%6~Od~_29oF{UoTWdPuIRZKF@woPLuH3E?@oP zd*Qj_TgvS9;``k+EsI|N2Y#`s0M?qs%S8$&zu(L`ZiV$oY@HPgCG7~exFIdv^o9nF zhCVIA6m8KI7jC@2>ZZ|^Lx&xARF_-p(NYro{(9hui5dI^0$)G?l!NhFhx{$-O~zl% zTM`)h)Z>USWP(Yd+^0wGlfrjmM9}B;3$c%~1ZdzJjEOx>2of?u#uNiTN0F;kC!cd^ zrM;oz=Px_9*?@ORm^r_%OWtc*GUDAgyNRD|T*|Uy%=)x%)=S0sjzFb)B9uLmlydvL zUyNgZ5wMj^L#puWC;)_!@tcgN&&(guM^VcKzws!rdHwL&muH)D*|AZ^;(-S@FhQjh znS=zY4zQDLsB^x*IZsSli7H@;D}Ed?%Shun_B?4fZA~pncj1V7W^-a9Gfa=Nb+k9J zK_o+ZxSVTo@rbV3>g@Z`xNbt#W3CRF3U;%8Ia{K_rH{$J9{CK~e88z;uY3nZqX#gC ze9PThga>m7yBz0X`_vO0X1nGnI86WwO-d!{`*=BnR4Y(W#_{`R$Ve07b%3E zR})Tg3!jv9O+J-71Y?mO7*KKQ$uO`NIsd&q!pcH1KWltEF|}1a$^kD#VdBw zbPLyBr2V%M8`I`w)W-GtFlB#ZV}JAeQA{DFz0#XEgj)(G4g#f@3j)Y}!RC40*5xuS z6t#BEsUyu&&5!Jk1y+uMM_|u$#;59z%e5gA=>?y#caEw-=-5?0-w4X}VjrQcfB=S+ zsW0|13EtzDvd<3v#R)P|zN;kN0^&k4rENlefs@tEI|ms+{M**S=?6382T#SguwVfb z9%>gBSX%ob$l|m`u5V@6eLGNO$h1T#^~(Nk8{t6*q+cIW@>%N_AtvJot6zAQmMlWzW!3K;~J#617z3SS{>nC@Hqf z#EmaJD%1+c4oOhwJ1JR-5KQ`c3Q^-RO0O=zQgwKS(N1pto84NjQP?V|r|&WR@4)~O zR?_ED=2!KLp<@3>EKfqstV7BLe|CUUekKxma?6Kd{0~l=b9qfY7sQhCHdr@|l3gEh zU5G4dU0KBLyYa=`Gc~RvoZ_|1QE!=`RoF6bO9&2YUa$gBS<5ylSha;@V9wowV?ha2Tu?o=WW+~^$52E^(z9Qx z*Q52BGaRWe=ToaS7AhlHKbqD*=6-Td(`r<5)_azRy*4AL`mAzBh<^rYAD3`b#lvLV zO2vadXCJ7d#XBA;v-cxJ(>ypR=;~(p=y~&5UV;Kfu0SKZ?*7Rngx2KQkJhLuF4B*( z{`3sI7WT$^`ZtSiTk0__DL+*uVyXq6lPm_Y-w9JDjgu{nEHg1*5Y~PxUr&RzIRL0= zG6kN9SkN)a-tiR9&}vK1V|dGCzJmN5nvr+*k$ z0K}MAL|b)W&vr&#nX{jMHt)Wh^4Wf901>4udakFcHVingS#0FS@bb&JsZiRC1%aqe z%$q(~oUH~O*nlj8zKP$lxvLrcdkeMJ&~Pc{X1}w``(ul92;4EnNiG*~H#C> z%>Q%w_?YyriTfa8vE{IqX+1uJqnrlD z)u7CT6>kA?h5F?m^Mi+sZst!knP5$(P8x-mS6`7;nNeQCN77jt$aa5Hc)K=lqSI?; zUzFmF9cCO1IR`I^Ft!grcYXMcK$YD-0FNr0FkmE|5`IX?%SD2CqMbC8dXBt*2t|UM8jb{-H2)bE;r1_B1^n=c}ly7!Ze31~dD+F#6N`$CYx@=z~MwY1n6F8%CI< zeLspiG2NGnJsg(mJ=_i#L^umny@MS;)W)0)|NeByYj~VkNgHQh?$$P%+S%a$K4T4H ze=rmtvqv2)X)Km~Bkemn4qW>A_(xXMi$85gDX%Fucc@@yzmwI6tfA)I%nAwNd{Gwd ze<&E=y-HG(v$>b;w{4>~HGK83Xw3J&Iq=2Ve;S3wIsg>|#_kp!@vO4iBL)qp9mh%6 z*Y`m#r$$O>YnmOIr8|d{tf8x+Q-kcNhi4J7fY!~WO$C8P5lJ|(Eg`J!9&blu3p^dA({wz*0Dh|6d`|o8=|1Z54^HAe1{4K zVsR>E4Rj7m=$m^|YQ(?!HOOjwGXH&QOrz^}aMDK+M$$Yz0eRSDZ;1ySxCY z6c_4#t^Vb5C)j-N(3ekGbFbM}&Yb?yPKbjHw#(NcGP;-UZm37LzXxvD!}&OZKKC7~ z&}M)K)gc4yOAtFZ&}w~`N;Tz4_b%JBFRCt~w!N;eWv%51P%ldISVsUMpg~zCHdPWO zTNiiTwQZLY75dhPBYnKGzhrBITXYsZvpdPX<-uVq(JaQf`}rwCnSb-1EubGysaDHX z`ja{MP*!JwWA9hI(4CKtE8t9>U9w;NJ7h4zW|-%*`}^^;?cv&C+k5q5#jKPv@Ht+# zdOOLiGR;-4=y!45K%?I}Oud1UT9NS$S4Vv^`OJdeRqoMwdaN?#1S8vQMK=rD{NB#+ zD_~K1!{sz=`?BrvaNeHQ1_p2H>`nbD`RrAiXRRB*CzLa4`fR21E-@A4_lH-y?ZK@q zSV~O0-mxX)42$R0$c$5zF&FCUcx`pfQgy|_IPor5apS&J_4Ve9I_dfbk9Q|s1JFq0 z(7Tw0iZGhxKT*A^z5EyZf6)1-LjoE^!R}U!GT3jz_G2EX);Y>H)vbCFpkw4m<4RVF zr#>505X_mGT;xeg6LON|)6l|FN!LcAoAZzuZ5e#U0cZtHwV1ZyFR+>iD~(m9xa~DJ zoFtvqF3TQu#7-j)?$7F^G&ZLy7FYKDO}vinvlgYpy)J9|Yk!tfJuvjcgR-9D?UtLz zSx{lLvP3XJd9uM6mH1(KNJ`E6>o+$Q+iF?^Ba{xwiMGEt$E)IRuCG%(?58$RD*fBs zJI$EYA<4iHgJOHRqkyx9o9VMJ_o!_aWjq(R-(Bvtcc9l~sY*1j(}n(wjpF;|Tc?EX zPa?R7dzZv@!wGPPHoaf6U|aVe}}~@ zt&)y?>)H?{DL1!+L_0Rip5Q`4Y%tkdkL>%w0C)tg>9dp*-%F0etKrwoUhdNDO?GsM zngwBvrb1cKED8JZlq2^uwrM2H3r4}z7c!~&^kk-vrlVJ+On6aBP~&F7s+f>E#?FEq{Jiu0gG1Hd$);J@oI4!P3|q;+x*&m<7}u5$6v1Lyq$; z&%6PrT;~SigAe3rh4A?+?dZ47G;LGkE?=tnHUpu+j(XgK>j~TU&@!(PdbT<{-{7=} z=**Mi<&nxn;TTX$0bnfw?LJuhc4<>c2pa^NpPj2XC`KTp<*q}N*0zh5arxE5UMvo$ z%?btjRRHbS_i4fY26`wB$vG;>`@PKT{>RnDi;*`rEZA>H5p+yckS{05yyNie!oNlc z^!LhqcNn9w*+iKeSJdp~Z;3rez@7!}J&F)>S=z(kJeqk1o}i2*>m;(V{#1 zi~X3!K3u-*at%sE>_6#YGg@mda8%t8n?DLXtDs_V?&iXUT-?82Xg1Pyni68Bf>^3e ziLt=srtkRHO2h!XIwH}R7w++;2S-J~){V!k^>sBH=hhSD)sMfhKCHvYy?$I_H53f@+TQqaJt7Zlqtfi^cS|yA~ff-dyraSC6r; z&rC_5T{Q_>^|X&Q^oc!o2P{!ba-f8aIt@~~Le93$st)Gle1NL4!m$yV-TE=YI-ygJ z#x2xxzEzEM5uLsg4H(}hv&9j;5T=LNg&~O#AOdWPF&;&RIsHxwnS){5PO%*g_D({T z9WVA&l_p}ovB}gyfAK7H<1D*ardw5LyECVA-f~G*`n0W@&Ac~^S(w{%cH*lC&qMui z2m$J5{eVjH;JXiM=hRa=tmyz8$7{~yHlIdK>!2N5vvAH;vr?VEuB&Ipc78?aV0MYh z?mvc#KLW1^W$g%I*=70z4JZaLhW&C+W^2AC`i;t<2(cE~etueT^SDkUd9-`cnS?|G zk=yoU4}{jfxeN&FOF_O90aRj|bm`d8_S6z47ov_IrHqjq{*+_uh_d1YP}@F!QS0>^j;+%qzmMDf;oG*Q#v# zJqX_u4OAA5ql<64`72iFwFh%{5?Wp;8T{lU*?pjb)1129X>XIvU!7Z=S$LcSDzy{q zWRQ6Q-V;+&oZObYk4kZVeWG=;et%hrX}VQ-?{I>}Tj&bm%*;Z`zeegyoOl23s)>;1 zmYXn-7VkSSkU2N^kCi(TjL3?B$cYIZd|694pEr2SvKqA8>U+s`FIo%c zujkNMN5px?^XQ-C5c2i1)_4_&IRH%qO3g0MI|$t<DM?ah0siVxC@r(2nex0-c04UIZ} z$0%10g&N-tCO??4Pr8iugx>f2*-B7~5uJ}}GHL4_6KV#3wEg9vZ4S_kXFU%8XPqyH z4bZe5%y-38&ZdO-e1`rYB8vFLjPss3^cn@|w<4NrJsh{f^QgB^J;R2nv7gk7M6~o{ zIx14ZIc~aSWCv~bNlwiT;}W{R<>yLker;Uy(3JbX%&epfdf{%Zk->Zv#$>|6lIw+- z*_7}lLR|bffxw&p5E7!-GFg3YR1uavSzFC&Dw&ld#C5Mspx64$I~OHIP3Cu#{wz*- z?Cj_|wQ8`Z|COTGo;Y-oQV#;%8*=MKN}7h7Q9JVXC#HzUgt~t=LJ$xDFK@K~=K72= z;CG;&oX?27fDbXHX#1X=Rr~bt+)EiN{&W^B|LPjA94yNWY&UfEqhleRp~~sp6AdIl zL{pdVXOlhF`@G93HY+1TpH&8a7g|`n{coL*9oiE-Q(O0a|2*IOG`?8&PM|>u<5NK3 z9(G}t02fuU*I15gYkfFi9)dwPjetXl2XJ06v+&K2y`Lpx=FL^DQvU#%I&`YYa)Kw` z3g}nZq{gPq-V2<__}5!IX?~e=X|C@G_P$nbd1t{ci6G2I{ANHgRKsW7*}8%aav8CH zmw7vJObZlh&omkqmd8%7_1G+x+hNoQMD$pPROg`VbA~1g#wbJTyvFKJ*e?B-L=S&Iv4!-beaLIh6X(wdQD9)EW z^@p~|=Nr0ypB=QbnX|W)&z9k9;NlxBcgoC<9h7My>$6=#TU#Omi06~%3jYNo5I%|+ zV8D5cx$1ji7YJ0aa-4i26|DQFfP*5E_BrrD5^xVkuIqZ{@C5KQtR1i$IA@S4c|ALp zJ*ZhXyZ*k<$Vac>?)L0*wOelQRF)5YW-nB*-%iUNsA5N<=|7vqrhMdoEdi{qP>+=O zGP1-tgERk9iS(A#soxuBRf z{P$W9|J+F4g^q4ObUty4gsdi{yPmYsjy3Q-rHAcaM}GK>O zKO3a)u3Tbf$GVzVD~hEY)F|^1I@NtoqAD<$pL{h0RN#3~2icPc>@}*@ zYnAqh@e49d4MXEL*XRMde^N;BsmE0{K=>sK?B(deByr#bqDZq}YVFP&Z=2@BeUBRN z9q=GkFNe?HCnh(V7E4e-$twmdX!$k2dtK6tAdsoM$1G^4I^Wn=`NWy$#`3K>TR@>d zhK25if=Rn6SBD%B*>s-lCo-vH<+yA86dJis+rwUC9f|Gmu;=hdmaBp(Tv!LxqbH6u z;%Dmx0)s$`AuOa=Wmc&Q%|-KkpZ<^SJ$IK2w<6;p9;hpgY@UWL=dnPhegq#yRckfN z2s zpZN5>$3jSSj=i%iC66?K3_eWLq!XG+K?1gJTlbAkw(ik14~A-@G;f{;OWK+h(I{{C z220MQDFjVu?4|;QUeRhKb2J{EkampR$MI8`)7*dzzzRS-1uKeOYj8rrKXwlNijE7G zOqOm)C%@+-E#2VtxwfouyC<^I{LS#I^S*dJujEKC{1J66=)c||$>9XOt#*uY-gvHW zV4&pGY(wE&Ri2?_AJouM=l0=jHm+O1>i5=k7>)(Rg3Ww>c$_TmfqK#KK{|K{V$Ce& zEDNtfdm4EjAF{*bI6;%c8(GT9qO*`OW*A99a^E@_Q*ggd+WX$b1}&ZhKhUm|_5x7A z*}X&#K7ZIVn!`&}xPP&YC7OiIzF=m-HqI7deJ1E>Sz(0X<|kP7V9D5YW$=o@z%x|& z5ECtYu1b^=)_LBpLI_tw5FV`hgTBw{f1t!p(D6e1%DI{@@p=cbiCZERFYovd^>8CJ z_El@rDqdP5w)sN>#;GE#nZu~d3XxgA(aSs9sArF8vIW}ml9QeWQkMGj^IamS66F+X_ZiS)v0dH zB^BP1)rCmUQ*-)DM?4PejV|nuH+)bw5SW$P7K!uxb}8~LdyQ3p|W z0PG##L97t~m+=66@(y35XaI!d{|N1Lb{N4@A@Tq6mVsyWl?1VOu+ElN+_wz23I4Ax z@+T>Qzmbx^bTKmf=72hCs0JbYEhTD*=P?@$IRoh2UX8qs+NC=O2@)OHqyggdb@~o7 zt(Sb42=cokWEM}h-=eev6My+At~lKNf2+9zL4=k;Y0#H${-oHwULsthGqn&F`93Hu z#xPniYY*%V+E+`*bNRK^J`2S*hy=cH!@{a3D)=FAb|-|J)xILZ&ND@xg}d&@&)UnB zop!m6#ReV#3nj4WCjzN~8;8}CM)ExZRx?Vzx&;n+-30Ey+V6NNju1MIYwZoF)whYs zmsDcfN!Xx(S6TOcKgaj&WP9s8`p41tD!E^z>v5LWqxWIHIoEg+gs>Mul`}eZS&fV{ z>JdgyRIZ{SWW?py^VSD*MC%({>#OTSvtAe=(bpkIgNq4iYUHhg?T#CaUDpf+cNJyC zMQYZAprCT zQqFYe%B~@_7ji_Y3~9zOKGpavdOm0l56DkXsg966lEq8aBujFUl1&8-#-!NZ5YYI5 z$@-<0=tIEat%6NAXNLiNV(LeD@C6C_RNQEHL^m_|;Bi9RWcdacQL!Zxz7lk_2Gf`ddc>>c~g-s&RsVPrUQnLpqY%Zf8p2`|Dq_!0n`)r>q^4kt#M zB261`|6B#PN%y`};q@vZbTF;28gpf9-;Y3KglGcl5|5D!N2Pz0YT8tI)yv6Gp>ba5 zw%+q!i_5yk@Se&3nItDo4m1%p*!0N9a)N-=^ky++6Mji2lUqty7x9&v zTd_}wvC!S&sP3hwjW?05@#ttLXv%K@TvJCd$^9h6IDW(5*JtSR=3n!soakh%hmB~0 z(3PDHxPx8So(;Pf@-C4K&gr3F38}vXG#h`ge}OEDj5Iyq-taTSNx z>*es9`mPHUJyZH!Q@M)`vZg^$r<%6(=D}FSbx>(EY*M@_GOLI_&Bg22&^M0Ycp{Ai zL;ja35fBnoWk_6S?8Bx4ua*MYia+a9KonndYzbdn@gUgR3{QK6^}JvFF@(%O4}QiL`kF4mLV4{CbfU;G!&<|3i!@osF5FYlIqU&6b&?drqAa6C)J~J!KyC^cB(xqxiX0KG*cu zHZ5g691s-oH9-{=*doJv6rR9WcrK9otLufiP{%Pbjtxp%(n{yX$7vK(@)r0(2}>7j q@f?0UCRp)5{cjg{M%w-jVO-tjJ-f<7AYLU+p#1WUV$pxlfd2 Effect.succeed(f(value))); v4 consolidated pure and effectful input mapping." "effect/Channel#mapInputInEffect": replacement: "Channel.mapInput" - note: "Renamed to mapInput; the mapper remains effectful." + note: "Renamed to mapInput; the mapper remains effectful. Both overloads retain mapper service requirements in the resulting channel, so provide those services before running it." "effect/Channel#mapOut": replacement: "Channel.map" note: "Renamed to map; the v4 mapper also receives the element index." diff --git a/repos/effect/migration/annotations/effect__ChildExecutorDecision.yaml b/migration/annotations/effect__ChildExecutorDecision.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ChildExecutorDecision.yaml rename to migration/annotations/effect__ChildExecutorDecision.yaml diff --git a/repos/effect/migration/annotations/effect__Chunk.yaml b/migration/annotations/effect__Chunk.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Chunk.yaml rename to migration/annotations/effect__Chunk.yaml diff --git a/repos/effect/migration/annotations/effect__Clock.yaml b/migration/annotations/effect__Clock.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Clock.yaml rename to migration/annotations/effect__Clock.yaml diff --git a/repos/effect/migration/annotations/effect__Config.yaml b/migration/annotations/effect__Config.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Config.yaml rename to migration/annotations/effect__Config.yaml diff --git a/repos/effect/migration/annotations/effect__ConfigError.yaml b/migration/annotations/effect__ConfigError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ConfigError.yaml rename to migration/annotations/effect__ConfigError.yaml diff --git a/repos/effect/migration/annotations/effect__ConfigProvider.yaml b/migration/annotations/effect__ConfigProvider.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ConfigProvider.yaml rename to migration/annotations/effect__ConfigProvider.yaml diff --git a/repos/effect/migration/annotations/effect__ConfigProviderPathPatch.yaml b/migration/annotations/effect__ConfigProviderPathPatch.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ConfigProviderPathPatch.yaml rename to migration/annotations/effect__ConfigProviderPathPatch.yaml diff --git a/repos/effect/migration/annotations/effect__Console.yaml b/migration/annotations/effect__Console.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Console.yaml rename to migration/annotations/effect__Console.yaml diff --git a/repos/effect/migration/annotations/effect__Context.yaml b/migration/annotations/effect__Context.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Context.yaml rename to migration/annotations/effect__Context.yaml diff --git a/repos/effect/migration/annotations/effect__Cron.yaml b/migration/annotations/effect__Cron.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Cron.yaml rename to migration/annotations/effect__Cron.yaml diff --git a/repos/effect/migration/annotations/effect__Data.yaml b/migration/annotations/effect__Data.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Data.yaml rename to migration/annotations/effect__Data.yaml diff --git a/repos/effect/migration/annotations/effect__DateTime.yaml b/migration/annotations/effect__DateTime.yaml similarity index 83% rename from repos/effect/migration/annotations/effect__DateTime.yaml rename to migration/annotations/effect__DateTime.yaml index f6c65c930..72dcbd320 100644 --- a/repos/effect/migration/annotations/effect__DateTime.yaml +++ b/migration/annotations/effect__DateTime.yaml @@ -22,12 +22,16 @@ "effect/DateTime#DateTime.UnitSingular": replacement: "DateTime.DateTime.UnitSingular" note: "Use millisecond instead of milli; the other singular unit strings remain." +"effect/DateTime#distance": + include: true + replacement: "Duration.toMillis(DateTime.distance(self, other))" + note: "V3 returns signed milliseconds; v4 returns a signed Duration (other minus self). Convert it with Duration.toMillis when a numeric millisecond result is required." "effect/DateTime#distanceDuration": - replacement: "Duration.millis(Math.abs(DateTime.distance(self, other)))" - note: "DateTime.distance returns signed milliseconds in v4; take the absolute value and construct a Duration to preserve v3 behavior." + replacement: "Duration.abs(DateTime.distance(self, other))" + note: "V4 DateTime.distance already returns a signed Duration. Use Duration.abs to preserve the non-negative distance returned by v3; Math.abs and Duration.millis expect numbers, not Durations." "effect/DateTime#distanceDurationEither": replacement: "DateTime.distance + Result" - note: "Compute the signed millisecond distance, wrap its absolute Duration as Result.succeed when positive and Result.fail when non-positive; v4 uses Result instead of Either." + note: "Let diff = DateTime.distance(self, other), a signed Duration. Use Duration.isPositive(diff) ? Result.succeed(Duration.abs(diff)) : Result.fail(Duration.abs(diff)) to preserve v3 behavior, including equal dates producing a failure with Duration.zero. V4 uses Result instead of Either." "effect/DateTime#greaterThan": replacement: "DateTime.isGreaterThan" note: "The comparison was renamed with the is prefix." diff --git a/repos/effect/migration/annotations/effect__DefaultServices.yaml b/migration/annotations/effect__DefaultServices.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__DefaultServices.yaml rename to migration/annotations/effect__DefaultServices.yaml diff --git a/repos/effect/migration/annotations/effect__Deferred.yaml b/migration/annotations/effect__Deferred.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Deferred.yaml rename to migration/annotations/effect__Deferred.yaml diff --git a/repos/effect/migration/annotations/effect__Differ.yaml b/migration/annotations/effect__Differ.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Differ.yaml rename to migration/annotations/effect__Differ.yaml diff --git a/repos/effect/migration/annotations/effect__Duration.yaml b/migration/annotations/effect__Duration.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Duration.yaml rename to migration/annotations/effect__Duration.yaml diff --git a/repos/effect/migration/annotations/effect__Effect.yaml b/migration/annotations/effect__Effect.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Effect.yaml rename to migration/annotations/effect__Effect.yaml diff --git a/repos/effect/migration/annotations/effect__Effectable.yaml b/migration/annotations/effect__Effectable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Effectable.yaml rename to migration/annotations/effect__Effectable.yaml diff --git a/repos/effect/migration/annotations/effect__Either.yaml b/migration/annotations/effect__Either.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Either.yaml rename to migration/annotations/effect__Either.yaml diff --git a/repos/effect/migration/annotations/effect__Encoding.yaml b/migration/annotations/effect__Encoding.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Encoding.yaml rename to migration/annotations/effect__Encoding.yaml diff --git a/repos/effect/migration/annotations/effect__Equal.yaml b/migration/annotations/effect__Equal.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Equal.yaml rename to migration/annotations/effect__Equal.yaml diff --git a/repos/effect/migration/annotations/effect__Equivalence.yaml b/migration/annotations/effect__Equivalence.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Equivalence.yaml rename to migration/annotations/effect__Equivalence.yaml diff --git a/migration/annotations/effect__ExecutionPlan.yaml b/migration/annotations/effect__ExecutionPlan.yaml new file mode 100644 index 000000000..191ccb261 --- /dev/null +++ b/migration/annotations/effect__ExecutionPlan.yaml @@ -0,0 +1,9 @@ +effect/ExecutionPlan#ExecutionPlan: + replacement: "ExecutionPlan.ExecutionPlan" + note: "The plan type remains; withRequirements was renamed to captureRequirements. Step schedules use Schedule; captureRequirements supplies captured services to schedule initialization and execution." +effect/ExecutionPlan#make: + replacement: "ExecutionPlan.make" + note: "The constructor remains variadic. Its inferred error channel includes failures from step layers, predicates, and schedules; requirements include services needed by all three across every step." +effect/ExecutionPlan#TypesBase: + replacement: "ExecutionPlan.ConfigBase" + note: "The base type for execution-plan step configuration was renamed." diff --git a/repos/effect/migration/annotations/effect__ExecutionStrategy.yaml b/migration/annotations/effect__ExecutionStrategy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ExecutionStrategy.yaml rename to migration/annotations/effect__ExecutionStrategy.yaml diff --git a/repos/effect/migration/annotations/effect__Exit.yaml b/migration/annotations/effect__Exit.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Exit.yaml rename to migration/annotations/effect__Exit.yaml diff --git a/repos/effect/migration/annotations/effect__FastCheck.yaml b/migration/annotations/effect__FastCheck.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FastCheck.yaml rename to migration/annotations/effect__FastCheck.yaml diff --git a/repos/effect/migration/annotations/effect__Fiber.yaml b/migration/annotations/effect__Fiber.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Fiber.yaml rename to migration/annotations/effect__Fiber.yaml diff --git a/repos/effect/migration/annotations/effect__FiberHandle.yaml b/migration/annotations/effect__FiberHandle.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberHandle.yaml rename to migration/annotations/effect__FiberHandle.yaml diff --git a/repos/effect/migration/annotations/effect__FiberId.yaml b/migration/annotations/effect__FiberId.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberId.yaml rename to migration/annotations/effect__FiberId.yaml diff --git a/repos/effect/migration/annotations/effect__FiberMap.yaml b/migration/annotations/effect__FiberMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberMap.yaml rename to migration/annotations/effect__FiberMap.yaml diff --git a/repos/effect/migration/annotations/effect__FiberRef.yaml b/migration/annotations/effect__FiberRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberRef.yaml rename to migration/annotations/effect__FiberRef.yaml diff --git a/repos/effect/migration/annotations/effect__FiberRefs.yaml b/migration/annotations/effect__FiberRefs.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberRefs.yaml rename to migration/annotations/effect__FiberRefs.yaml diff --git a/repos/effect/migration/annotations/effect__FiberRefsPatch.yaml b/migration/annotations/effect__FiberRefsPatch.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberRefsPatch.yaml rename to migration/annotations/effect__FiberRefsPatch.yaml diff --git a/repos/effect/migration/annotations/effect__FiberSet.yaml b/migration/annotations/effect__FiberSet.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberSet.yaml rename to migration/annotations/effect__FiberSet.yaml diff --git a/repos/effect/migration/annotations/effect__FiberStatus.yaml b/migration/annotations/effect__FiberStatus.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__FiberStatus.yaml rename to migration/annotations/effect__FiberStatus.yaml diff --git a/repos/effect/migration/annotations/effect__Function.yaml b/migration/annotations/effect__Function.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Function.yaml rename to migration/annotations/effect__Function.yaml diff --git a/repos/effect/migration/annotations/effect__GlobalValue.yaml b/migration/annotations/effect__GlobalValue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__GlobalValue.yaml rename to migration/annotations/effect__GlobalValue.yaml diff --git a/repos/effect/migration/annotations/effect__Graph.yaml b/migration/annotations/effect__Graph.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Graph.yaml rename to migration/annotations/effect__Graph.yaml diff --git a/repos/effect/migration/annotations/effect__GroupBy.yaml b/migration/annotations/effect__GroupBy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__GroupBy.yaml rename to migration/annotations/effect__GroupBy.yaml diff --git a/repos/effect/migration/annotations/effect__Hash.yaml b/migration/annotations/effect__Hash.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Hash.yaml rename to migration/annotations/effect__Hash.yaml diff --git a/repos/effect/migration/annotations/effect__HashMap.yaml b/migration/annotations/effect__HashMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__HashMap.yaml rename to migration/annotations/effect__HashMap.yaml diff --git a/repos/effect/migration/annotations/effect__HashSet.yaml b/migration/annotations/effect__HashSet.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__HashSet.yaml rename to migration/annotations/effect__HashSet.yaml diff --git a/repos/effect/migration/annotations/effect__Inspectable.yaml b/migration/annotations/effect__Inspectable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Inspectable.yaml rename to migration/annotations/effect__Inspectable.yaml diff --git a/repos/effect/migration/annotations/effect__Iterable.yaml b/migration/annotations/effect__Iterable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Iterable.yaml rename to migration/annotations/effect__Iterable.yaml diff --git a/repos/effect/migration/annotations/effect__JSONSchema.yaml b/migration/annotations/effect__JSONSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__JSONSchema.yaml rename to migration/annotations/effect__JSONSchema.yaml diff --git a/repos/effect/migration/annotations/effect__KeyedPool.yaml b/migration/annotations/effect__KeyedPool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__KeyedPool.yaml rename to migration/annotations/effect__KeyedPool.yaml diff --git a/repos/effect/migration/annotations/effect__Layer.yaml b/migration/annotations/effect__Layer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Layer.yaml rename to migration/annotations/effect__Layer.yaml diff --git a/repos/effect/migration/annotations/effect__LayerMap.yaml b/migration/annotations/effect__LayerMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__LayerMap.yaml rename to migration/annotations/effect__LayerMap.yaml diff --git a/repos/effect/migration/annotations/effect__List.yaml b/migration/annotations/effect__List.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__List.yaml rename to migration/annotations/effect__List.yaml diff --git a/repos/effect/migration/annotations/effect__LogLevel.yaml b/migration/annotations/effect__LogLevel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__LogLevel.yaml rename to migration/annotations/effect__LogLevel.yaml diff --git a/repos/effect/migration/annotations/effect__LogSpan.yaml b/migration/annotations/effect__LogSpan.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__LogSpan.yaml rename to migration/annotations/effect__LogSpan.yaml diff --git a/repos/effect/migration/annotations/effect__Logger.yaml b/migration/annotations/effect__Logger.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Logger.yaml rename to migration/annotations/effect__Logger.yaml diff --git a/repos/effect/migration/annotations/effect__Mailbox.yaml b/migration/annotations/effect__Mailbox.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Mailbox.yaml rename to migration/annotations/effect__Mailbox.yaml diff --git a/repos/effect/migration/annotations/effect__ManagedRuntime.yaml b/migration/annotations/effect__ManagedRuntime.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ManagedRuntime.yaml rename to migration/annotations/effect__ManagedRuntime.yaml diff --git a/repos/effect/migration/annotations/effect__Match.yaml b/migration/annotations/effect__Match.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Match.yaml rename to migration/annotations/effect__Match.yaml diff --git a/repos/effect/migration/annotations/effect__MergeDecision.yaml b/migration/annotations/effect__MergeDecision.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MergeDecision.yaml rename to migration/annotations/effect__MergeDecision.yaml diff --git a/repos/effect/migration/annotations/effect__MergeState.yaml b/migration/annotations/effect__MergeState.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MergeState.yaml rename to migration/annotations/effect__MergeState.yaml diff --git a/repos/effect/migration/annotations/effect__MergeStrategy.yaml b/migration/annotations/effect__MergeStrategy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MergeStrategy.yaml rename to migration/annotations/effect__MergeStrategy.yaml diff --git a/repos/effect/migration/annotations/effect__Metric.yaml b/migration/annotations/effect__Metric.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Metric.yaml rename to migration/annotations/effect__Metric.yaml diff --git a/repos/effect/migration/annotations/effect__MetricBoundaries.yaml b/migration/annotations/effect__MetricBoundaries.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricBoundaries.yaml rename to migration/annotations/effect__MetricBoundaries.yaml diff --git a/repos/effect/migration/annotations/effect__MetricHook.yaml b/migration/annotations/effect__MetricHook.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricHook.yaml rename to migration/annotations/effect__MetricHook.yaml diff --git a/repos/effect/migration/annotations/effect__MetricKey.yaml b/migration/annotations/effect__MetricKey.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricKey.yaml rename to migration/annotations/effect__MetricKey.yaml diff --git a/repos/effect/migration/annotations/effect__MetricKeyType.yaml b/migration/annotations/effect__MetricKeyType.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricKeyType.yaml rename to migration/annotations/effect__MetricKeyType.yaml diff --git a/repos/effect/migration/annotations/effect__MetricLabel.yaml b/migration/annotations/effect__MetricLabel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricLabel.yaml rename to migration/annotations/effect__MetricLabel.yaml diff --git a/repos/effect/migration/annotations/effect__MetricPair.yaml b/migration/annotations/effect__MetricPair.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricPair.yaml rename to migration/annotations/effect__MetricPair.yaml diff --git a/repos/effect/migration/annotations/effect__MetricPolling.yaml b/migration/annotations/effect__MetricPolling.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricPolling.yaml rename to migration/annotations/effect__MetricPolling.yaml diff --git a/repos/effect/migration/annotations/effect__MetricRegistry.yaml b/migration/annotations/effect__MetricRegistry.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricRegistry.yaml rename to migration/annotations/effect__MetricRegistry.yaml diff --git a/repos/effect/migration/annotations/effect__MetricState.yaml b/migration/annotations/effect__MetricState.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MetricState.yaml rename to migration/annotations/effect__MetricState.yaml diff --git a/repos/effect/migration/annotations/effect__Micro.yaml b/migration/annotations/effect__Micro.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Micro.yaml rename to migration/annotations/effect__Micro.yaml diff --git a/repos/effect/migration/annotations/effect__ModuleVersion.yaml b/migration/annotations/effect__ModuleVersion.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ModuleVersion.yaml rename to migration/annotations/effect__ModuleVersion.yaml diff --git a/repos/effect/migration/annotations/effect__MutableHashMap.yaml b/migration/annotations/effect__MutableHashMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MutableHashMap.yaml rename to migration/annotations/effect__MutableHashMap.yaml diff --git a/repos/effect/migration/annotations/effect__MutableHashSet.yaml b/migration/annotations/effect__MutableHashSet.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MutableHashSet.yaml rename to migration/annotations/effect__MutableHashSet.yaml diff --git a/repos/effect/migration/annotations/effect__MutableList.yaml b/migration/annotations/effect__MutableList.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MutableList.yaml rename to migration/annotations/effect__MutableList.yaml diff --git a/repos/effect/migration/annotations/effect__MutableQueue.yaml b/migration/annotations/effect__MutableQueue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MutableQueue.yaml rename to migration/annotations/effect__MutableQueue.yaml diff --git a/repos/effect/migration/annotations/effect__MutableRef.yaml b/migration/annotations/effect__MutableRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__MutableRef.yaml rename to migration/annotations/effect__MutableRef.yaml diff --git a/repos/effect/migration/annotations/effect__Number.yaml b/migration/annotations/effect__Number.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Number.yaml rename to migration/annotations/effect__Number.yaml diff --git a/repos/effect/migration/annotations/effect__Option.yaml b/migration/annotations/effect__Option.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Option.yaml rename to migration/annotations/effect__Option.yaml diff --git a/repos/effect/migration/annotations/effect__Order.yaml b/migration/annotations/effect__Order.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Order.yaml rename to migration/annotations/effect__Order.yaml diff --git a/repos/effect/migration/annotations/effect__Ordering.yaml b/migration/annotations/effect__Ordering.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Ordering.yaml rename to migration/annotations/effect__Ordering.yaml diff --git a/repos/effect/migration/annotations/effect__ParseResult.yaml b/migration/annotations/effect__ParseResult.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ParseResult.yaml rename to migration/annotations/effect__ParseResult.yaml diff --git a/repos/effect/migration/annotations/effect__PartitionedSemaphore.yaml b/migration/annotations/effect__PartitionedSemaphore.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__PartitionedSemaphore.yaml rename to migration/annotations/effect__PartitionedSemaphore.yaml diff --git a/repos/effect/migration/annotations/effect__Pipeable.yaml b/migration/annotations/effect__Pipeable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Pipeable.yaml rename to migration/annotations/effect__Pipeable.yaml diff --git a/repos/effect/migration/annotations/effect__Pool.yaml b/migration/annotations/effect__Pool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Pool.yaml rename to migration/annotations/effect__Pool.yaml diff --git a/repos/effect/migration/annotations/effect__Predicate.yaml b/migration/annotations/effect__Predicate.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Predicate.yaml rename to migration/annotations/effect__Predicate.yaml diff --git a/repos/effect/migration/annotations/effect__Pretty.yaml b/migration/annotations/effect__Pretty.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Pretty.yaml rename to migration/annotations/effect__Pretty.yaml diff --git a/repos/effect/migration/annotations/effect__PubSub.yaml b/migration/annotations/effect__PubSub.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__PubSub.yaml rename to migration/annotations/effect__PubSub.yaml diff --git a/repos/effect/migration/annotations/effect__Queue.yaml b/migration/annotations/effect__Queue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Queue.yaml rename to migration/annotations/effect__Queue.yaml diff --git a/repos/effect/migration/annotations/effect__Random.yaml b/migration/annotations/effect__Random.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Random.yaml rename to migration/annotations/effect__Random.yaml diff --git a/repos/effect/migration/annotations/effect__RateLimiter.yaml b/migration/annotations/effect__RateLimiter.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RateLimiter.yaml rename to migration/annotations/effect__RateLimiter.yaml diff --git a/repos/effect/migration/annotations/effect__RcMap.yaml b/migration/annotations/effect__RcMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RcMap.yaml rename to migration/annotations/effect__RcMap.yaml diff --git a/repos/effect/migration/annotations/effect__RcRef.yaml b/migration/annotations/effect__RcRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RcRef.yaml rename to migration/annotations/effect__RcRef.yaml diff --git a/repos/effect/migration/annotations/effect__Readable.yaml b/migration/annotations/effect__Readable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Readable.yaml rename to migration/annotations/effect__Readable.yaml diff --git a/repos/effect/migration/annotations/effect__Record.yaml b/migration/annotations/effect__Record.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Record.yaml rename to migration/annotations/effect__Record.yaml diff --git a/repos/effect/migration/annotations/effect__RedBlackTree.yaml b/migration/annotations/effect__RedBlackTree.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RedBlackTree.yaml rename to migration/annotations/effect__RedBlackTree.yaml diff --git a/repos/effect/migration/annotations/effect__Redacted.yaml b/migration/annotations/effect__Redacted.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Redacted.yaml rename to migration/annotations/effect__Redacted.yaml diff --git a/repos/effect/migration/annotations/effect__Ref.yaml b/migration/annotations/effect__Ref.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Ref.yaml rename to migration/annotations/effect__Ref.yaml diff --git a/repos/effect/migration/annotations/effect__Reloadable.yaml b/migration/annotations/effect__Reloadable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Reloadable.yaml rename to migration/annotations/effect__Reloadable.yaml diff --git a/repos/effect/migration/annotations/effect__Request.yaml b/migration/annotations/effect__Request.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Request.yaml rename to migration/annotations/effect__Request.yaml diff --git a/repos/effect/migration/annotations/effect__RequestBlock.yaml b/migration/annotations/effect__RequestBlock.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RequestBlock.yaml rename to migration/annotations/effect__RequestBlock.yaml diff --git a/repos/effect/migration/annotations/effect__RequestResolver.yaml b/migration/annotations/effect__RequestResolver.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RequestResolver.yaml rename to migration/annotations/effect__RequestResolver.yaml diff --git a/repos/effect/migration/annotations/effect__Resource.yaml b/migration/annotations/effect__Resource.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Resource.yaml rename to migration/annotations/effect__Resource.yaml diff --git a/repos/effect/migration/annotations/effect__Runtime.yaml b/migration/annotations/effect__Runtime.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Runtime.yaml rename to migration/annotations/effect__Runtime.yaml diff --git a/repos/effect/migration/annotations/effect__RuntimeFlags.yaml b/migration/annotations/effect__RuntimeFlags.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RuntimeFlags.yaml rename to migration/annotations/effect__RuntimeFlags.yaml diff --git a/repos/effect/migration/annotations/effect__RuntimeFlagsPatch.yaml b/migration/annotations/effect__RuntimeFlagsPatch.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__RuntimeFlagsPatch.yaml rename to migration/annotations/effect__RuntimeFlagsPatch.yaml diff --git a/repos/effect/migration/annotations/effect__STM.yaml b/migration/annotations/effect__STM.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__STM.yaml rename to migration/annotations/effect__STM.yaml diff --git a/repos/effect/migration/annotations/effect__Schedule.yaml b/migration/annotations/effect__Schedule.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Schedule.yaml rename to migration/annotations/effect__Schedule.yaml diff --git a/repos/effect/migration/annotations/effect__ScheduleDecision.yaml b/migration/annotations/effect__ScheduleDecision.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ScheduleDecision.yaml rename to migration/annotations/effect__ScheduleDecision.yaml diff --git a/repos/effect/migration/annotations/effect__ScheduleInterval.yaml b/migration/annotations/effect__ScheduleInterval.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ScheduleInterval.yaml rename to migration/annotations/effect__ScheduleInterval.yaml diff --git a/repos/effect/migration/annotations/effect__ScheduleIntervals.yaml b/migration/annotations/effect__ScheduleIntervals.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ScheduleIntervals.yaml rename to migration/annotations/effect__ScheduleIntervals.yaml diff --git a/repos/effect/migration/annotations/effect__Scheduler.yaml b/migration/annotations/effect__Scheduler.yaml similarity index 86% rename from repos/effect/migration/annotations/effect__Scheduler.yaml rename to migration/annotations/effect__Scheduler.yaml index 06e3b0191..26b764e9a 100644 --- a/repos/effect/migration/annotations/effect__Scheduler.yaml +++ b/migration/annotations/effect__Scheduler.yaml @@ -18,7 +18,7 @@ effect/Scheduler#makeMatrix: note: "Matrix routing was removed; implement routing in a custom Scheduler and SchedulerDispatcher if still required." effect/Scheduler#MixedScheduler: replacement: "Scheduler.MixedScheduler" - note: "The class remains with a redesigned constructor and makeDispatcher-based task API." + note: "The class remains with a redesigned constructor and makeDispatcher-based task API. On Cloudflare Workers, request-owned scheduler timers can leave shared fibers suspended; provide new Scheduler.MixedScheduler(\"sync\") to both layers and requests and build handlers at module scope (see HttpRouter.toWebHandler). Sync mode does not yield to the event loop and does not fix request-owned Effect.sleep timers or request-bound async contexts." effect/Scheduler#PriorityBuckets: replacement: "none" note: "Priority buckets are now an internal Scheduler implementation detail." diff --git a/repos/effect/migration/annotations/effect__Schema.yaml b/migration/annotations/effect__Schema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Schema.yaml rename to migration/annotations/effect__Schema.yaml diff --git a/repos/effect/migration/annotations/effect__SchemaAST.yaml b/migration/annotations/effect__SchemaAST.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__SchemaAST.yaml rename to migration/annotations/effect__SchemaAST.yaml diff --git a/repos/effect/migration/annotations/effect__Scope.yaml b/migration/annotations/effect__Scope.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Scope.yaml rename to migration/annotations/effect__Scope.yaml diff --git a/repos/effect/migration/annotations/effect__ScopedCache.yaml b/migration/annotations/effect__ScopedCache.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ScopedCache.yaml rename to migration/annotations/effect__ScopedCache.yaml diff --git a/repos/effect/migration/annotations/effect__ScopedRef.yaml b/migration/annotations/effect__ScopedRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ScopedRef.yaml rename to migration/annotations/effect__ScopedRef.yaml diff --git a/repos/effect/migration/annotations/effect__Secret.yaml b/migration/annotations/effect__Secret.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Secret.yaml rename to migration/annotations/effect__Secret.yaml diff --git a/repos/effect/migration/annotations/effect__SingleProducerAsyncInput.yaml b/migration/annotations/effect__SingleProducerAsyncInput.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__SingleProducerAsyncInput.yaml rename to migration/annotations/effect__SingleProducerAsyncInput.yaml diff --git a/repos/effect/migration/annotations/effect__Sink.yaml b/migration/annotations/effect__Sink.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Sink.yaml rename to migration/annotations/effect__Sink.yaml diff --git a/repos/effect/migration/annotations/effect__SortedMap.yaml b/migration/annotations/effect__SortedMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__SortedMap.yaml rename to migration/annotations/effect__SortedMap.yaml diff --git a/repos/effect/migration/annotations/effect__SortedSet.yaml b/migration/annotations/effect__SortedSet.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__SortedSet.yaml rename to migration/annotations/effect__SortedSet.yaml diff --git a/repos/effect/migration/annotations/effect__Stream.yaml b/migration/annotations/effect__Stream.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Stream.yaml rename to migration/annotations/effect__Stream.yaml diff --git a/repos/effect/migration/annotations/effect__StreamEmit.yaml b/migration/annotations/effect__StreamEmit.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__StreamEmit.yaml rename to migration/annotations/effect__StreamEmit.yaml diff --git a/repos/effect/migration/annotations/effect__StreamHaltStrategy.yaml b/migration/annotations/effect__StreamHaltStrategy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__StreamHaltStrategy.yaml rename to migration/annotations/effect__StreamHaltStrategy.yaml diff --git a/repos/effect/migration/annotations/effect__Streamable.yaml b/migration/annotations/effect__Streamable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Streamable.yaml rename to migration/annotations/effect__Streamable.yaml diff --git a/repos/effect/migration/annotations/effect__Struct.yaml b/migration/annotations/effect__Struct.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Struct.yaml rename to migration/annotations/effect__Struct.yaml diff --git a/repos/effect/migration/annotations/effect__Subscribable.yaml b/migration/annotations/effect__Subscribable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Subscribable.yaml rename to migration/annotations/effect__Subscribable.yaml diff --git a/repos/effect/migration/annotations/effect__SubscriptionRef.yaml b/migration/annotations/effect__SubscriptionRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__SubscriptionRef.yaml rename to migration/annotations/effect__SubscriptionRef.yaml diff --git a/repos/effect/migration/annotations/effect__Supervisor.yaml b/migration/annotations/effect__Supervisor.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Supervisor.yaml rename to migration/annotations/effect__Supervisor.yaml diff --git a/repos/effect/migration/annotations/effect__Symbol.yaml b/migration/annotations/effect__Symbol.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Symbol.yaml rename to migration/annotations/effect__Symbol.yaml diff --git a/repos/effect/migration/annotations/effect__SynchronizedRef.yaml b/migration/annotations/effect__SynchronizedRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__SynchronizedRef.yaml rename to migration/annotations/effect__SynchronizedRef.yaml diff --git a/repos/effect/migration/annotations/effect__TArray.yaml b/migration/annotations/effect__TArray.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TArray.yaml rename to migration/annotations/effect__TArray.yaml diff --git a/repos/effect/migration/annotations/effect__TDeferred.yaml b/migration/annotations/effect__TDeferred.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TDeferred.yaml rename to migration/annotations/effect__TDeferred.yaml diff --git a/repos/effect/migration/annotations/effect__TMap.yaml b/migration/annotations/effect__TMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TMap.yaml rename to migration/annotations/effect__TMap.yaml diff --git a/repos/effect/migration/annotations/effect__TPriorityQueue.yaml b/migration/annotations/effect__TPriorityQueue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TPriorityQueue.yaml rename to migration/annotations/effect__TPriorityQueue.yaml diff --git a/repos/effect/migration/annotations/effect__TPubSub.yaml b/migration/annotations/effect__TPubSub.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TPubSub.yaml rename to migration/annotations/effect__TPubSub.yaml diff --git a/repos/effect/migration/annotations/effect__TQueue.yaml b/migration/annotations/effect__TQueue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TQueue.yaml rename to migration/annotations/effect__TQueue.yaml diff --git a/repos/effect/migration/annotations/effect__TRandom.yaml b/migration/annotations/effect__TRandom.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TRandom.yaml rename to migration/annotations/effect__TRandom.yaml diff --git a/repos/effect/migration/annotations/effect__TReentrantLock.yaml b/migration/annotations/effect__TReentrantLock.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TReentrantLock.yaml rename to migration/annotations/effect__TReentrantLock.yaml diff --git a/repos/effect/migration/annotations/effect__TRef.yaml b/migration/annotations/effect__TRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TRef.yaml rename to migration/annotations/effect__TRef.yaml diff --git a/repos/effect/migration/annotations/effect__TSemaphore.yaml b/migration/annotations/effect__TSemaphore.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TSemaphore.yaml rename to migration/annotations/effect__TSemaphore.yaml diff --git a/repos/effect/migration/annotations/effect__TSet.yaml b/migration/annotations/effect__TSet.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TSet.yaml rename to migration/annotations/effect__TSet.yaml diff --git a/repos/effect/migration/annotations/effect__TSubscriptionRef.yaml b/migration/annotations/effect__TSubscriptionRef.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TSubscriptionRef.yaml rename to migration/annotations/effect__TSubscriptionRef.yaml diff --git a/repos/effect/migration/annotations/effect__Take.yaml b/migration/annotations/effect__Take.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Take.yaml rename to migration/annotations/effect__Take.yaml diff --git a/repos/effect/migration/annotations/effect__TestAnnotation.yaml b/migration/annotations/effect__TestAnnotation.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestAnnotation.yaml rename to migration/annotations/effect__TestAnnotation.yaml diff --git a/repos/effect/migration/annotations/effect__TestAnnotationMap.yaml b/migration/annotations/effect__TestAnnotationMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestAnnotationMap.yaml rename to migration/annotations/effect__TestAnnotationMap.yaml diff --git a/repos/effect/migration/annotations/effect__TestAnnotations.yaml b/migration/annotations/effect__TestAnnotations.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestAnnotations.yaml rename to migration/annotations/effect__TestAnnotations.yaml diff --git a/repos/effect/migration/annotations/effect__TestClock.yaml b/migration/annotations/effect__TestClock.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestClock.yaml rename to migration/annotations/effect__TestClock.yaml diff --git a/repos/effect/migration/annotations/effect__TestConfig.yaml b/migration/annotations/effect__TestConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestConfig.yaml rename to migration/annotations/effect__TestConfig.yaml diff --git a/repos/effect/migration/annotations/effect__TestContext.yaml b/migration/annotations/effect__TestContext.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestContext.yaml rename to migration/annotations/effect__TestContext.yaml diff --git a/repos/effect/migration/annotations/effect__TestLive.yaml b/migration/annotations/effect__TestLive.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestLive.yaml rename to migration/annotations/effect__TestLive.yaml diff --git a/repos/effect/migration/annotations/effect__TestServices.yaml b/migration/annotations/effect__TestServices.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestServices.yaml rename to migration/annotations/effect__TestServices.yaml diff --git a/repos/effect/migration/annotations/effect__TestSized.yaml b/migration/annotations/effect__TestSized.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__TestSized.yaml rename to migration/annotations/effect__TestSized.yaml diff --git a/repos/effect/migration/annotations/effect__Tracer.yaml b/migration/annotations/effect__Tracer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Tracer.yaml rename to migration/annotations/effect__Tracer.yaml diff --git a/repos/effect/migration/annotations/effect__Trie.yaml b/migration/annotations/effect__Trie.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Trie.yaml rename to migration/annotations/effect__Trie.yaml diff --git a/repos/effect/migration/annotations/effect__Tuple.yaml b/migration/annotations/effect__Tuple.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Tuple.yaml rename to migration/annotations/effect__Tuple.yaml diff --git a/repos/effect/migration/annotations/effect__Types.yaml b/migration/annotations/effect__Types.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Types.yaml rename to migration/annotations/effect__Types.yaml diff --git a/repos/effect/migration/annotations/effect__UpstreamPullRequest.yaml b/migration/annotations/effect__UpstreamPullRequest.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__UpstreamPullRequest.yaml rename to migration/annotations/effect__UpstreamPullRequest.yaml diff --git a/repos/effect/migration/annotations/effect__UpstreamPullStrategy.yaml b/migration/annotations/effect__UpstreamPullStrategy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__UpstreamPullStrategy.yaml rename to migration/annotations/effect__UpstreamPullStrategy.yaml diff --git a/repos/effect/migration/annotations/effect__Utils.yaml b/migration/annotations/effect__Utils.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__Utils.yaml rename to migration/annotations/effect__Utils.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock.yaml b/migration/annotations/effect__ai-amazon-bedrock.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock.yaml rename to migration/annotations/effect__ai-amazon-bedrock.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockClient.yaml b/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockClient.yaml rename to migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockClient.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockConfig.yaml b/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockConfig.yaml rename to migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockConfig.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockLanguageModel.yaml b/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockLanguageModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockLanguageModel.yaml rename to migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockLanguageModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockSchema.yaml b/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockSchema.yaml rename to migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockSchema.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockTool.yaml b/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockTool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockTool.yaml rename to migration/annotations/effect__ai-amazon-bedrock__AmazonBedrockTool.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__EventStreamEncoding.yaml b/migration/annotations/effect__ai-amazon-bedrock__EventStreamEncoding.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__EventStreamEncoding.yaml rename to migration/annotations/effect__ai-amazon-bedrock__EventStreamEncoding.yaml diff --git a/repos/effect/migration/annotations/effect__ai-amazon-bedrock__index.yaml b/migration/annotations/effect__ai-amazon-bedrock__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-amazon-bedrock__index.yaml rename to migration/annotations/effect__ai-amazon-bedrock__index.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicClient.yaml b/migration/annotations/effect__ai-anthropic__AnthropicClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__AnthropicClient.yaml rename to migration/annotations/effect__ai-anthropic__AnthropicClient.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicConfig.yaml b/migration/annotations/effect__ai-anthropic__AnthropicConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__AnthropicConfig.yaml rename to migration/annotations/effect__ai-anthropic__AnthropicConfig.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicLanguageModel.yaml b/migration/annotations/effect__ai-anthropic__AnthropicLanguageModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__AnthropicLanguageModel.yaml rename to migration/annotations/effect__ai-anthropic__AnthropicLanguageModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml b/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml rename to migration/annotations/effect__ai-anthropic__AnthropicTokenizer.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTool.yaml b/migration/annotations/effect__ai-anthropic__AnthropicTool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__AnthropicTool.yaml rename to migration/annotations/effect__ai-anthropic__AnthropicTool.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__Generated.yaml b/migration/annotations/effect__ai-anthropic__Generated.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__Generated.yaml rename to migration/annotations/effect__ai-anthropic__Generated.yaml diff --git a/repos/effect/migration/annotations/effect__ai-anthropic__index.yaml b/migration/annotations/effect__ai-anthropic__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-anthropic__index.yaml rename to migration/annotations/effect__ai-anthropic__index.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google.yaml b/migration/annotations/effect__ai-google.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google.yaml rename to migration/annotations/effect__ai-google.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google__Generated.yaml b/migration/annotations/effect__ai-google__Generated.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google__Generated.yaml rename to migration/annotations/effect__ai-google__Generated.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google__GoogleClient.yaml b/migration/annotations/effect__ai-google__GoogleClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google__GoogleClient.yaml rename to migration/annotations/effect__ai-google__GoogleClient.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google__GoogleConfig.yaml b/migration/annotations/effect__ai-google__GoogleConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google__GoogleConfig.yaml rename to migration/annotations/effect__ai-google__GoogleConfig.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google__GoogleLanguageModel.yaml b/migration/annotations/effect__ai-google__GoogleLanguageModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google__GoogleLanguageModel.yaml rename to migration/annotations/effect__ai-google__GoogleLanguageModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google__GoogleTool.yaml b/migration/annotations/effect__ai-google__GoogleTool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google__GoogleTool.yaml rename to migration/annotations/effect__ai-google__GoogleTool.yaml diff --git a/repos/effect/migration/annotations/effect__ai-google__index.yaml b/migration/annotations/effect__ai-google__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-google__index.yaml rename to migration/annotations/effect__ai-google__index.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__Generated.yaml b/migration/annotations/effect__ai-openai__Generated.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__Generated.yaml rename to migration/annotations/effect__ai-openai__Generated.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiClient.yaml b/migration/annotations/effect__ai-openai__OpenAiClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiClient.yaml rename to migration/annotations/effect__ai-openai__OpenAiClient.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiConfig.yaml b/migration/annotations/effect__ai-openai__OpenAiConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiConfig.yaml rename to migration/annotations/effect__ai-openai__OpenAiConfig.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiEmbeddingModel.yaml b/migration/annotations/effect__ai-openai__OpenAiEmbeddingModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiEmbeddingModel.yaml rename to migration/annotations/effect__ai-openai__OpenAiEmbeddingModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiLanguageModel.yaml b/migration/annotations/effect__ai-openai__OpenAiLanguageModel.yaml similarity index 68% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiLanguageModel.yaml rename to migration/annotations/effect__ai-openai__OpenAiLanguageModel.yaml index 21af9e0d6..0c8abcd15 100644 --- a/repos/effect/migration/annotations/effect__ai-openai__OpenAiLanguageModel.yaml +++ b/migration/annotations/effect__ai-openai__OpenAiLanguageModel.yaml @@ -1,9 +1,9 @@ "@effect/ai-openai/OpenAiLanguageModel#Config": replacement: "OpenAiLanguageModel.Config" - note: "Still exported in v4; update imports and adapt to the revised v4 service and schema types." + note: "Still exported in v4; update imports and adapt to the revised service and schema types. Unrecognized model names now default to reasoning models, sending system prompts as developer messages and requesting reasoning.encrypted_content when item references are disabled or WebSockets are used. Set reasoningModel: false in config for deployments that do not support these defaults." "@effect/ai-openai/OpenAiLanguageModel#Config.Service": replacement: "OpenAiLanguageModel.Config.Service" - note: "Still exported in v4; update imports and adapt to the revised v4 service and schema types." + note: "Still exported in v4 with revised configuration fields. Set reasoningModel: false for custom deployments that need system messages rather than developer messages and do not support automatic encrypted reasoning requests; unrecognized model names otherwise default to reasoning models. Fine-tuned ft: names follow their base model." "@effect/ai-openai/OpenAiLanguageModel#layerWithTokenizer": replacement: "OpenAiLanguageModel.layer" note: "The tokenizer-combining layer was removed; provide the language model and any Tokenizer service separately." diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml b/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml similarity index 78% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml rename to migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml index 0f1a0c05b..ba310c82b 100644 --- a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml +++ b/migration/annotations/effect__ai-openai__OpenAiTelemetry.yaml @@ -13,3 +13,7 @@ "@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." +"@effect/ai-openai/OpenAiTelemetry#OpenAiTelemetryAttributes": + include: true + replacement: "OpenAiTelemetry.OpenAiTelemetryAttributes" + note: "Use openai.request.* for request keys and openai.response.* for response keys instead of gen_ai.openai.*. The response-format attribute was removed; use gen_ai.output.type from LanguageModel instead." diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml b/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml rename to migration/annotations/effect__ai-openai__OpenAiTokenizer.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__OpenAiTool.yaml b/migration/annotations/effect__ai-openai__OpenAiTool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__OpenAiTool.yaml rename to migration/annotations/effect__ai-openai__OpenAiTool.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openai__index.yaml b/migration/annotations/effect__ai-openai__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openai__index.yaml rename to migration/annotations/effect__ai-openai__index.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openrouter__Generated.yaml b/migration/annotations/effect__ai-openrouter__Generated.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openrouter__Generated.yaml rename to migration/annotations/effect__ai-openrouter__Generated.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml b/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml rename to migration/annotations/effect__ai-openrouter__OpenRouterClient.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterConfig.yaml b/migration/annotations/effect__ai-openrouter__OpenRouterConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterConfig.yaml rename to migration/annotations/effect__ai-openrouter__OpenRouterConfig.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterLanguageModel.yaml b/migration/annotations/effect__ai-openrouter__OpenRouterLanguageModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openrouter__OpenRouterLanguageModel.yaml rename to migration/annotations/effect__ai-openrouter__OpenRouterLanguageModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai-openrouter__index.yaml b/migration/annotations/effect__ai-openrouter__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai-openrouter__index.yaml rename to migration/annotations/effect__ai-openrouter__index.yaml diff --git a/repos/effect/migration/annotations/effect__ai.yaml b/migration/annotations/effect__ai.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai.yaml rename to migration/annotations/effect__ai.yaml diff --git a/repos/effect/migration/annotations/effect__ai__AiError.yaml b/migration/annotations/effect__ai__AiError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__AiError.yaml rename to migration/annotations/effect__ai__AiError.yaml diff --git a/repos/effect/migration/annotations/effect__ai__Chat.yaml b/migration/annotations/effect__ai__Chat.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__Chat.yaml rename to migration/annotations/effect__ai__Chat.yaml diff --git a/repos/effect/migration/annotations/effect__ai__EmbeddingModel.yaml b/migration/annotations/effect__ai__EmbeddingModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__EmbeddingModel.yaml rename to migration/annotations/effect__ai__EmbeddingModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai__IdGenerator.yaml b/migration/annotations/effect__ai__IdGenerator.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__IdGenerator.yaml rename to migration/annotations/effect__ai__IdGenerator.yaml diff --git a/repos/effect/migration/annotations/effect__ai__LanguageModel.yaml b/migration/annotations/effect__ai__LanguageModel.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__LanguageModel.yaml rename to migration/annotations/effect__ai__LanguageModel.yaml diff --git a/repos/effect/migration/annotations/effect__ai__McpSchema.yaml b/migration/annotations/effect__ai__McpSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__McpSchema.yaml rename to migration/annotations/effect__ai__McpSchema.yaml diff --git a/repos/effect/migration/annotations/effect__ai__McpServer.yaml b/migration/annotations/effect__ai__McpServer.yaml similarity index 74% rename from repos/effect/migration/annotations/effect__ai__McpServer.yaml rename to migration/annotations/effect__ai__McpServer.yaml index 51598405d..66e2f5e03 100644 --- a/repos/effect/migration/annotations/effect__ai__McpServer.yaml +++ b/migration/annotations/effect__ai__McpServer.yaml @@ -18,10 +18,10 @@ note: "The service is now a Context.Service. initializedClients and notificationsMailbox are no longer exposed. Registration callbacks use McpSchema.McpRequestContext; McpServerClient is only supplied for initialized stateful requests. Prefer registerToolkit, registerResource, and registerPrompt over implementing the registry shape directly." "@effect/ai/McpServer#registerToolkit": replacement: "McpServer.registerToolkit" - note: "Moved to effect/ai/McpServer. Handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient. Strict tools reject excess input properties; raw JSON Schema dynamic tools cannot use strict mode. Declared handler failures produce isError results, while parameter validation fails with InvalidParams." + note: "Moved to effect/ai/McpServer. Requirements from every tool handler are propagated, excluding McpSchema.McpRequestContext instead of McpServerClient; provide the remaining handler services. Strict tools reject excess input properties; raw JSON Schema dynamic tools cannot use strict mode. Declared handler failures produce isError results, while parameter validation fails with InvalidParams." "@effect/ai/McpServer#toolkit": replacement: "McpServer.toolkit" - note: "Moved to effect/ai/McpServer. The registration layer supplies McpSchema.McpRequestContext to handlers instead of excluding McpServerClient from requirements. Strict tools reject excess properties and require an Effect Schema rather than raw dynamic JSON Schema." + note: "Moved to effect/ai/McpServer. The registration layer supplies McpSchema.McpRequestContext to handlers instead of excluding McpServerClient from requirements. Its requirements include services from every tool handler. Strict tools reject excess properties and require an Effect Schema rather than raw dynamic JSON Schema." "@effect/ai/McpServer#registerResource": replacement: "McpServer.registerResource" note: "Moved to effect/ai/McpServer. Resource and completion handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata." @@ -30,7 +30,7 @@ note: "Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from resource and completion handler requirements instead of McpServerClient." "@effect/ai/McpServer#registerPrompt": replacement: "McpServer.registerPrompt" - note: "Moved to effect/ai/McpServer. Prompt decoding and handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata." + note: "Moved to effect/ai/McpServer. Prompt decoding, handler, and completion callback requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata." "@effect/ai/McpServer#prompt": replacement: "McpServer.prompt" - note: "Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from prompt decoding and handler requirements instead of McpServerClient." + note: "Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from prompt decoding, handler, and completion callback requirements instead of McpServerClient." diff --git a/repos/effect/migration/annotations/effect__ai__Model.yaml b/migration/annotations/effect__ai__Model.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__Model.yaml rename to migration/annotations/effect__ai__Model.yaml diff --git a/repos/effect/migration/annotations/effect__ai__Prompt.yaml b/migration/annotations/effect__ai__Prompt.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__Prompt.yaml rename to migration/annotations/effect__ai__Prompt.yaml diff --git a/repos/effect/migration/annotations/effect__ai__Response.yaml b/migration/annotations/effect__ai__Response.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__Response.yaml rename to migration/annotations/effect__ai__Response.yaml diff --git a/repos/effect/migration/annotations/effect__ai__Telemetry.yaml b/migration/annotations/effect__ai__Telemetry.yaml similarity index 78% rename from repos/effect/migration/annotations/effect__ai__Telemetry.yaml rename to migration/annotations/effect__ai__Telemetry.yaml index f815ee141..cd2d7b132 100644 --- a/repos/effect/migration/annotations/effect__ai__Telemetry.yaml +++ b/migration/annotations/effect__ai__Telemetry.yaml @@ -13,3 +13,7 @@ "@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." +"@effect/ai/Telemetry#GenAITelemetryAttributes": + include: true + replacement: "Telemetry.GenAITelemetryAttributes" + note: "The flat span attribute keys changed: replace gen_ai.system with gen_ai.provider.name and remove gen_ai.token.type. gen_ai.output.type records text or json. Update code constructing or indexing this attribute type." diff --git a/repos/effect/migration/annotations/effect__ai__Tool.yaml b/migration/annotations/effect__ai__Tool.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__Tool.yaml rename to migration/annotations/effect__ai__Tool.yaml diff --git a/repos/effect/migration/annotations/effect__ai__Toolkit.yaml b/migration/annotations/effect__ai__Toolkit.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__Toolkit.yaml rename to migration/annotations/effect__ai__Toolkit.yaml diff --git a/repos/effect/migration/annotations/effect__ai__index.yaml b/migration/annotations/effect__ai__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__ai__index.yaml rename to migration/annotations/effect__ai__index.yaml diff --git a/repos/effect/migration/annotations/effect__cli.yaml b/migration/annotations/effect__cli.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli.yaml rename to migration/annotations/effect__cli.yaml diff --git a/repos/effect/migration/annotations/effect__cli__Args.yaml b/migration/annotations/effect__cli__Args.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__Args.yaml rename to migration/annotations/effect__cli__Args.yaml diff --git a/repos/effect/migration/annotations/effect__cli__AutoCorrect.yaml b/migration/annotations/effect__cli__AutoCorrect.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__AutoCorrect.yaml rename to migration/annotations/effect__cli__AutoCorrect.yaml diff --git a/repos/effect/migration/annotations/effect__cli__BuiltInOptions.yaml b/migration/annotations/effect__cli__BuiltInOptions.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__BuiltInOptions.yaml rename to migration/annotations/effect__cli__BuiltInOptions.yaml diff --git a/repos/effect/migration/annotations/effect__cli__CliApp.yaml b/migration/annotations/effect__cli__CliApp.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__CliApp.yaml rename to migration/annotations/effect__cli__CliApp.yaml diff --git a/repos/effect/migration/annotations/effect__cli__CliConfig.yaml b/migration/annotations/effect__cli__CliConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__CliConfig.yaml rename to migration/annotations/effect__cli__CliConfig.yaml diff --git a/repos/effect/migration/annotations/effect__cli__Command.yaml b/migration/annotations/effect__cli__Command.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__Command.yaml rename to migration/annotations/effect__cli__Command.yaml diff --git a/repos/effect/migration/annotations/effect__cli__CommandDescriptor.yaml b/migration/annotations/effect__cli__CommandDescriptor.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__CommandDescriptor.yaml rename to migration/annotations/effect__cli__CommandDescriptor.yaml diff --git a/repos/effect/migration/annotations/effect__cli__CommandDirective.yaml b/migration/annotations/effect__cli__CommandDirective.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__CommandDirective.yaml rename to migration/annotations/effect__cli__CommandDirective.yaml diff --git a/repos/effect/migration/annotations/effect__cli__ConfigFile.yaml b/migration/annotations/effect__cli__ConfigFile.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__ConfigFile.yaml rename to migration/annotations/effect__cli__ConfigFile.yaml diff --git a/repos/effect/migration/annotations/effect__cli__HelpDoc.yaml b/migration/annotations/effect__cli__HelpDoc.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__HelpDoc.yaml rename to migration/annotations/effect__cli__HelpDoc.yaml diff --git a/repos/effect/migration/annotations/effect__cli__HelpDoc__Span.yaml b/migration/annotations/effect__cli__HelpDoc__Span.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__HelpDoc__Span.yaml rename to migration/annotations/effect__cli__HelpDoc__Span.yaml diff --git a/repos/effect/migration/annotations/effect__cli__Options.yaml b/migration/annotations/effect__cli__Options.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__Options.yaml rename to migration/annotations/effect__cli__Options.yaml diff --git a/repos/effect/migration/annotations/effect__cli__Primitive.yaml b/migration/annotations/effect__cli__Primitive.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__Primitive.yaml rename to migration/annotations/effect__cli__Primitive.yaml diff --git a/repos/effect/migration/annotations/effect__cli__Prompt.yaml b/migration/annotations/effect__cli__Prompt.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__Prompt.yaml rename to migration/annotations/effect__cli__Prompt.yaml diff --git a/repos/effect/migration/annotations/effect__cli__Usage.yaml b/migration/annotations/effect__cli__Usage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__Usage.yaml rename to migration/annotations/effect__cli__Usage.yaml diff --git a/repos/effect/migration/annotations/effect__cli__ValidationError.yaml b/migration/annotations/effect__cli__ValidationError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__ValidationError.yaml rename to migration/annotations/effect__cli__ValidationError.yaml diff --git a/repos/effect/migration/annotations/effect__cli__index.yaml b/migration/annotations/effect__cli__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cli__index.yaml rename to migration/annotations/effect__cli__index.yaml diff --git a/repos/effect/migration/annotations/effect__cluster.yaml b/migration/annotations/effect__cluster.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster.yaml rename to migration/annotations/effect__cluster.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterCron.yaml b/migration/annotations/effect__cluster__ClusterCron.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ClusterCron.yaml rename to migration/annotations/effect__cluster__ClusterCron.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterError.yaml b/migration/annotations/effect__cluster__ClusterError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ClusterError.yaml rename to migration/annotations/effect__cluster__ClusterError.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterSchema.yaml b/migration/annotations/effect__cluster__ClusterSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ClusterSchema.yaml rename to migration/annotations/effect__cluster__ClusterSchema.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml b/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml rename to migration/annotations/effect__cluster__ClusterWorkflowEngine.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__DeliverAt.yaml b/migration/annotations/effect__cluster__DeliverAt.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__DeliverAt.yaml rename to migration/annotations/effect__cluster__DeliverAt.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Entity.yaml b/migration/annotations/effect__cluster__Entity.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Entity.yaml rename to migration/annotations/effect__cluster__Entity.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__EntityAddress.yaml b/migration/annotations/effect__cluster__EntityAddress.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__EntityAddress.yaml rename to migration/annotations/effect__cluster__EntityAddress.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__EntityId.yaml b/migration/annotations/effect__cluster__EntityId.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__EntityId.yaml rename to migration/annotations/effect__cluster__EntityId.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__EntityProxy.yaml b/migration/annotations/effect__cluster__EntityProxy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__EntityProxy.yaml rename to migration/annotations/effect__cluster__EntityProxy.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__EntityProxyServer.yaml b/migration/annotations/effect__cluster__EntityProxyServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__EntityProxyServer.yaml rename to migration/annotations/effect__cluster__EntityProxyServer.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__EntityResource.yaml b/migration/annotations/effect__cluster__EntityResource.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__EntityResource.yaml rename to migration/annotations/effect__cluster__EntityResource.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Envelope.yaml b/migration/annotations/effect__cluster__Envelope.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Envelope.yaml rename to migration/annotations/effect__cluster__Envelope.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__HttpRunner.yaml b/migration/annotations/effect__cluster__HttpRunner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__HttpRunner.yaml rename to migration/annotations/effect__cluster__HttpRunner.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__K8sHttpClient.yaml b/migration/annotations/effect__cluster__K8sHttpClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__K8sHttpClient.yaml rename to migration/annotations/effect__cluster__K8sHttpClient.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__MachineId.yaml b/migration/annotations/effect__cluster__MachineId.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__MachineId.yaml rename to migration/annotations/effect__cluster__MachineId.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Message.yaml b/migration/annotations/effect__cluster__Message.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Message.yaml rename to migration/annotations/effect__cluster__Message.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__MessageStorage.yaml b/migration/annotations/effect__cluster__MessageStorage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__MessageStorage.yaml rename to migration/annotations/effect__cluster__MessageStorage.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Reply.yaml b/migration/annotations/effect__cluster__Reply.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Reply.yaml rename to migration/annotations/effect__cluster__Reply.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Runner.yaml b/migration/annotations/effect__cluster__Runner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Runner.yaml rename to migration/annotations/effect__cluster__Runner.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerAddress.yaml b/migration/annotations/effect__cluster__RunnerAddress.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__RunnerAddress.yaml rename to migration/annotations/effect__cluster__RunnerAddress.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerHealth.yaml b/migration/annotations/effect__cluster__RunnerHealth.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__RunnerHealth.yaml rename to migration/annotations/effect__cluster__RunnerHealth.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerServer.yaml b/migration/annotations/effect__cluster__RunnerServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__RunnerServer.yaml rename to migration/annotations/effect__cluster__RunnerServer.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__RunnerStorage.yaml b/migration/annotations/effect__cluster__RunnerStorage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__RunnerStorage.yaml rename to migration/annotations/effect__cluster__RunnerStorage.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Runners.yaml b/migration/annotations/effect__cluster__Runners.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Runners.yaml rename to migration/annotations/effect__cluster__Runners.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ShardId.yaml b/migration/annotations/effect__cluster__ShardId.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ShardId.yaml rename to migration/annotations/effect__cluster__ShardId.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Sharding.yaml b/migration/annotations/effect__cluster__Sharding.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Sharding.yaml rename to migration/annotations/effect__cluster__Sharding.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ShardingConfig.yaml b/migration/annotations/effect__cluster__ShardingConfig.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ShardingConfig.yaml rename to migration/annotations/effect__cluster__ShardingConfig.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml b/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml rename to migration/annotations/effect__cluster__ShardingRegistrationEvent.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__SingleRunner.yaml b/migration/annotations/effect__cluster__SingleRunner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__SingleRunner.yaml rename to migration/annotations/effect__cluster__SingleRunner.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Singleton.yaml b/migration/annotations/effect__cluster__Singleton.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Singleton.yaml rename to migration/annotations/effect__cluster__Singleton.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__SingletonAddress.yaml b/migration/annotations/effect__cluster__SingletonAddress.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__SingletonAddress.yaml rename to migration/annotations/effect__cluster__SingletonAddress.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__Snowflake.yaml b/migration/annotations/effect__cluster__Snowflake.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__Snowflake.yaml rename to migration/annotations/effect__cluster__Snowflake.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__SocketRunner.yaml b/migration/annotations/effect__cluster__SocketRunner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__SocketRunner.yaml rename to migration/annotations/effect__cluster__SocketRunner.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__SqlMessageStorage.yaml b/migration/annotations/effect__cluster__SqlMessageStorage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__SqlMessageStorage.yaml rename to migration/annotations/effect__cluster__SqlMessageStorage.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__SqlRunnerStorage.yaml b/migration/annotations/effect__cluster__SqlRunnerStorage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__SqlRunnerStorage.yaml rename to migration/annotations/effect__cluster__SqlRunnerStorage.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__TestRunner.yaml b/migration/annotations/effect__cluster__TestRunner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__TestRunner.yaml rename to migration/annotations/effect__cluster__TestRunner.yaml diff --git a/repos/effect/migration/annotations/effect__cluster__index.yaml b/migration/annotations/effect__cluster__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__cluster__index.yaml rename to migration/annotations/effect__cluster__index.yaml diff --git a/repos/effect/migration/annotations/effect__experimental.yaml b/migration/annotations/effect__experimental.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental.yaml rename to migration/annotations/effect__experimental.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools.yaml b/migration/annotations/effect__experimental__DevTools.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__DevTools.yaml rename to migration/annotations/effect__experimental__DevTools.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools__Client.yaml b/migration/annotations/effect__experimental__DevTools__Client.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__DevTools__Client.yaml rename to migration/annotations/effect__experimental__DevTools__Client.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools__Domain.yaml b/migration/annotations/effect__experimental__DevTools__Domain.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__DevTools__Domain.yaml rename to migration/annotations/effect__experimental__DevTools__Domain.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__DevTools__Server.yaml b/migration/annotations/effect__experimental__DevTools__Server.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__DevTools__Server.yaml rename to migration/annotations/effect__experimental__DevTools__Server.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Event.yaml b/migration/annotations/effect__experimental__Event.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Event.yaml rename to migration/annotations/effect__experimental__Event.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__EventGroup.yaml b/migration/annotations/effect__experimental__EventGroup.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__EventGroup.yaml rename to migration/annotations/effect__experimental__EventGroup.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__EventJournal.yaml b/migration/annotations/effect__experimental__EventJournal.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__EventJournal.yaml rename to migration/annotations/effect__experimental__EventJournal.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__EventLog.yaml b/migration/annotations/effect__experimental__EventLog.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__EventLog.yaml rename to migration/annotations/effect__experimental__EventLog.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__EventLogRemote.yaml b/migration/annotations/effect__experimental__EventLogRemote.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__EventLogRemote.yaml rename to migration/annotations/effect__experimental__EventLogRemote.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__EventLogServer.yaml b/migration/annotations/effect__experimental__EventLogServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__EventLogServer.yaml rename to migration/annotations/effect__experimental__EventLogServer.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__EventLogServer__Cloudflare.yaml b/migration/annotations/effect__experimental__EventLogServer__Cloudflare.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__EventLogServer__Cloudflare.yaml rename to migration/annotations/effect__experimental__EventLogServer__Cloudflare.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Machine.yaml b/migration/annotations/effect__experimental__Machine.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Machine.yaml rename to migration/annotations/effect__experimental__Machine.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Machine__Procedure.yaml b/migration/annotations/effect__experimental__Machine__Procedure.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Machine__Procedure.yaml rename to migration/annotations/effect__experimental__Machine__Procedure.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Machine__ProcedureList.yaml b/migration/annotations/effect__experimental__Machine__ProcedureList.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Machine__ProcedureList.yaml rename to migration/annotations/effect__experimental__Machine__ProcedureList.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Machine__SerializableProcedureList.yaml b/migration/annotations/effect__experimental__Machine__SerializableProcedureList.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Machine__SerializableProcedureList.yaml rename to migration/annotations/effect__experimental__Machine__SerializableProcedureList.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__PersistedCache.yaml b/migration/annotations/effect__experimental__PersistedCache.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__PersistedCache.yaml rename to migration/annotations/effect__experimental__PersistedCache.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__PersistedQueue.yaml b/migration/annotations/effect__experimental__PersistedQueue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__PersistedQueue.yaml rename to migration/annotations/effect__experimental__PersistedQueue.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml b/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__PersistedQueue__Redis.yaml rename to migration/annotations/effect__experimental__PersistedQueue__Redis.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Persistence.yaml b/migration/annotations/effect__experimental__Persistence.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Persistence.yaml rename to migration/annotations/effect__experimental__Persistence.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Persistence__Lmdb.yaml b/migration/annotations/effect__experimental__Persistence__Lmdb.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Persistence__Lmdb.yaml rename to migration/annotations/effect__experimental__Persistence__Lmdb.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Persistence__Redis.yaml b/migration/annotations/effect__experimental__Persistence__Redis.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Persistence__Redis.yaml rename to migration/annotations/effect__experimental__Persistence__Redis.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__RateLimiter.yaml b/migration/annotations/effect__experimental__RateLimiter.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__RateLimiter.yaml rename to migration/annotations/effect__experimental__RateLimiter.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__RateLimiter__Redis.yaml b/migration/annotations/effect__experimental__RateLimiter__Redis.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__RateLimiter__Redis.yaml rename to migration/annotations/effect__experimental__RateLimiter__Redis.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Reactivity.yaml b/migration/annotations/effect__experimental__Reactivity.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Reactivity.yaml rename to migration/annotations/effect__experimental__Reactivity.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__RequestResolver.yaml b/migration/annotations/effect__experimental__RequestResolver.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__RequestResolver.yaml rename to migration/annotations/effect__experimental__RequestResolver.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__Sse.yaml b/migration/annotations/effect__experimental__Sse.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__Sse.yaml rename to migration/annotations/effect__experimental__Sse.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__VariantSchema.yaml b/migration/annotations/effect__experimental__VariantSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__VariantSchema.yaml rename to migration/annotations/effect__experimental__VariantSchema.yaml diff --git a/repos/effect/migration/annotations/effect__experimental__index.yaml b/migration/annotations/effect__experimental__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__experimental__index.yaml rename to migration/annotations/effect__experimental__index.yaml diff --git a/repos/effect/migration/annotations/effect__index.yaml b/migration/annotations/effect__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__index.yaml rename to migration/annotations/effect__index.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__Logger.yaml b/migration/annotations/effect__opentelemetry__Logger.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__Logger.yaml rename to migration/annotations/effect__opentelemetry__Logger.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__Metrics.yaml b/migration/annotations/effect__opentelemetry__Metrics.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__Metrics.yaml rename to migration/annotations/effect__opentelemetry__Metrics.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__NodeSdk.yaml b/migration/annotations/effect__opentelemetry__NodeSdk.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__NodeSdk.yaml rename to migration/annotations/effect__opentelemetry__NodeSdk.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__Otlp.yaml b/migration/annotations/effect__opentelemetry__Otlp.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__Otlp.yaml rename to migration/annotations/effect__opentelemetry__Otlp.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpLogger.yaml b/migration/annotations/effect__opentelemetry__OtlpLogger.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__OtlpLogger.yaml rename to migration/annotations/effect__opentelemetry__OtlpLogger.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml b/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__OtlpMetrics.yaml rename to migration/annotations/effect__opentelemetry__OtlpMetrics.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpResource.yaml b/migration/annotations/effect__opentelemetry__OtlpResource.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__OtlpResource.yaml rename to migration/annotations/effect__opentelemetry__OtlpResource.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__OtlpTracer.yaml b/migration/annotations/effect__opentelemetry__OtlpTracer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__OtlpTracer.yaml rename to migration/annotations/effect__opentelemetry__OtlpTracer.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__Resource.yaml b/migration/annotations/effect__opentelemetry__Resource.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__Resource.yaml rename to migration/annotations/effect__opentelemetry__Resource.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__Tracer.yaml b/migration/annotations/effect__opentelemetry__Tracer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__Tracer.yaml rename to migration/annotations/effect__opentelemetry__Tracer.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__WebSdk.yaml b/migration/annotations/effect__opentelemetry__WebSdk.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__WebSdk.yaml rename to migration/annotations/effect__opentelemetry__WebSdk.yaml diff --git a/repos/effect/migration/annotations/effect__opentelemetry__index.yaml b/migration/annotations/effect__opentelemetry__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__opentelemetry__index.yaml rename to migration/annotations/effect__opentelemetry__index.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__BrowserHttpClient.yaml b/migration/annotations/effect__platform-browser__BrowserHttpClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__BrowserHttpClient.yaml rename to migration/annotations/effect__platform-browser__BrowserHttpClient.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__BrowserWorker.yaml b/migration/annotations/effect__platform-browser__BrowserWorker.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__BrowserWorker.yaml rename to migration/annotations/effect__platform-browser__BrowserWorker.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__BrowserWorkerRunner.yaml b/migration/annotations/effect__platform-browser__BrowserWorkerRunner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__BrowserWorkerRunner.yaml rename to migration/annotations/effect__platform-browser__BrowserWorkerRunner.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__Clipboard.yaml b/migration/annotations/effect__platform-browser__Clipboard.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__Clipboard.yaml rename to migration/annotations/effect__platform-browser__Clipboard.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__Geolocation.yaml b/migration/annotations/effect__platform-browser__Geolocation.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__Geolocation.yaml rename to migration/annotations/effect__platform-browser__Geolocation.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__Permissions.yaml b/migration/annotations/effect__platform-browser__Permissions.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__Permissions.yaml rename to migration/annotations/effect__platform-browser__Permissions.yaml diff --git a/repos/effect/migration/annotations/effect__platform-browser__index.yaml b/migration/annotations/effect__platform-browser__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-browser__index.yaml rename to migration/annotations/effect__platform-browser__index.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml b/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunCommandExecutor.yaml rename to migration/annotations/effect__platform-bun__BunCommandExecutor.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunContext.yaml b/migration/annotations/effect__platform-bun__BunContext.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunContext.yaml rename to migration/annotations/effect__platform-bun__BunContext.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunFileSystem__ParcelWatcher.yaml b/migration/annotations/effect__platform-bun__BunFileSystem__ParcelWatcher.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunFileSystem__ParcelWatcher.yaml rename to migration/annotations/effect__platform-bun__BunFileSystem__ParcelWatcher.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunHttpPlatform.yaml b/migration/annotations/effect__platform-bun__BunHttpPlatform.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunHttpPlatform.yaml rename to migration/annotations/effect__platform-bun__BunHttpPlatform.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunHttpServer.yaml b/migration/annotations/effect__platform-bun__BunHttpServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunHttpServer.yaml rename to migration/annotations/effect__platform-bun__BunHttpServer.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunHttpServerRequest.yaml b/migration/annotations/effect__platform-bun__BunHttpServerRequest.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunHttpServerRequest.yaml rename to migration/annotations/effect__platform-bun__BunHttpServerRequest.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml b/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunKeyValueStore.yaml rename to migration/annotations/effect__platform-bun__BunKeyValueStore.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunSink.yaml b/migration/annotations/effect__platform-bun__BunSink.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunSink.yaml rename to migration/annotations/effect__platform-bun__BunSink.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunSocket.yaml b/migration/annotations/effect__platform-bun__BunSocket.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunSocket.yaml rename to migration/annotations/effect__platform-bun__BunSocket.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunSocketServer.yaml b/migration/annotations/effect__platform-bun__BunSocketServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunSocketServer.yaml rename to migration/annotations/effect__platform-bun__BunSocketServer.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunStream.yaml b/migration/annotations/effect__platform-bun__BunStream.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunStream.yaml rename to migration/annotations/effect__platform-bun__BunStream.yaml diff --git a/migration/annotations/effect__platform-bun__BunTerminal.yaml b/migration/annotations/effect__platform-bun__BunTerminal.yaml new file mode 100644 index 000000000..4002ba608 --- /dev/null +++ b/migration/annotations/effect__platform-bun__BunTerminal.yaml @@ -0,0 +1,8 @@ +"@effect/platform-bun/BunTerminal#make": + include: true + replacement: "BunTerminal.make" + note: "V4 ends key input on bare Esc as well as Ctrl+C and Ctrl+D. Pass a custom shouldQuit predicate to make if your application needs to receive Esc; the v3 default quit only on Ctrl+C and Ctrl+D." +"@effect/platform-bun/BunTerminal#layer": + include: true + replacement: "BunTerminal.layer" + note: "The default layer now ends key input on bare Esc as well as Ctrl+C and Ctrl+D. To keep Esc as input, build a layer with Layer.effect(Terminal.Terminal, BunTerminal.make(shouldQuit)) using a custom quit predicate." diff --git a/repos/effect/migration/annotations/effect__platform-bun__BunWorker.yaml b/migration/annotations/effect__platform-bun__BunWorker.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__BunWorker.yaml rename to migration/annotations/effect__platform-bun__BunWorker.yaml diff --git a/repos/effect/migration/annotations/effect__platform-bun__index.yaml b/migration/annotations/effect__platform-bun__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-bun__index.yaml rename to migration/annotations/effect__platform-bun__index.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml b/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml rename to migration/annotations/effect__platform-node-shared__NodeCommandExecutor.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeFileSystem__ParcelWatcher.yaml b/migration/annotations/effect__platform-node-shared__NodeFileSystem__ParcelWatcher.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeFileSystem__ParcelWatcher.yaml rename to migration/annotations/effect__platform-node-shared__NodeFileSystem__ParcelWatcher.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml b/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml rename to migration/annotations/effect__platform-node-shared__NodeKeyValueStore.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeMultipart.yaml b/migration/annotations/effect__platform-node-shared__NodeMultipart.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeMultipart.yaml rename to migration/annotations/effect__platform-node-shared__NodeMultipart.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeSink.yaml b/migration/annotations/effect__platform-node-shared__NodeSink.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeSink.yaml rename to migration/annotations/effect__platform-node-shared__NodeSink.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeSocket.yaml b/migration/annotations/effect__platform-node-shared__NodeSocket.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeSocket.yaml rename to migration/annotations/effect__platform-node-shared__NodeSocket.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node-shared__NodeStream.yaml b/migration/annotations/effect__platform-node-shared__NodeStream.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node-shared__NodeStream.yaml rename to migration/annotations/effect__platform-node-shared__NodeStream.yaml diff --git a/migration/annotations/effect__platform-node-shared__NodeTerminal.yaml b/migration/annotations/effect__platform-node-shared__NodeTerminal.yaml new file mode 100644 index 000000000..08d16aa48 --- /dev/null +++ b/migration/annotations/effect__platform-node-shared__NodeTerminal.yaml @@ -0,0 +1,8 @@ +"@effect/platform-node-shared/NodeTerminal#make": + include: true + replacement: "NodeTerminal.make" + note: "V4 ends key input on bare Esc as well as Ctrl+C and Ctrl+D. Pass a custom shouldQuit predicate to make if your application needs to receive Esc; the v3 default quit only on Ctrl+C and Ctrl+D." +"@effect/platform-node-shared/NodeTerminal#layer": + include: true + replacement: "NodeTerminal.layer" + note: "The default layer now ends key input on bare Esc as well as Ctrl+C and Ctrl+D. To keep Esc as input, build a layer with Layer.effect(Terminal.Terminal, NodeTerminal.make(shouldQuit)) using a custom quit predicate." diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeCommandExecutor.yaml b/migration/annotations/effect__platform-node__NodeCommandExecutor.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeCommandExecutor.yaml rename to migration/annotations/effect__platform-node__NodeCommandExecutor.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeContext.yaml b/migration/annotations/effect__platform-node__NodeContext.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeContext.yaml rename to migration/annotations/effect__platform-node__NodeContext.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeFileSystem__ParcelWatcher.yaml b/migration/annotations/effect__platform-node__NodeFileSystem__ParcelWatcher.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeFileSystem__ParcelWatcher.yaml rename to migration/annotations/effect__platform-node__NodeFileSystem__ParcelWatcher.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeHttpClient.yaml b/migration/annotations/effect__platform-node__NodeHttpClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeHttpClient.yaml rename to migration/annotations/effect__platform-node__NodeHttpClient.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeHttpServer.yaml b/migration/annotations/effect__platform-node__NodeHttpServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeHttpServer.yaml rename to migration/annotations/effect__platform-node__NodeHttpServer.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml b/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeKeyValueStore.yaml rename to migration/annotations/effect__platform-node__NodeKeyValueStore.yaml diff --git a/migration/annotations/effect__platform-node__NodeTerminal.yaml b/migration/annotations/effect__platform-node__NodeTerminal.yaml new file mode 100644 index 000000000..6045db1e5 --- /dev/null +++ b/migration/annotations/effect__platform-node__NodeTerminal.yaml @@ -0,0 +1,8 @@ +"@effect/platform-node/NodeTerminal#make": + include: true + replacement: "NodeTerminal.make" + note: "V4 ends key input on bare Esc as well as Ctrl+C and Ctrl+D. Pass a custom shouldQuit predicate to make if your application needs to receive Esc; the v3 default quit only on Ctrl+C and Ctrl+D." +"@effect/platform-node/NodeTerminal#layer": + include: true + replacement: "NodeTerminal.layer" + note: "The default layer now ends key input on bare Esc as well as Ctrl+C and Ctrl+D. To keep Esc as input, build a layer with Layer.effect(Terminal.Terminal, NodeTerminal.make(shouldQuit)) using a custom quit predicate." diff --git a/repos/effect/migration/annotations/effect__platform-node__NodeWorker.yaml b/migration/annotations/effect__platform-node__NodeWorker.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__NodeWorker.yaml rename to migration/annotations/effect__platform-node__NodeWorker.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__Undici.yaml b/migration/annotations/effect__platform-node__Undici.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__Undici.yaml rename to migration/annotations/effect__platform-node__Undici.yaml diff --git a/repos/effect/migration/annotations/effect__platform-node__index.yaml b/migration/annotations/effect__platform-node__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform-node__index.yaml rename to migration/annotations/effect__platform-node__index.yaml diff --git a/repos/effect/migration/annotations/effect__platform.yaml b/migration/annotations/effect__platform.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform.yaml rename to migration/annotations/effect__platform.yaml diff --git a/repos/effect/migration/annotations/effect__platform__ChannelSchema.yaml b/migration/annotations/effect__platform__ChannelSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__ChannelSchema.yaml rename to migration/annotations/effect__platform__ChannelSchema.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Command.yaml b/migration/annotations/effect__platform__Command.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Command.yaml rename to migration/annotations/effect__platform__Command.yaml diff --git a/repos/effect/migration/annotations/effect__platform__CommandExecutor.yaml b/migration/annotations/effect__platform__CommandExecutor.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__CommandExecutor.yaml rename to migration/annotations/effect__platform__CommandExecutor.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Cookies.yaml b/migration/annotations/effect__platform__Cookies.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Cookies.yaml rename to migration/annotations/effect__platform__Cookies.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Effectify.yaml b/migration/annotations/effect__platform__Effectify.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Effectify.yaml rename to migration/annotations/effect__platform__Effectify.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Error.yaml b/migration/annotations/effect__platform__Error.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Error.yaml rename to migration/annotations/effect__platform__Error.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Etag.yaml b/migration/annotations/effect__platform__Etag.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Etag.yaml rename to migration/annotations/effect__platform__Etag.yaml diff --git a/repos/effect/migration/annotations/effect__platform__FetchHttpClient.yaml b/migration/annotations/effect__platform__FetchHttpClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__FetchHttpClient.yaml rename to migration/annotations/effect__platform__FetchHttpClient.yaml diff --git a/repos/effect/migration/annotations/effect__platform__FileSystem.yaml b/migration/annotations/effect__platform__FileSystem.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__FileSystem.yaml rename to migration/annotations/effect__platform__FileSystem.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Headers.yaml b/migration/annotations/effect__platform__Headers.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Headers.yaml rename to migration/annotations/effect__platform__Headers.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApi.yaml b/migration/annotations/effect__platform__HttpApi.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApi.yaml rename to migration/annotations/effect__platform__HttpApi.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiBuilder.yaml b/migration/annotations/effect__platform__HttpApiBuilder.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiBuilder.yaml rename to migration/annotations/effect__platform__HttpApiBuilder.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml b/migration/annotations/effect__platform__HttpApiClient.yaml similarity index 83% rename from repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml rename to migration/annotations/effect__platform__HttpApiClient.yaml index 3d0fcbe36..d323c0dbe 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpApiClient.yaml +++ b/migration/annotations/effect__platform__HttpApiClient.yaml @@ -1,6 +1,6 @@ "@effect/platform/HttpApiClient#Client.Method": replacement: "effect/http-api/HttpApiClient#Client.Method" - note: "The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse." + note: "The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse. Omitting responseMode returns the decoded value; a union-valued mode returns the corresponding union of decoded values, responses, or [value, response] tuples." "@effect/platform/HttpApiClient#endpoint": replacement: "effect/http-api/HttpApiClient#endpoint" note: "The endpoint client remains, selected by group and endpoint identifiers and using v4 request and responseMode fields." diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiEndpoint.yaml b/migration/annotations/effect__platform__HttpApiEndpoint.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiEndpoint.yaml rename to migration/annotations/effect__platform__HttpApiEndpoint.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiError.yaml b/migration/annotations/effect__platform__HttpApiError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiError.yaml rename to migration/annotations/effect__platform__HttpApiError.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiGroup.yaml b/migration/annotations/effect__platform__HttpApiGroup.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiGroup.yaml rename to migration/annotations/effect__platform__HttpApiGroup.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiMiddleware.yaml b/migration/annotations/effect__platform__HttpApiMiddleware.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiMiddleware.yaml rename to migration/annotations/effect__platform__HttpApiMiddleware.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiScalar.yaml b/migration/annotations/effect__platform__HttpApiScalar.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiScalar.yaml rename to migration/annotations/effect__platform__HttpApiScalar.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiSchema.yaml b/migration/annotations/effect__platform__HttpApiSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiSchema.yaml rename to migration/annotations/effect__platform__HttpApiSchema.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiSecurity.yaml b/migration/annotations/effect__platform__HttpApiSecurity.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiSecurity.yaml rename to migration/annotations/effect__platform__HttpApiSecurity.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApiSwagger.yaml b/migration/annotations/effect__platform__HttpApiSwagger.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApiSwagger.yaml rename to migration/annotations/effect__platform__HttpApiSwagger.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpApp.yaml b/migration/annotations/effect__platform__HttpApp.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpApp.yaml rename to migration/annotations/effect__platform__HttpApp.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpBody.yaml b/migration/annotations/effect__platform__HttpBody.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpBody.yaml rename to migration/annotations/effect__platform__HttpBody.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpClient.yaml b/migration/annotations/effect__platform__HttpClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpClient.yaml rename to migration/annotations/effect__platform__HttpClient.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpClientError.yaml b/migration/annotations/effect__platform__HttpClientError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpClientError.yaml rename to migration/annotations/effect__platform__HttpClientError.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpClientRequest.yaml b/migration/annotations/effect__platform__HttpClientRequest.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpClientRequest.yaml rename to migration/annotations/effect__platform__HttpClientRequest.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpClientResponse.yaml b/migration/annotations/effect__platform__HttpClientResponse.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpClientResponse.yaml rename to migration/annotations/effect__platform__HttpClientResponse.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpIncomingMessage.yaml b/migration/annotations/effect__platform__HttpIncomingMessage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpIncomingMessage.yaml rename to migration/annotations/effect__platform__HttpIncomingMessage.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpLayerRouter.yaml b/migration/annotations/effect__platform__HttpLayerRouter.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpLayerRouter.yaml rename to migration/annotations/effect__platform__HttpLayerRouter.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpMethod.yaml b/migration/annotations/effect__platform__HttpMethod.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpMethod.yaml rename to migration/annotations/effect__platform__HttpMethod.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpMiddleware.yaml b/migration/annotations/effect__platform__HttpMiddleware.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpMiddleware.yaml rename to migration/annotations/effect__platform__HttpMiddleware.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpMultiplex.yaml b/migration/annotations/effect__platform__HttpMultiplex.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpMultiplex.yaml rename to migration/annotations/effect__platform__HttpMultiplex.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpPlatform.yaml b/migration/annotations/effect__platform__HttpPlatform.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpPlatform.yaml rename to migration/annotations/effect__platform__HttpPlatform.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpRouter.yaml b/migration/annotations/effect__platform__HttpRouter.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpRouter.yaml rename to migration/annotations/effect__platform__HttpRouter.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpServer.yaml b/migration/annotations/effect__platform__HttpServer.yaml similarity index 91% rename from repos/effect/migration/annotations/effect__platform__HttpServer.yaml rename to migration/annotations/effect__platform__HttpServer.yaml index 8f295f9d8..200433f72 100644 --- a/repos/effect/migration/annotations/effect__platform__HttpServer.yaml +++ b/migration/annotations/effect__platform__HttpServer.yaml @@ -6,7 +6,7 @@ note: "The accessor was removed; read the service and pass its Address to the callback." "@effect/platform/HttpServer#HttpServer": replacement: "HttpServer.HttpServer" - note: "The interface and tag became one Context.Service class; use its Service member for implementations." + note: "The interface and tag became one Context.Service class; use its Service member for implementations. The service-level serve method derives requirements from the middleware-transformed application, so services added by middleware must also be provided." "@effect/platform/HttpServer#layerContext": replacement: "HttpServer.layerServices" note: "Renamed; it provides the standard HTTP platform services." diff --git a/repos/effect/migration/annotations/effect__platform__HttpServerError.yaml b/migration/annotations/effect__platform__HttpServerError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpServerError.yaml rename to migration/annotations/effect__platform__HttpServerError.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpServerRequest.yaml b/migration/annotations/effect__platform__HttpServerRequest.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpServerRequest.yaml rename to migration/annotations/effect__platform__HttpServerRequest.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpServerRespondable.yaml b/migration/annotations/effect__platform__HttpServerRespondable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpServerRespondable.yaml rename to migration/annotations/effect__platform__HttpServerRespondable.yaml diff --git a/repos/effect/migration/annotations/effect__platform__HttpServerResponse.yaml b/migration/annotations/effect__platform__HttpServerResponse.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__HttpServerResponse.yaml rename to migration/annotations/effect__platform__HttpServerResponse.yaml diff --git a/repos/effect/migration/annotations/effect__platform__KeyValueStore.yaml b/migration/annotations/effect__platform__KeyValueStore.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__KeyValueStore.yaml rename to migration/annotations/effect__platform__KeyValueStore.yaml diff --git a/repos/effect/migration/annotations/effect__platform__MsgPack.yaml b/migration/annotations/effect__platform__MsgPack.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__MsgPack.yaml rename to migration/annotations/effect__platform__MsgPack.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Multipart.yaml b/migration/annotations/effect__platform__Multipart.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Multipart.yaml rename to migration/annotations/effect__platform__Multipart.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Ndjson.yaml b/migration/annotations/effect__platform__Ndjson.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Ndjson.yaml rename to migration/annotations/effect__platform__Ndjson.yaml diff --git a/repos/effect/migration/annotations/effect__platform__OpenApi.yaml b/migration/annotations/effect__platform__OpenApi.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__OpenApi.yaml rename to migration/annotations/effect__platform__OpenApi.yaml diff --git a/repos/effect/migration/annotations/effect__platform__OpenApiJsonSchema.yaml b/migration/annotations/effect__platform__OpenApiJsonSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__OpenApiJsonSchema.yaml rename to migration/annotations/effect__platform__OpenApiJsonSchema.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Path.yaml b/migration/annotations/effect__platform__Path.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Path.yaml rename to migration/annotations/effect__platform__Path.yaml diff --git a/repos/effect/migration/annotations/effect__platform__PlatformConfigProvider.yaml b/migration/annotations/effect__platform__PlatformConfigProvider.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__PlatformConfigProvider.yaml rename to migration/annotations/effect__platform__PlatformConfigProvider.yaml diff --git a/repos/effect/migration/annotations/effect__platform__PlatformLogger.yaml b/migration/annotations/effect__platform__PlatformLogger.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__PlatformLogger.yaml rename to migration/annotations/effect__platform__PlatformLogger.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Runtime.yaml b/migration/annotations/effect__platform__Runtime.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Runtime.yaml rename to migration/annotations/effect__platform__Runtime.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Socket.yaml b/migration/annotations/effect__platform__Socket.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Socket.yaml rename to migration/annotations/effect__platform__Socket.yaml diff --git a/repos/effect/migration/annotations/effect__platform__SocketServer.yaml b/migration/annotations/effect__platform__SocketServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__SocketServer.yaml rename to migration/annotations/effect__platform__SocketServer.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Template.yaml b/migration/annotations/effect__platform__Template.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Template.yaml rename to migration/annotations/effect__platform__Template.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Terminal.yaml b/migration/annotations/effect__platform__Terminal.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Terminal.yaml rename to migration/annotations/effect__platform__Terminal.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Transferable.yaml b/migration/annotations/effect__platform__Transferable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Transferable.yaml rename to migration/annotations/effect__platform__Transferable.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Url.yaml b/migration/annotations/effect__platform__Url.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Url.yaml rename to migration/annotations/effect__platform__Url.yaml diff --git a/repos/effect/migration/annotations/effect__platform__UrlParams.yaml b/migration/annotations/effect__platform__UrlParams.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__UrlParams.yaml rename to migration/annotations/effect__platform__UrlParams.yaml diff --git a/repos/effect/migration/annotations/effect__platform__Worker.yaml b/migration/annotations/effect__platform__Worker.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__Worker.yaml rename to migration/annotations/effect__platform__Worker.yaml diff --git a/repos/effect/migration/annotations/effect__platform__WorkerError.yaml b/migration/annotations/effect__platform__WorkerError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__WorkerError.yaml rename to migration/annotations/effect__platform__WorkerError.yaml diff --git a/repos/effect/migration/annotations/effect__platform__WorkerRunner.yaml b/migration/annotations/effect__platform__WorkerRunner.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__WorkerRunner.yaml rename to migration/annotations/effect__platform__WorkerRunner.yaml diff --git a/repos/effect/migration/annotations/effect__platform__index.yaml b/migration/annotations/effect__platform__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__platform__index.yaml rename to migration/annotations/effect__platform__index.yaml diff --git a/repos/effect/migration/annotations/effect__printer-ansi.yaml b/migration/annotations/effect__printer-ansi.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer-ansi.yaml rename to migration/annotations/effect__printer-ansi.yaml diff --git a/repos/effect/migration/annotations/effect__printer-ansi__Ansi.yaml b/migration/annotations/effect__printer-ansi__Ansi.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer-ansi__Ansi.yaml rename to migration/annotations/effect__printer-ansi__Ansi.yaml diff --git a/repos/effect/migration/annotations/effect__printer-ansi__AnsiDoc.yaml b/migration/annotations/effect__printer-ansi__AnsiDoc.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer-ansi__AnsiDoc.yaml rename to migration/annotations/effect__printer-ansi__AnsiDoc.yaml diff --git a/repos/effect/migration/annotations/effect__printer-ansi__Color.yaml b/migration/annotations/effect__printer-ansi__Color.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer-ansi__Color.yaml rename to migration/annotations/effect__printer-ansi__Color.yaml diff --git a/repos/effect/migration/annotations/effect__printer-ansi__index.yaml b/migration/annotations/effect__printer-ansi__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer-ansi__index.yaml rename to migration/annotations/effect__printer-ansi__index.yaml diff --git a/repos/effect/migration/annotations/effect__printer.yaml b/migration/annotations/effect__printer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer.yaml rename to migration/annotations/effect__printer.yaml diff --git a/repos/effect/migration/annotations/effect__printer__Doc.yaml b/migration/annotations/effect__printer__Doc.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__Doc.yaml rename to migration/annotations/effect__printer__Doc.yaml diff --git a/repos/effect/migration/annotations/effect__printer__DocStream.yaml b/migration/annotations/effect__printer__DocStream.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__DocStream.yaml rename to migration/annotations/effect__printer__DocStream.yaml diff --git a/repos/effect/migration/annotations/effect__printer__DocTree.yaml b/migration/annotations/effect__printer__DocTree.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__DocTree.yaml rename to migration/annotations/effect__printer__DocTree.yaml diff --git a/repos/effect/migration/annotations/effect__printer__Flatten.yaml b/migration/annotations/effect__printer__Flatten.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__Flatten.yaml rename to migration/annotations/effect__printer__Flatten.yaml diff --git a/repos/effect/migration/annotations/effect__printer__Layout.yaml b/migration/annotations/effect__printer__Layout.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__Layout.yaml rename to migration/annotations/effect__printer__Layout.yaml diff --git a/repos/effect/migration/annotations/effect__printer__Optimize.yaml b/migration/annotations/effect__printer__Optimize.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__Optimize.yaml rename to migration/annotations/effect__printer__Optimize.yaml diff --git a/repos/effect/migration/annotations/effect__printer__PageWidth.yaml b/migration/annotations/effect__printer__PageWidth.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__PageWidth.yaml rename to migration/annotations/effect__printer__PageWidth.yaml diff --git a/repos/effect/migration/annotations/effect__printer__index.yaml b/migration/annotations/effect__printer__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__printer__index.yaml rename to migration/annotations/effect__printer__index.yaml diff --git a/repos/effect/migration/annotations/effect__rpc.yaml b/migration/annotations/effect__rpc.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc.yaml rename to migration/annotations/effect__rpc.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__Rpc.yaml b/migration/annotations/effect__rpc__Rpc.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__Rpc.yaml rename to migration/annotations/effect__rpc__Rpc.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcClient.yaml b/migration/annotations/effect__rpc__RpcClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcClient.yaml rename to migration/annotations/effect__rpc__RpcClient.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcClientError.yaml b/migration/annotations/effect__rpc__RpcClientError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcClientError.yaml rename to migration/annotations/effect__rpc__RpcClientError.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcGroup.yaml b/migration/annotations/effect__rpc__RpcGroup.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcGroup.yaml rename to migration/annotations/effect__rpc__RpcGroup.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcMessage.yaml b/migration/annotations/effect__rpc__RpcMessage.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcMessage.yaml rename to migration/annotations/effect__rpc__RpcMessage.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcMiddleware.yaml b/migration/annotations/effect__rpc__RpcMiddleware.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcMiddleware.yaml rename to migration/annotations/effect__rpc__RpcMiddleware.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcSchema.yaml b/migration/annotations/effect__rpc__RpcSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcSchema.yaml rename to migration/annotations/effect__rpc__RpcSchema.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcSerialization.yaml b/migration/annotations/effect__rpc__RpcSerialization.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcSerialization.yaml rename to migration/annotations/effect__rpc__RpcSerialization.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcServer.yaml b/migration/annotations/effect__rpc__RpcServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcServer.yaml rename to migration/annotations/effect__rpc__RpcServer.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__RpcTest.yaml b/migration/annotations/effect__rpc__RpcTest.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__RpcTest.yaml rename to migration/annotations/effect__rpc__RpcTest.yaml diff --git a/repos/effect/migration/annotations/effect__rpc__index.yaml b/migration/annotations/effect__rpc__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__rpc__index.yaml rename to migration/annotations/effect__rpc__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseClient.yaml b/migration/annotations/effect__sql-clickhouse__ClickhouseClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseClient.yaml rename to migration/annotations/effect__sql-clickhouse__ClickhouseClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml b/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml rename to migration/annotations/effect__sql-clickhouse__ClickhouseMigrator.yaml diff --git a/repos/effect/migration/annotations/effect__sql-clickhouse__index.yaml b/migration/annotations/effect__sql-clickhouse__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-clickhouse__index.yaml rename to migration/annotations/effect__sql-clickhouse__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-d1__D1Client.yaml b/migration/annotations/effect__sql-d1__D1Client.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-d1__D1Client.yaml rename to migration/annotations/effect__sql-d1__D1Client.yaml diff --git a/repos/effect/migration/annotations/effect__sql-d1__index.yaml b/migration/annotations/effect__sql-d1__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-d1__index.yaml rename to migration/annotations/effect__sql-d1__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-drizzle__Mysql.yaml b/migration/annotations/effect__sql-drizzle__Mysql.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-drizzle__Mysql.yaml rename to migration/annotations/effect__sql-drizzle__Mysql.yaml diff --git a/repos/effect/migration/annotations/effect__sql-drizzle__Pg.yaml b/migration/annotations/effect__sql-drizzle__Pg.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-drizzle__Pg.yaml rename to migration/annotations/effect__sql-drizzle__Pg.yaml diff --git a/repos/effect/migration/annotations/effect__sql-drizzle__Sqlite.yaml b/migration/annotations/effect__sql-drizzle__Sqlite.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-drizzle__Sqlite.yaml rename to migration/annotations/effect__sql-drizzle__Sqlite.yaml diff --git a/repos/effect/migration/annotations/effect__sql-kysely__Kysely.yaml b/migration/annotations/effect__sql-kysely__Kysely.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-kysely__Kysely.yaml rename to migration/annotations/effect__sql-kysely__Kysely.yaml diff --git a/repos/effect/migration/annotations/effect__sql-kysely__Mssql.yaml b/migration/annotations/effect__sql-kysely__Mssql.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-kysely__Mssql.yaml rename to migration/annotations/effect__sql-kysely__Mssql.yaml diff --git a/repos/effect/migration/annotations/effect__sql-kysely__Mysql.yaml b/migration/annotations/effect__sql-kysely__Mysql.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-kysely__Mysql.yaml rename to migration/annotations/effect__sql-kysely__Mysql.yaml diff --git a/repos/effect/migration/annotations/effect__sql-kysely__Pg.yaml b/migration/annotations/effect__sql-kysely__Pg.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-kysely__Pg.yaml rename to migration/annotations/effect__sql-kysely__Pg.yaml diff --git a/repos/effect/migration/annotations/effect__sql-kysely__Sqlite.yaml b/migration/annotations/effect__sql-kysely__Sqlite.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-kysely__Sqlite.yaml rename to migration/annotations/effect__sql-kysely__Sqlite.yaml diff --git a/repos/effect/migration/annotations/effect__sql-kysely__patch.types.yaml b/migration/annotations/effect__sql-kysely__patch.types.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-kysely__patch.types.yaml rename to migration/annotations/effect__sql-kysely__patch.types.yaml diff --git a/repos/effect/migration/annotations/effect__sql-libsql__index.yaml b/migration/annotations/effect__sql-libsql__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-libsql__index.yaml rename to migration/annotations/effect__sql-libsql__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-mssql__MssqlClient.yaml b/migration/annotations/effect__sql-mssql__MssqlClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-mssql__MssqlClient.yaml rename to migration/annotations/effect__sql-mssql__MssqlClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-mssql__Parameter.yaml b/migration/annotations/effect__sql-mssql__Parameter.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-mssql__Parameter.yaml rename to migration/annotations/effect__sql-mssql__Parameter.yaml diff --git a/repos/effect/migration/annotations/effect__sql-mssql__Procedure.yaml b/migration/annotations/effect__sql-mssql__Procedure.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-mssql__Procedure.yaml rename to migration/annotations/effect__sql-mssql__Procedure.yaml diff --git a/repos/effect/migration/annotations/effect__sql-mssql__index.yaml b/migration/annotations/effect__sql-mssql__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-mssql__index.yaml rename to migration/annotations/effect__sql-mssql__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-mysql2__MysqlClient.yaml b/migration/annotations/effect__sql-mysql2__MysqlClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-mysql2__MysqlClient.yaml rename to migration/annotations/effect__sql-mysql2__MysqlClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-mysql2__index.yaml b/migration/annotations/effect__sql-mysql2__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-mysql2__index.yaml rename to migration/annotations/effect__sql-mysql2__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-pg__PgClient.yaml b/migration/annotations/effect__sql-pg__PgClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-pg__PgClient.yaml rename to migration/annotations/effect__sql-pg__PgClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-pg__index.yaml b/migration/annotations/effect__sql-pg__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-pg__index.yaml rename to migration/annotations/effect__sql-pg__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml b/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml rename to migration/annotations/effect__sql-sqlite-bun__SqliteClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-bun__index.yaml b/migration/annotations/effect__sql-sqlite-bun__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-bun__index.yaml rename to migration/annotations/effect__sql-sqlite-bun__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-do__SqliteClient.yaml b/migration/annotations/effect__sql-sqlite-do__SqliteClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-do__SqliteClient.yaml rename to migration/annotations/effect__sql-sqlite-do__SqliteClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-do__index.yaml b/migration/annotations/effect__sql-sqlite-do__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-do__index.yaml rename to migration/annotations/effect__sql-sqlite-do__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml b/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml rename to migration/annotations/effect__sql-sqlite-node__SqliteClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-node__index.yaml b/migration/annotations/effect__sql-sqlite-node__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-node__index.yaml rename to migration/annotations/effect__sql-sqlite-node__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-react-native__SqliteClient.yaml b/migration/annotations/effect__sql-sqlite-react-native__SqliteClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-react-native__SqliteClient.yaml rename to migration/annotations/effect__sql-sqlite-react-native__SqliteClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-react-native__index.yaml b/migration/annotations/effect__sql-sqlite-react-native__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-react-native__index.yaml rename to migration/annotations/effect__sql-sqlite-react-native__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-wasm__SqliteClient.yaml b/migration/annotations/effect__sql-sqlite-wasm__SqliteClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-wasm__SqliteClient.yaml rename to migration/annotations/effect__sql-sqlite-wasm__SqliteClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql-sqlite-wasm__index.yaml b/migration/annotations/effect__sql-sqlite-wasm__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql-sqlite-wasm__index.yaml rename to migration/annotations/effect__sql-sqlite-wasm__index.yaml diff --git a/repos/effect/migration/annotations/effect__sql.yaml b/migration/annotations/effect__sql.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql.yaml rename to migration/annotations/effect__sql.yaml diff --git a/repos/effect/migration/annotations/effect__sql__Migrator.yaml b/migration/annotations/effect__sql__Migrator.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__Migrator.yaml rename to migration/annotations/effect__sql__Migrator.yaml diff --git a/repos/effect/migration/annotations/effect__sql__Migrator__FileSystem.yaml b/migration/annotations/effect__sql__Migrator__FileSystem.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__Migrator__FileSystem.yaml rename to migration/annotations/effect__sql__Migrator__FileSystem.yaml diff --git a/repos/effect/migration/annotations/effect__sql__Model.yaml b/migration/annotations/effect__sql__Model.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__Model.yaml rename to migration/annotations/effect__sql__Model.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlClient.yaml b/migration/annotations/effect__sql__SqlClient.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlClient.yaml rename to migration/annotations/effect__sql__SqlClient.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlConnection.yaml b/migration/annotations/effect__sql__SqlConnection.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlConnection.yaml rename to migration/annotations/effect__sql__SqlConnection.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlError.yaml b/migration/annotations/effect__sql__SqlError.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlError.yaml rename to migration/annotations/effect__sql__SqlError.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlEventJournal.yaml b/migration/annotations/effect__sql__SqlEventJournal.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlEventJournal.yaml rename to migration/annotations/effect__sql__SqlEventJournal.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlEventLogServer.yaml b/migration/annotations/effect__sql__SqlEventLogServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlEventLogServer.yaml rename to migration/annotations/effect__sql__SqlEventLogServer.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlPersistedQueue.yaml b/migration/annotations/effect__sql__SqlPersistedQueue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlPersistedQueue.yaml rename to migration/annotations/effect__sql__SqlPersistedQueue.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlResolver.yaml b/migration/annotations/effect__sql__SqlResolver.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlResolver.yaml rename to migration/annotations/effect__sql__SqlResolver.yaml diff --git a/repos/effect/migration/annotations/effect__sql__SqlSchema.yaml b/migration/annotations/effect__sql__SqlSchema.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__SqlSchema.yaml rename to migration/annotations/effect__sql__SqlSchema.yaml diff --git a/repos/effect/migration/annotations/effect__sql__Statement.yaml b/migration/annotations/effect__sql__Statement.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__Statement.yaml rename to migration/annotations/effect__sql__Statement.yaml diff --git a/repos/effect/migration/annotations/effect__sql__index.yaml b/migration/annotations/effect__sql__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__sql__index.yaml rename to migration/annotations/effect__sql__index.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass.yaml b/migration/annotations/effect__typeclass.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass.yaml rename to migration/annotations/effect__typeclass.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Alternative.yaml b/migration/annotations/effect__typeclass__Alternative.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Alternative.yaml rename to migration/annotations/effect__typeclass__Alternative.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Applicative.yaml b/migration/annotations/effect__typeclass__Applicative.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Applicative.yaml rename to migration/annotations/effect__typeclass__Applicative.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Bicovariant.yaml b/migration/annotations/effect__typeclass__Bicovariant.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Bicovariant.yaml rename to migration/annotations/effect__typeclass__Bicovariant.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Bounded.yaml b/migration/annotations/effect__typeclass__Bounded.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Bounded.yaml rename to migration/annotations/effect__typeclass__Bounded.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Chainable.yaml b/migration/annotations/effect__typeclass__Chainable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Chainable.yaml rename to migration/annotations/effect__typeclass__Chainable.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Contravariant.yaml b/migration/annotations/effect__typeclass__Contravariant.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Contravariant.yaml rename to migration/annotations/effect__typeclass__Contravariant.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Coproduct.yaml b/migration/annotations/effect__typeclass__Coproduct.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Coproduct.yaml rename to migration/annotations/effect__typeclass__Coproduct.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Covariant.yaml b/migration/annotations/effect__typeclass__Covariant.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Covariant.yaml rename to migration/annotations/effect__typeclass__Covariant.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Filterable.yaml b/migration/annotations/effect__typeclass__Filterable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Filterable.yaml rename to migration/annotations/effect__typeclass__Filterable.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__FlatMap.yaml b/migration/annotations/effect__typeclass__FlatMap.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__FlatMap.yaml rename to migration/annotations/effect__typeclass__FlatMap.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Foldable.yaml b/migration/annotations/effect__typeclass__Foldable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Foldable.yaml rename to migration/annotations/effect__typeclass__Foldable.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Invariant.yaml b/migration/annotations/effect__typeclass__Invariant.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Invariant.yaml rename to migration/annotations/effect__typeclass__Invariant.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Monad.yaml b/migration/annotations/effect__typeclass__Monad.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Monad.yaml rename to migration/annotations/effect__typeclass__Monad.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Monoid.yaml b/migration/annotations/effect__typeclass__Monoid.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Monoid.yaml rename to migration/annotations/effect__typeclass__Monoid.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Of.yaml b/migration/annotations/effect__typeclass__Of.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Of.yaml rename to migration/annotations/effect__typeclass__Of.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Pointed.yaml b/migration/annotations/effect__typeclass__Pointed.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Pointed.yaml rename to migration/annotations/effect__typeclass__Pointed.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Product.yaml b/migration/annotations/effect__typeclass__Product.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Product.yaml rename to migration/annotations/effect__typeclass__Product.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__SemiAlternative.yaml b/migration/annotations/effect__typeclass__SemiAlternative.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__SemiAlternative.yaml rename to migration/annotations/effect__typeclass__SemiAlternative.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__SemiApplicative.yaml b/migration/annotations/effect__typeclass__SemiApplicative.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__SemiApplicative.yaml rename to migration/annotations/effect__typeclass__SemiApplicative.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__SemiCoproduct.yaml b/migration/annotations/effect__typeclass__SemiCoproduct.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__SemiCoproduct.yaml rename to migration/annotations/effect__typeclass__SemiCoproduct.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__SemiProduct.yaml b/migration/annotations/effect__typeclass__SemiProduct.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__SemiProduct.yaml rename to migration/annotations/effect__typeclass__SemiProduct.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Semigroup.yaml b/migration/annotations/effect__typeclass__Semigroup.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Semigroup.yaml rename to migration/annotations/effect__typeclass__Semigroup.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__Traversable.yaml b/migration/annotations/effect__typeclass__Traversable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__Traversable.yaml rename to migration/annotations/effect__typeclass__Traversable.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__TraversableFilterable.yaml b/migration/annotations/effect__typeclass__TraversableFilterable.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__TraversableFilterable.yaml rename to migration/annotations/effect__typeclass__TraversableFilterable.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Array.yaml b/migration/annotations/effect__typeclass__data__Array.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Array.yaml rename to migration/annotations/effect__typeclass__data__Array.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__BigInt.yaml b/migration/annotations/effect__typeclass__data__BigInt.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__BigInt.yaml rename to migration/annotations/effect__typeclass__data__BigInt.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Boolean.yaml b/migration/annotations/effect__typeclass__data__Boolean.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Boolean.yaml rename to migration/annotations/effect__typeclass__data__Boolean.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Duration.yaml b/migration/annotations/effect__typeclass__data__Duration.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Duration.yaml rename to migration/annotations/effect__typeclass__data__Duration.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Effect.yaml b/migration/annotations/effect__typeclass__data__Effect.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Effect.yaml rename to migration/annotations/effect__typeclass__data__Effect.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Either.yaml b/migration/annotations/effect__typeclass__data__Either.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Either.yaml rename to migration/annotations/effect__typeclass__data__Either.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Identity.yaml b/migration/annotations/effect__typeclass__data__Identity.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Identity.yaml rename to migration/annotations/effect__typeclass__data__Identity.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Micro.yaml b/migration/annotations/effect__typeclass__data__Micro.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Micro.yaml rename to migration/annotations/effect__typeclass__data__Micro.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Number.yaml b/migration/annotations/effect__typeclass__data__Number.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Number.yaml rename to migration/annotations/effect__typeclass__data__Number.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Option.yaml b/migration/annotations/effect__typeclass__data__Option.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Option.yaml rename to migration/annotations/effect__typeclass__data__Option.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Ordering.yaml b/migration/annotations/effect__typeclass__data__Ordering.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Ordering.yaml rename to migration/annotations/effect__typeclass__data__Ordering.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Predicate.yaml b/migration/annotations/effect__typeclass__data__Predicate.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Predicate.yaml rename to migration/annotations/effect__typeclass__data__Predicate.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Record.yaml b/migration/annotations/effect__typeclass__data__Record.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Record.yaml rename to migration/annotations/effect__typeclass__data__Record.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__String.yaml b/migration/annotations/effect__typeclass__data__String.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__String.yaml rename to migration/annotations/effect__typeclass__data__String.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__data__Tuple.yaml b/migration/annotations/effect__typeclass__data__Tuple.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__data__Tuple.yaml rename to migration/annotations/effect__typeclass__data__Tuple.yaml diff --git a/repos/effect/migration/annotations/effect__typeclass__index.yaml b/migration/annotations/effect__typeclass__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__typeclass__index.yaml rename to migration/annotations/effect__typeclass__index.yaml diff --git a/repos/effect/migration/annotations/effect__vitest__index.yaml b/migration/annotations/effect__vitest__index.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__vitest__index.yaml rename to migration/annotations/effect__vitest__index.yaml diff --git a/repos/effect/migration/annotations/effect__vitest__utils.yaml b/migration/annotations/effect__vitest__utils.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__vitest__utils.yaml rename to migration/annotations/effect__vitest__utils.yaml diff --git a/repos/effect/migration/annotations/effect__workflow.yaml b/migration/annotations/effect__workflow.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow.yaml rename to migration/annotations/effect__workflow.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__Activity.yaml b/migration/annotations/effect__workflow__Activity.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__Activity.yaml rename to migration/annotations/effect__workflow__Activity.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__DurableClock.yaml b/migration/annotations/effect__workflow__DurableClock.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__DurableClock.yaml rename to migration/annotations/effect__workflow__DurableClock.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__DurableDeferred.yaml b/migration/annotations/effect__workflow__DurableDeferred.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__DurableDeferred.yaml rename to migration/annotations/effect__workflow__DurableDeferred.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__DurableQueue.yaml b/migration/annotations/effect__workflow__DurableQueue.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__DurableQueue.yaml rename to migration/annotations/effect__workflow__DurableQueue.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__DurableRateLimiter.yaml b/migration/annotations/effect__workflow__DurableRateLimiter.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__DurableRateLimiter.yaml rename to migration/annotations/effect__workflow__DurableRateLimiter.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__Workflow.yaml b/migration/annotations/effect__workflow__Workflow.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__Workflow.yaml rename to migration/annotations/effect__workflow__Workflow.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__WorkflowEngine.yaml b/migration/annotations/effect__workflow__WorkflowEngine.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__WorkflowEngine.yaml rename to migration/annotations/effect__workflow__WorkflowEngine.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__WorkflowProxy.yaml b/migration/annotations/effect__workflow__WorkflowProxy.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__WorkflowProxy.yaml rename to migration/annotations/effect__workflow__WorkflowProxy.yaml diff --git a/repos/effect/migration/annotations/effect__workflow__WorkflowProxyServer.yaml b/migration/annotations/effect__workflow__WorkflowProxyServer.yaml similarity index 100% rename from repos/effect/migration/annotations/effect__workflow__WorkflowProxyServer.yaml rename to migration/annotations/effect__workflow__WorkflowProxyServer.yaml diff --git a/repos/effect/migration/cause.md b/migration/cause.md similarity index 100% rename from repos/effect/migration/cause.md rename to migration/cause.md diff --git a/repos/effect/migration/equality.md b/migration/equality.md similarity index 100% rename from repos/effect/migration/equality.md rename to migration/equality.md diff --git a/repos/effect/migration/error-handling.md b/migration/error-handling.md similarity index 100% rename from repos/effect/migration/error-handling.md rename to migration/error-handling.md diff --git a/repos/effect/migration/fiber-keep-alive.md b/migration/fiber-keep-alive.md similarity index 100% rename from repos/effect/migration/fiber-keep-alive.md rename to migration/fiber-keep-alive.md diff --git a/repos/effect/migration/fiberref.md b/migration/fiberref.md similarity index 100% rename from repos/effect/migration/fiberref.md rename to migration/fiberref.md diff --git a/repos/effect/migration/forking.md b/migration/forking.md similarity index 100% rename from repos/effect/migration/forking.md rename to migration/forking.md diff --git a/repos/effect/migration/generators.md b/migration/generators.md similarity index 100% rename from repos/effect/migration/generators.md rename to migration/generators.md diff --git a/repos/effect/migration/layer-memoization.md b/migration/layer-memoization.md similarity index 100% rename from repos/effect/migration/layer-memoization.md rename to migration/layer-memoization.md diff --git a/repos/effect/migration/runtime.md b/migration/runtime.md similarity index 100% rename from repos/effect/migration/runtime.md rename to migration/runtime.md diff --git a/repos/effect/migration/schema.md b/migration/schema.md similarity index 100% rename from repos/effect/migration/schema.md rename to migration/schema.md diff --git a/repos/effect/migration/scope.md b/migration/scope.md similarity index 100% rename from repos/effect/migration/scope.md rename to migration/scope.md diff --git a/repos/effect/migration/services.md b/migration/services.md similarity index 100% rename from repos/effect/migration/services.md rename to migration/services.md diff --git a/repos/effect/migration/v3-to-v4.md b/migration/v3-to-v4.md similarity index 99% rename from repos/effect/migration/v3-to-v4.md rename to migration/v3-to-v4.md index d10fe28cf..0adea8d81 100644 --- a/repos/effect/migration/v3-to-v4.md +++ b/migration/v3-to-v4.md @@ -2,9 +2,9 @@ # v3 to v4 Migration Reference -Base: `origin/v3` (`e730f59d29db5f1e312be6476a95d07bcad45788`) +Base: `e730f59d29db5f1e312be6476a95d07bcad45788` (`e730f59d29db5f1e312be6476a95d07bcad45788`) -Head: `HEAD` (`79b88977582fb715494d08405c73a47a593afced`) +Head: `origin/main` (`7547a99f81fa6f7e651d52f3d42081f399bfbca8`) This file is generated from the API diff and `migration/annotations/*.yaml`. @@ -4136,9 +4136,9 @@ effect/rpc/Utils (barrel: effect/rpc) ### `@effect/ai-openai/OpenAiLanguageModel` -- `OpenAiLanguageModel.Config` -> `OpenAiLanguageModel.Config`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. +- `OpenAiLanguageModel.Config` -> `OpenAiLanguageModel.Config`: Still exported in v4; update imports and adapt to the revised service and schema types. Unrecognized model names now default to reasoning models, sending system prompts as developer messages and requesting reasoning.encrypted\_content when item references are disabled or WebSockets are used. Set reasoningModel: false in config for deployments that do not support these defaults. -- `OpenAiLanguageModel.Config.Service` -> `OpenAiLanguageModel.Config.Service`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. +- `OpenAiLanguageModel.Config.Service` -> `OpenAiLanguageModel.Config.Service`: Still exported in v4 with revised configuration fields. Set reasoningModel: false for custom deployments that need system messages rather than developer messages and do not support automatic encrypted reasoning requests; unrecognized model names otherwise default to reasoning models. Fine-tuned ft: names follow their base model. - `OpenAiLanguageModel.Model` -> `OpenAiLanguageModel.Model`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. @@ -4156,6 +4156,8 @@ effect/rpc/Utils (barrel: effect/rpc) - `OpenAiTelemetry.OpenAiTelemetryAttributeOptions` -> `OpenAiTelemetry.OpenAiTelemetryAttributeOptions`: Still exported in v4; update imports and adapt to the revised v4 service and schema types. +- `OpenAiTelemetry.OpenAiTelemetryAttributes` -> `OpenAiTelemetry.OpenAiTelemetryAttributes`: Use openai.request.\* for request keys and openai.response.\* for response keys instead of gen\_ai.openai.\*. The response-format attribute was removed; use gen\_ai.output.type from LanguageModel instead. + - `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. @@ -4976,19 +4978,19 @@ effect/rpc/Utils (barrel: effect/rpc) - `McpServer.layerStdio` -> `McpServer.layerStdio`: Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai. -- `McpServer.prompt` -> `McpServer.prompt`: Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from prompt decoding and handler requirements instead of McpServerClient. +- `McpServer.prompt` -> `McpServer.prompt`: Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from prompt decoding, handler, and completion callback requirements instead of McpServerClient. -- `McpServer.registerPrompt` -> `McpServer.registerPrompt`: Moved to effect/ai/McpServer. Prompt decoding and handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata. +- `McpServer.registerPrompt` -> `McpServer.registerPrompt`: Moved to effect/ai/McpServer. Prompt decoding, handler, and completion callback requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata. - `McpServer.registerResource` -> `McpServer.registerResource`: Moved to effect/ai/McpServer. Resource and completion handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient; use the request context for protocol-neutral client metadata. -- `McpServer.registerToolkit` -> `McpServer.registerToolkit`: Moved to effect/ai/McpServer. Handler requirements now exclude McpSchema.McpRequestContext instead of McpServerClient. Strict tools reject excess input properties; raw JSON Schema dynamic tools cannot use strict mode. Declared handler failures produce isError results, while parameter validation fails with InvalidParams. +- `McpServer.registerToolkit` -> `McpServer.registerToolkit`: Moved to effect/ai/McpServer. Requirements from every tool handler are propagated, excluding McpSchema.McpRequestContext instead of McpServerClient; provide the remaining handler services. Strict tools reject excess input properties; raw JSON Schema dynamic tools cannot use strict mode. Declared handler failures produce isError results, while parameter validation fails with InvalidParams. - `McpServer.resource` -> `McpServer.resource`: Moved to effect/ai/McpServer. The registration layer excludes McpSchema.McpRequestContext from resource and completion handler requirements instead of McpServerClient. - `McpServer.run` -> `McpServer.run`: Moved to effect/ai/McpServer. Pass a non-empty protocols array of adapters, such as [McpProtocol.v2025\_06\_18], imported with McpProtocol from effect/ai; it remains the Effect-level runner over RpcServer.Protocol. -- `McpServer.toolkit` -> `McpServer.toolkit`: Moved to effect/ai/McpServer. The registration layer supplies McpSchema.McpRequestContext to handlers instead of excluding McpServerClient from requirements. Strict tools reject excess properties and require an Effect Schema rather than raw dynamic JSON Schema. +- `McpServer.toolkit` -> `McpServer.toolkit`: Moved to effect/ai/McpServer. The registration layer supplies McpSchema.McpRequestContext to handlers instead of excluding McpServerClient from requirements. Its requirements include services from every tool handler. Strict tools reject excess properties and require an Effect Schema rather than raw dynamic JSON Schema. ### `@effect/ai/Model` @@ -5076,6 +5078,8 @@ effect/rpc/Utils (barrel: effect/rpc) - `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.GenAITelemetryAttributes` -> `Telemetry.GenAITelemetryAttributes`: The flat span attribute keys changed: replace gen\_ai.system with gen\_ai.provider.name and remove gen\_ai.token.type. gen\_ai.output.type records text or json. Update code constructing or indexing this attribute 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. @@ -6188,6 +6192,8 @@ effect/rpc/Utils (barrel: effect/rpc) ### `@effect/opentelemetry/OtlpResource` +- `OtlpResource.make` -> `OtlpResource.make`: The constructor remains in the module moved to effect/observability/OtlpResource. + - `OtlpResource.unsafeServiceName` -> `OtlpResource.serviceNameUnsafe`: Moved to effect/observability/OtlpResource and renamed to follow the v4 unsafe-suffix convention. ### `@effect/opentelemetry/OtlpTracer` @@ -6318,6 +6324,12 @@ effect/rpc/Utils (barrel: effect/rpc) - `BunStream.stdout` -> `stdio.stdout() from Stdio.Stdio`: Standard output is correctly modeled as a Sink in v4. Explicitly adapt process.stdout with BunStream.fromReadable only to preserve the old unusual read behavior. +### `@effect/platform-bun/BunTerminal` + +- `BunTerminal.layer` -> `BunTerminal.layer`: The default layer now ends key input on bare Esc as well as Ctrl+C and Ctrl+D. To keep Esc as input, build a layer with Layer.effect(Terminal.Terminal, BunTerminal.make(shouldQuit)) using a custom quit predicate. + +- `BunTerminal.make` -> `BunTerminal.make`: V4 ends key input on bare Esc as well as Ctrl+C and Ctrl+D. Pass a custom shouldQuit predicate to make if your application needs to receive Esc; the v3 default quit only on Ctrl+C and Ctrl+D. + ### `@effect/platform-bun/BunWorker` - `BunWorker.layerManager` -> `BunWorker.layerPlatform`: WorkerManager was removed. Provide WorkerPlatform directly, or use BunWorker.layer(spawn) when a Worker.Spawner is also required. @@ -6362,6 +6374,12 @@ effect/rpc/Utils (barrel: effect/rpc) - `NodeStream.stdout` -> `NodeStream.fromReadable({ evaluate: () => process.stdout, closeOnDone: false }).pipe(Stream.orDie)`: This preserves the unusual v3 read behavior; for normal output use the stdio.stdout() Sink from effect/Stdio. +### `@effect/platform-node-shared/NodeTerminal` + +- `NodeTerminal.layer` -> `NodeTerminal.layer`: The default layer now ends key input on bare Esc as well as Ctrl+C and Ctrl+D. To keep Esc as input, build a layer with Layer.effect(Terminal.Terminal, NodeTerminal.make(shouldQuit)) using a custom quit predicate. + +- `NodeTerminal.make` -> `NodeTerminal.make`: V4 ends key input on bare Esc as well as Ctrl+C and Ctrl+D. Pass a custom shouldQuit predicate to make if your application needs to receive Esc; the v3 default quit only on Ctrl+C and Ctrl+D. + ### `@effect/platform-node/NodeCommandExecutor` - `NodeCommandExecutor.layer` -> `NodeChildProcessSpawner.layer`: CommandExecutor became ChildProcessSpawner; use the @effect/platform-node/NodeChildProcessSpawner re-export. @@ -6404,6 +6422,12 @@ effect/rpc/Utils (barrel: effect/rpc) - `NodeHttpServer.layerContext` -> `NodeHttpServer.layerHttpServices`: Direct rename; it provides NodeServices, HttpPlatform, and Etag.Generator, without the removed WorkerManager. +### `@effect/platform-node/NodeTerminal` + +- `NodeTerminal.layer` -> `NodeTerminal.layer`: The default layer now ends key input on bare Esc as well as Ctrl+C and Ctrl+D. To keep Esc as input, build a layer with Layer.effect(Terminal.Terminal, NodeTerminal.make(shouldQuit)) using a custom quit predicate. + +- `NodeTerminal.make` -> `NodeTerminal.make`: V4 ends key input on bare Esc as well as Ctrl+C and Ctrl+D. Pass a custom shouldQuit predicate to make if your application needs to receive Esc; the v3 default quit only on Ctrl+C and Ctrl+D. + ### `@effect/platform-node/NodeWorker` - `NodeWorker.layerManager` -> `NodeWorker.layerPlatform`: WorkerManager was removed. Provide WorkerPlatform directly, or use NodeWorker.layer(spawn) when a Worker.Spawner is also required. @@ -6718,7 +6742,7 @@ effect/rpc/Utils (barrel: effect/rpc) ### `@effect/platform/HttpApiClient` -- `HttpApiClient.Client.Method` -> `effect/http-api/HttpApiClient#Client.Method`: The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse. +- `HttpApiClient.Client.Method` -> `effect/http-api/HttpApiClient#Client.Method`: The type remains without GroupError. Requests use params/query and responseMode instead of path/urlParams and withResponse. Omitting responseMode returns the decoded value; a union-valued mode returns the corresponding union of decoded values, responses, or [value, response] tuples. - `HttpApiClient.endpoint` -> `effect/http-api/HttpApiClient#endpoint`: The endpoint client remains, selected by group and endpoint identifiers and using v4 request and responseMode fields. @@ -7264,7 +7288,7 @@ effect/rpc/Utils (barrel: effect/rpc) - `HttpServer.Address` -> `effect/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union. -- `HttpServer.HttpServer` -> `HttpServer.HttpServer`: The interface and tag became one Context.Service class; use its Service member for implementations. +- `HttpServer.HttpServer` -> `HttpServer.HttpServer`: The interface and tag became one Context.Service class; use its Service member for implementations. The service-level serve method derives requirements from the middleware-transformed application, so services added by middleware must also be provided. - `HttpServer.ServeOptions` -> `none`: The unused respond option model was removed with no shared v4 counterpart. @@ -8036,6 +8060,8 @@ effect/rpc/Utils (barrel: effect/rpc) - `SqlClient.SafeIntegers` -> `effect/sql/SqlClient#SafeIntegers`: Moved and changed from a Reference subclass to a Context.Reference value; provide it as a service. +- `SqlClient.SqlClient.MakeOptions`: TODO: needs guidance + - `SqlClient.TransactionConnection` -> `effect/sql/SqlClient#TransactionConnection`: Now a factory keyed by client id, not a singleton tag. Prefer the client's transactionService; the payload type is TransactionConnection.Service. - `SqlClient.TypeId` -> `none`: The brand is private in v4; do not inspect or attach it, and obtain clients through SqlClient or SqlClient.make. @@ -9174,7 +9200,7 @@ Arbitrary.schema(schema) - `Channel.mapInputIn` -> `Channel.mapInput`: Use Channel.mapInput(self, (value) =\> Effect.succeed(f(value))); v4 consolidated pure and effectful input mapping. -- `Channel.mapInputInEffect` -> `Channel.mapInput`: Renamed to mapInput; the mapper remains effectful. +- `Channel.mapInputInEffect` -> `Channel.mapInput`: Renamed to mapInput; the mapper remains effectful. Both overloads retain mapper service requirements in the resulting channel, so provide those services before running it. - `Channel.mapOut` -> `Channel.map`: Renamed to map; the v4 mapper also receives the element index. @@ -9612,9 +9638,11 @@ Arbitrary.schema(schema) - `DateTime.Zoned` -> `DateTime.Zoned`: The model remains; rename epochMillis and adjustedEpochMillis to epochMilliseconds and adjustedEpochMilliseconds. -- `DateTime.distanceDuration` -> `Duration.millis(Math.abs(DateTime.distance(self, other)))`: DateTime.distance returns signed milliseconds in v4; take the absolute value and construct a Duration to preserve v3 behavior. +- `DateTime.distance` -> `Duration.toMillis(DateTime.distance(self, other))`: V3 returns signed milliseconds; v4 returns a signed Duration (other minus self). Convert it with Duration.toMillis when a numeric millisecond result is required. + +- `DateTime.distanceDuration` -> `Duration.abs(DateTime.distance(self, other))`: V4 DateTime.distance already returns a signed Duration. Use Duration.abs to preserve the non-negative distance returned by v3; Math.abs and Duration.millis expect numbers, not Durations. -- `DateTime.distanceDurationEither` -> `DateTime.distance + Result`: Compute the signed millisecond distance, wrap its absolute Duration as Result.succeed when positive and Result.fail when non-positive; v4 uses Result instead of Either. +- `DateTime.distanceDurationEither` -> `DateTime.distance + Result`: Let diff = DateTime.distance(self, other), a signed Duration. Use Duration.isPositive(diff) ? Result.succeed(Duration.abs(diff)) : Result.fail(Duration.abs(diff)) to preserve v3 behavior, including equal dates producing a failure with Duration.zero. V4 uses Result instead of Either. - `DateTime.greaterThan` -> `DateTime.isGreaterThan`: The comparison was renamed with the is prefix. @@ -10382,11 +10410,11 @@ Arbitrary.schema(schema) ### `effect/ExecutionPlan` -- `ExecutionPlan.ExecutionPlan` -> `ExecutionPlan.ExecutionPlan`: The plan type remains; withRequirements was renamed to captureRequirements. +- `ExecutionPlan.ExecutionPlan` -> `ExecutionPlan.ExecutionPlan`: The plan type remains; withRequirements was renamed to captureRequirements. Step schedules use Schedule\; captureRequirements supplies captured services to schedule initialization and execution. - `ExecutionPlan.TypesBase` -> `ExecutionPlan.ConfigBase`: The base type for execution-plan step configuration was renamed. -- `ExecutionPlan.make` -> `ExecutionPlan.make`: The variadic execution-plan constructor remains unchanged. +- `ExecutionPlan.make` -> `ExecutionPlan.make`: The constructor remains variadic. Its inferred error channel includes failures from step layers, predicates, and schedules; requirements include services needed by all three across every step. ### `effect/ExecutionStrategy` @@ -14103,7 +14131,7 @@ Schema.toFormatter(schema) - `Scheduler.ControlledScheduler` -> `none`: No public step-controlled scheduler remains; implement Scheduler and SchedulerDispatcher for exact controlled stepping. -- `Scheduler.MixedScheduler` -> `Scheduler.MixedScheduler`: The class remains with a redesigned constructor and makeDispatcher-based task API. +- `Scheduler.MixedScheduler` -> `Scheduler.MixedScheduler`: The class remains with a redesigned constructor and makeDispatcher-based task API. On Cloudflare Workers, request-owned scheduler timers can leave shared fibers suspended; provide new Scheduler.MixedScheduler("sync") to both layers and requests and build handlers at module scope (see HttpRouter.toWebHandler). Sync mode does not yield to the event loop and does not fix request-owned Effect.sleep timers or request-bound async contexts. - `Scheduler.PriorityBuckets` -> `none`: Priority buckets are now an internal Scheduler implementation detail. diff --git a/repos/effect/migration/yieldable.md b/migration/yieldable.md similarity index 100% rename from repos/effect/migration/yieldable.md rename to migration/yieldable.md diff --git a/old.patch b/old.patch deleted file mode 100644 index e69de29bb..000000000 diff --git a/package.json b/package.json index 06659be2e..3be5cbf06 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,84 @@ { "private": true, - "packageManager": "pnpm@11.18.0", - "engines": { - "node": ">=24", - "pnpm": ">= 11.0.0" - }, + "type": "module", + "packageManager": "pnpm@11.20.0", "scripts": { - "all": "pnpm -r", - "pub": "pnpm ci:version && git commit -am 'chore: update versions' && pnpm pub:only", - "pub:only": "pnpm ci:publish && git push && git push --follow-tags", - "ci:version": "pnpm changeset version", - "ci:publish": "pnpm build && pnpm changeset publish", - "preinstall": "npx only-allow pnpm", - "clean": "pnpm all clean", - "clean-dist": "pnpm -r clean-dist", - "lint-fix": "NODE_OPTIONS=--max-old-space-size=6144 pnpm -r --no-bail lint-fix", - "lint": "pnpm -r lint", - "circular:dist": "pnpm -r circular:dist", - "test": "pnpm -r --no-bail test:run", - "pr:ready": "node scripts/agent-pr-ready.mjs", - "testsuite": "pnpm -r --no-bail testsuite", - "up-all": "pnpm --recursive update", - "u": "pnpm run update && pnpm i && pnpm dedupe", - "update": "ncu -u && pnpm -r exec ncu -u", - "ncu": "ncu", - "packages": "pnpm -r --workspace-concurrency 1", - "circular-packages": "pnpm packages circular", - "lint-packages": "pnpm packages lint", - "test-packages": "pnpm packages test:run", - "testsuite-packages": "pnpm packages testsuite", - "watch-packages": "pnpm packages build && pnpm packages watch", - "build:tsc": "pnpm check && node scripts/rewrite-dist-declaration-imports.mjs", - "check": "tsgo --build ./tsconfig.all.json", - "watch": "pnpm build:tsc --watch", - "build": "cd packages/eslint-shared-config && pnpm build && cd ../.. && pnpm build:tsc && cd packages/vue-components && pnpm build", - "rbuild": "pnpm clean && pnpm build", - "nnm": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && pnpm i", - "prepare": "node -e \"const v=require('typescript/package.json').version;if(v.startsWith('6.')){require('child_process').execSync('effect-language-service patch',{stdio:'inherit'})}else{console.log('Skipping effect-language-service patch for TypeScript '+v)}\" && effect-tsgo patch --no-typescript --oxlint && node scripts/install-git-hooks.mjs", - "subtree:effect": "node scripts/sync-effect-subtree.js" - }, - "dependencies": { - "fast-check": "^4.9.0", - "vue": "^3.5.35" + "prepare": "node scripts/setup-agents.mjs && effect-tsgo patch", + "clean": "node scripts/clean.mjs", + "codegen": "pnpm --recursive --parallel --filter \"./packages/**/*\" run codegen", + "codemod": "node scripts/codemod.mjs", + "build": "tsc -b tsconfig.packages.json && pnpm --recursive --parallel --filter \"./packages/**/*\" run build && node scripts/copy-ai-docs.mjs", + "bundle-analyze": "bash scripts/bundle-analyze.sh", + "bundle-compare": "bash scripts/bundle-compare.sh", + "bundle-compare-selected": "bash scripts/bundle-compare-selected.sh", + "circular": "node scripts/circular.mjs", + "test": "vitest", + "test-cluster": "vitest run --project cluster-integration", + "doctest": "vitest --config vitest.docs.ts", + "coverage": "vitest --coverage", + "check": "tsc -b tsconfig.json", + "typeperf": "pnpm --dir packages/effect exec node typeperf/run.mjs", + "typeperf-compare": "pnpm --dir packages/effect exec node typeperf/compare.mjs", + "runtimeperf": "pnpm --dir packages/effect exec node runtimeperf/run.mts", + "runtimeperf-compare": "pnpm --dir packages/effect exec node runtimeperf/compare.mts", + "check-recursive": "pnpm --recursive --filter \"./packages/**/*\" exec tsc -b tsconfig.json", + "check-dist-types": "node scripts/check-dist-types.mjs", + "jsdocs": "effect-jsdocs", + "lint": "oxlint -f unix && dprint check", + "lint-fix": "oxlint --fix && dprint fmt", + "ai-docgen": "effect-ai-docgen ai-docs/src -o LLMS.md", + "ai-docgen:watch": "pnpm ai-docgen --watch", + "api-diff": "pnpm --dir packages/tools/api-diff exec node src/bin.ts", + "test-types": "tstyche --target '>=5.9'", + "changeset-version": "changeset version && node scripts/version.mjs", + "changeset-publish": "node scripts/set-strip-internal.mjs && pnpm codemod && pnpm build && changeset publish --tag staging" }, "devDependencies": { - "@changesets/cli": "^2.31.0", - "@effect-app/cli": "workspace:*", - "@effect-app/eslint-codegen-model": "workspace:*", - "@effect-app/infra": "workspace:*", - "@effect/language-service": "0.87.3", - "@effect/tsgo": "^0.47.2", - "@effect/platform-node": "4.0.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.2", - "effect-app": "workspace:*", - "eslint": "^10.4.1", - "json5": "^2.2.3", - "lint-staged": "^17.3.0", + "@babel/cli": "^8.0.6", + "@babel/core": "^8.0.7", + "@babel/plugin-transform-export-namespace-from": "^8.0.1", + "@babel/plugin-transform-modules-commonjs": "^8.0.1", + "@changesets/changelog-github": "1.0.1", + "@changesets/cli": "3.0.3", + "@effect/ai-docgen": "workspace:^", + "@effect/bundle": "workspace:^", + "@effect/docgen": "workspace:^", + "@effect/doctest": "workspace:^", + "@effect/jsdocs": "workspace:^", + "@effect/oxc": "workspace:^", + "@effect/tsgo": "^0.51.1", + "@effect/utils": "workspace:^", + "@effect/vitest": "workspace:^", + "@faker-js/faker": "^10.6.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-replace": "^6.0.3", + "@rollup/plugin-terser": "^1.0.0", + "@types/jscodeshift": "^17.3.0", + "@types/node": "^26.6.5", + "@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.61.1", + "fast-check": "^4.10.2", + "glob": "^13.0.6", + "happy-dom": "^20.14.6", + "jscodeshift": "^17.4.0", + "lalph": "^0.3.139", "madge": "^8.0.0", - "npm-check-updates": "^22.2.1", - "oxlint": "1.82.0", - "oxlint-tsgolint": "7.0.2001", - "pkg-pr-new": "0.0.75", - "ts-plugin-sort-import-suggestions": "^1.0.4", - "typescript": "~6.0.3", - "vite": "^8.0.15", - "vitest": "^4.1.7" + "oxlint": "^1.87.0", + "pkg-pr-new": "0.0.91", + "playwright": "^1.64.0", + "rollup": "^4.64.5", + "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.4", + "vitest": "^5.0.3", + "vitest-websocket-mock": "^0.8.0", + "zod": "^4.6.5" } } diff --git a/repos/effect/packages/ai/anthropic/CHANGELOG.md b/packages/ai/anthropic/CHANGELOG.md similarity index 93% rename from repos/effect/packages/ai/anthropic/CHANGELOG.md rename to packages/ai/anthropic/CHANGELOG.md index ccd63f7aa..d1d417677 100644 --- a/repos/effect/packages/ai/anthropic/CHANGELOG.md +++ b/packages/ai/anthropic/CHANGELOG.md @@ -1,5 +1,30 @@ # @effect/ai-anthropic +## 4.0.5 + +### Patch Changes + +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- [#9008](https://github.com/Effect-TS/effect/pull/9008) [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d) Thanks @effect-bot! - Add `toolCallValidation: "deferred"` for callers using `disableToolCallResolution: true`. Model-generated tool calls return with `unknown` parameters, including invalid parameters and undeclared tool names, even without a toolkit. Supported by the Anthropic, OpenAI, and OpenAI-compatible providers. Strict validation remains the default; provider-executed calls remain validated. + +- [#9029](https://github.com/Effect-TS/effect/pull/9029) [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163) Thanks @effect-bot! - Clean up unused Anthropic bindings and make documentation-only imports type-only, preserving server-tool inputs and documentation links. +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- [#8971](https://github.com/Effect-TS/effect/pull/8971) [`1db5671`](https://github.com/Effect-TS/effect/commit/1db5671d77e70a07d65d3c8a17056a9d46e748e2) Thanks @m9tdev! - Decode streamed and non-streamed Anthropic fallback responses. Preserve fallback blocks in response metadata and model attribution in usage iterations. +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/ai/anthropic/README.md b/packages/ai/anthropic/README.md similarity index 100% rename from repos/effect/packages/ai/anthropic/README.md rename to packages/ai/anthropic/README.md diff --git a/repos/effect/packages/ai/anthropic/codegen.yml b/packages/ai/anthropic/codegen.yml similarity index 96% rename from repos/effect/packages/ai/anthropic/codegen.yml rename to packages/ai/anthropic/codegen.yml index 12e961dee..2d576c581 100644 --- a/repos/effect/packages/ai/anthropic/codegen.yml +++ b/packages/ai/anthropic/codegen.yml @@ -9,6 +9,8 @@ patches: - '[{"op":"replace","path":"/components/schemas/BetaInputMessage/properties/role/enum","value":["user","assistant","system"]},{"op":"replace","path":"/components/schemas/InputMessage/properties/role/enum","value":["user","assistant","system"]}]' # Make iterations and speed optional in BetaUsage, iterations optional in BetaMessageDeltaUsage (API may omit these fields) - '[{"op":"remove","path":"/components/schemas/BetaUsage/required/9"},{"op":"remove","path":"/components/schemas/BetaUsage/required/5"},{"op":"remove","path":"/components/schemas/BetaMessageDeltaUsage/required/3"}]' + # Make model optional in BetaMessageIterationUsage (gateways and older responses may omit it) + - '[{"op":"replace","path":"/components/schemas/BetaMessageIterationUsage/required","value":["cache_creation","cache_creation_input_tokens","cache_read_input_tokens","input_tokens","output_tokens","type"]}]' header: | /** * @stability unstable diff --git a/repos/effect/packages/ai/anthropic/package.json b/packages/ai/anthropic/package.json similarity index 98% rename from repos/effect/packages/ai/anthropic/package.json rename to packages/ai/anthropic/package.json index eb3b2c36a..e38e71320 100644 --- a/repos/effect/packages/ai/anthropic/package.json +++ b/packages/ai/anthropic/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-anthropic", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "An Anthropic provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts b/packages/ai/anthropic/src/AnthropicClient.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicClient.ts rename to packages/ai/anthropic/src/AnthropicClient.ts diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts b/packages/ai/anthropic/src/AnthropicConfig.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts rename to packages/ai/anthropic/src/AnthropicConfig.ts diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicError.ts b/packages/ai/anthropic/src/AnthropicError.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicError.ts rename to packages/ai/anthropic/src/AnthropicError.ts diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts b/packages/ai/anthropic/src/AnthropicLanguageModel.ts similarity index 98% rename from repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts rename to packages/ai/anthropic/src/AnthropicLanguageModel.ts index 3709ddec4..8f3e30f94 100644 --- a/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts +++ b/packages/ai/anthropic/src/AnthropicLanguageModel.ts @@ -659,6 +659,24 @@ declare module "effect/ai/Response" { requestId?: string | null } | null } + + /** + * Anthropic metadata attached to response metadata parts. + * + * **Details** + * + * Fallback blocks are stored in `anthropic.fallback`; the part's `modelId` + * identifies the fallback model. + * + * @stability unstable + * @category models + * @since 4.0.3 + */ + export interface ResponseMetadataPartMetadata extends ProviderMetadata { + readonly anthropic?: { + readonly fallback?: typeof Generated.BetaResponseFallbackBlock.Encoded + } | null + } } // ============================================================================= @@ -1044,7 +1062,7 @@ const prepareMessages = Effect.fnUntraced( pendingSystem = [] } - const content: Array = [] + const content: Array = [] const mcpToolIds = new Set() for (let j = 0; j < group.messages.length; j++) { @@ -1142,7 +1160,7 @@ const prepareMessages = Effect.fnUntraced( Predicate.hasProperty(part.params, "type") && part.params.type === "programmatic-tool-call" ) { - const { type, ...params } = part.params + const { type: _type, ...params } = part.params content.push({ type: "server_tool_use", id: part.id, @@ -1651,6 +1669,15 @@ const makeResponse = Effect.fnUntraced( for (const part of rawResponse.content) { switch (part.type) { + case "fallback": { + parts.push({ + type: "response-metadata", + modelId: part.to.model, + timestamp: DateTime.formatIso(yield* DateTime.now), + metadata: { anthropic: { fallback: part } } + }) + break + } case "text": { // The response tool supplies the JSON payload. Accompanying prose // must not be concatenated with it during structured output decoding. @@ -1722,7 +1749,7 @@ const makeResponse = Effect.fnUntraced( // Map the provider wire name (e.g. "memory") back to the tool's // custom name (e.g. "AnthropicMemory") that the toolkit is keyed by const toolName = toolNameMapper.getCustomName(part.name) - const params = yield* transformToolCallParams(options.tools, toolName, part.input) + const params = yield* transformToolCallParams(options, toolName, part.input) parts.push({ type: "tool-call", @@ -2162,7 +2189,7 @@ const makeStreamResponse = Effect.fnUntraced( id: part.id }) - const params = yield* transformToolCallParams(options.tools, toolName, part.input) + const params = yield* transformToolCallParams(options, toolName, part.input) parts.push({ type: "tool-call", @@ -2602,6 +2629,17 @@ const makeStreamResponse = Effect.fnUntraced( break } + + case "fallback": { + parts.push({ + type: "response-metadata", + modelId: event.content_block.to.model, + timestamp: DateTime.formatIso(yield* DateTime.now), + metadata: { anthropic: { fallback: event.content_block } } + }) + + break + } } break @@ -2765,7 +2803,7 @@ const makeStreamResponse = Effect.fnUntraced( const params = contentBlock.providerExecuted === true ? toolParams - : yield* transformToolCallParams(options.tools, contentBlock.name, toolParams) + : yield* transformToolCallParams(options, contentBlock.name, toolParams) parts.push({ type: "tool-call", @@ -3205,20 +3243,23 @@ const getOutputFormat = Effect.fnUntraced(function*({ capabilities, options }: { return undefined }) -const transformToolCallParams = Effect.fnUntraced(function*>( - tools: Tools, +const transformToolCallParams = Effect.fnUntraced(function*( + options: LanguageModel.ProviderOptions, toolName: string, toolParams: unknown ): Effect.fn.Return { - const tool = tools.find((tool) => tool.name === toolName) + const tool = options.tools.find((tool) => tool.name === toolName) if (Predicate.isUndefined(tool)) { + if (options.toolCallValidation === "deferred") { + return toolParams + } return yield* AiError.make({ module: "AnthropicLanguageModel", method: "makeResponse", reason: new AiError.ToolNotFoundError({ toolName, - availableTools: tools.map((tool) => tool.name) + availableTools: options.tools.map((tool) => tool.name) }) }) } diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts b/packages/ai/anthropic/src/AnthropicTelemetry.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts rename to packages/ai/anthropic/src/AnthropicTelemetry.ts diff --git a/repos/effect/packages/ai/anthropic/src/AnthropicTool.ts b/packages/ai/anthropic/src/AnthropicTool.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/AnthropicTool.ts rename to packages/ai/anthropic/src/AnthropicTool.ts diff --git a/repos/effect/packages/ai/anthropic/src/Generated.ts b/packages/ai/anthropic/src/Generated.ts similarity index 99% rename from repos/effect/packages/ai/anthropic/src/Generated.ts rename to packages/ai/anthropic/src/Generated.ts index 16969168e..7622bc5be 100644 --- a/repos/effect/packages/ai/anthropic/src/Generated.ts +++ b/packages/ai/anthropic/src/Generated.ts @@ -2915,6 +2915,52 @@ export const Model = Schema.Union([ "description": "The model that will complete your prompt.\n\nSee [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options." }) +export type BetaResponseFallbackHopInfo = { readonly "model": Model } +export const BetaResponseFallbackHopInfo = Schema.Struct({ "model": Model }).annotate({ + "title": "ResponseFallbackHopInfo", + "description": "Identifies one hop of a fallback transition." +}) +export type BetaRefusalCategory = "cyber" | "bio" | "frontier_llm" | "reasoning_extraction" | "general_harms" +export const BetaRefusalCategory = Schema.Literals([ + "cyber", + "bio", + "frontier_llm", + "reasoning_extraction", + "general_harms" +]).annotate({ "title": "RefusalCategory", "description": "The policy category that triggered a refusal." }) +export type BetaFallbackRefusalTrigger = { readonly "category": BetaRefusalCategory | null; readonly "type": "refusal" } +export const BetaFallbackRefusalTrigger = Schema.Struct({ + "category": Schema.Union([BetaRefusalCategory, Schema.Null]).annotate({ + "description": + "The policy category that triggered the `from` model's refusal at this hop. `null` when the refusal doesn't map to a named category. Same vocabulary as `stop_details.category`.", + "default": null + }), + "type": Schema.Literal("refusal").annotate({ "title": "Type", "default": "refusal" }) +}).annotate({ "title": "FallbackRefusalTrigger", "description": "The `from` model declined for policy reasons." }) +export type BetaResponseFallbackBlock = { + readonly "from": BetaResponseFallbackHopInfo + readonly "to": BetaResponseFallbackHopInfo + readonly "trigger": BetaFallbackRefusalTrigger + readonly "type": "fallback" +} +export const BetaResponseFallbackBlock = Schema.Struct({ + "from": BetaResponseFallbackHopInfo.annotate({ + "description": + "The model whose output ends at this point — the model that declined at this hop. When the declining hop is the requested model, its `model` echoes the top-level `model` string the caller sent (alias or canonical); when the declining hop is a fallback model, its `model` is that model's canonical id." + }), + "to": BetaResponseFallbackHopInfo.annotate({ + "description": + "The fallback model producing the content that follows this block. Its `model` is always the canonical id." + }), + "trigger": BetaFallbackRefusalTrigger.annotate({ + "description": "What caused the `from` model to hand over at this hop." + }), + "type": Schema.Literal("fallback").annotate({ "title": "Type", "default": "fallback" }) +}).annotate({ + "title": "ResponseFallbackBlock", + "description": + "Marks the point in `content` where one model's output gives way to the next.\n\nOne block appears per hop where a preceding model actually ran this turn and\ndeclined. A turn where no preceding model ran and declined has no such\nboundary and carries no block — the signal for whether a fallback model\nserved the response is the presence of a `fallback_message` entry in\n`usage.iterations`, not this block.\n\nThe block is treated like a server-tool content block for streaming: it\narrives via the standard `content_block_start` / `content_block_stop`\npair and carries no deltas." +}) export type BetaMemoryTool_20250818_ViewCommand = { readonly "command": "view" readonly "path": string @@ -3343,6 +3389,7 @@ export type BetaMessageIterationUsage = { readonly "cache_creation_input_tokens": number readonly "cache_read_input_tokens": number readonly "input_tokens": number + readonly "model"?: Model readonly "output_tokens": number readonly "type": "message" } @@ -3365,6 +3412,7 @@ export const BetaMessageIterationUsage = Schema.Struct({ "title": "Input Tokens", "description": "The number of input tokens which were used." }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), + "model": Schema.optionalKey(Model), "output_tokens": Schema.Number.annotate({ "title": "Output Tokens", "description": "The number of output tokens which were used." @@ -6672,9 +6720,56 @@ export const BetaRequestToolSearchToolSearchResultBlock = Schema.Struct({ "tool_references": Schema.Array(BetaRequestToolReferenceBlock).annotate({ "title": "Tool References" }), "type": Schema.Literal("tool_search_tool_search_result").annotate({ "title": "Type" }) }).annotate({ "title": "RequestToolSearchToolSearchResultBlock" }) -export type BetaIterationsUsage = ReadonlyArray | null +export type BetaFallbackMessageIterationUsage = { + readonly "cache_creation": BetaCacheCreation | null + readonly "cache_creation_input_tokens": number + readonly "cache_read_input_tokens": number + readonly "input_tokens": number + readonly "model": Model + readonly "output_tokens": number + readonly "type": "fallback_message" +} +export const BetaFallbackMessageIterationUsage = Schema.Struct({ + "cache_creation": Schema.Union([BetaCacheCreation, Schema.Null]).annotate({ + "description": "Breakdown of cached tokens by TTL", + "default": null + }), + "cache_creation_input_tokens": Schema.Number.annotate({ + "title": "Cache Creation Input Tokens", + "description": "The number of input tokens used to create the cache entry.", + "default": 0 + }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), + "cache_read_input_tokens": Schema.Number.annotate({ + "title": "Cache Read Input Tokens", + "description": "The number of input tokens read from the cache.", + "default": 0 + }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), + "input_tokens": Schema.Number.annotate({ + "title": "Input Tokens", + "description": "The number of input tokens which were used." + }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), + "model": Model, + "output_tokens": Schema.Number.annotate({ + "title": "Output Tokens", + "description": "The number of output tokens which were used." + }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), + "type": Schema.Literal("fallback_message").annotate({ + "title": "Type", + "description": "Usage for the fallback-model attempt that served the response", + "default": "fallback_message" + }) +}).annotate({ + "title": "FallbackMessageIterationUsage", + "description": + "Token usage for the fallback-model attempt of a server-side fallback request.\n\nProduced in place of a `message` entry for whichever hop served the\nresponse. A declined hop produces the existing `message` entry. Whether\na fallback model served the response is signalled by the presence of this\nentry in `usage.iterations`." +}) +export type BetaIterationsUsage = + | ReadonlyArray + | null export const BetaIterationsUsage = Schema.Union([ - Schema.Array(Schema.Union([BetaMessageIterationUsage, BetaCompactionIterationUsage])), + Schema.Array( + Schema.Union([BetaMessageIterationUsage, BetaCompactionIterationUsage, BetaFallbackMessageIterationUsage]) + ), Schema.Null ]).annotate({ "title": "Iterations", @@ -7554,6 +7649,7 @@ export type BetaContentBlockStartEvent = { | BetaResponseMCPToolResultBlock | BetaResponseContainerUploadBlock | BetaResponseCompactionBlock + | BetaResponseFallbackBlock readonly "index": number readonly "type": "content_block_start" } @@ -7573,7 +7669,8 @@ export const BetaContentBlockStartEvent = Schema.Struct({ BetaResponseMCPToolUseBlock, BetaResponseMCPToolResultBlock, BetaResponseContainerUploadBlock, - BetaResponseCompactionBlock + BetaResponseCompactionBlock, + BetaResponseFallbackBlock ], { mode: "oneOf" }).annotate({ "title": "Content Block" }), "index": Schema.Number.annotate({ "title": "Index" }).check(Schema.isInt()), "type": Schema.Literal("content_block_start").annotate({ "title": "Type", "default": "content_block_start" }) @@ -7594,6 +7691,7 @@ export type BetaContentBlock = | BetaResponseMCPToolResultBlock | BetaResponseContainerUploadBlock | BetaResponseCompactionBlock + | BetaResponseFallbackBlock export const BetaContentBlock = Schema.Union([ BetaResponseTextBlock, BetaResponseThinkingBlock, @@ -7609,7 +7707,8 @@ export const BetaContentBlock = Schema.Union([ BetaResponseMCPToolUseBlock, BetaResponseMCPToolResultBlock, BetaResponseContainerUploadBlock, - BetaResponseCompactionBlock + BetaResponseCompactionBlock, + BetaResponseFallbackBlock ], { mode: "oneOf" }) export type BetaRequestWebFetchResultBlock = { readonly "content": BetaRequestDocumentBlock diff --git a/repos/effect/packages/ai/anthropic/src/index.ts b/packages/ai/anthropic/src/index.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/index.ts rename to packages/ai/anthropic/src/index.ts diff --git a/repos/effect/packages/ai/anthropic/src/internal/errors.ts b/packages/ai/anthropic/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/internal/errors.ts rename to packages/ai/anthropic/src/internal/errors.ts diff --git a/repos/effect/packages/ai/anthropic/src/internal/utilities.ts b/packages/ai/anthropic/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/src/internal/utilities.ts rename to packages/ai/anthropic/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts b/packages/ai/anthropic/test/AnthropicClient.test.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/test/AnthropicClient.test.ts rename to packages/ai/anthropic/test/AnthropicClient.test.ts diff --git a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts b/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts similarity index 87% rename from repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts rename to packages/ai/anthropic/test/AnthropicLanguageModel.test.ts index 5f240511d..2d8975226 100644 --- a/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts +++ b/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts @@ -254,6 +254,84 @@ describe("AnthropicLanguageModel", () => { assert.deepStrictEqual(toolCall.params, toolParams) })) + it.effect("streams undeclared tool calls when tool call validation is deferred", () => + Effect.gen(function*() { + 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: null, + cache_read_input_tokens: null, + inference_geo: null, + input_tokens: 10, + output_tokens: 0, + service_tier: null + } + } + }, + { + type: "content_block_start", + index: 0, + content_block: { type: "tool_use", id: "toolu_test_1", name: "Write", input: {} } + }, + { + type: "content_block_delta", + index: 0, + delta: { type: "input_json_delta", partial_json: JSON.stringify({ path: "a" }) } + }, + { type: "content_block_stop", index: 0 }, + { + type: "message_delta", + delta: { stop_reason: "tool_use", stop_sequence: null }, + usage: { + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + input_tokens: null, + output_tokens: 5 + } + }, + { type: "message_stop" } + ])) + ) + )) + ) + const Read = Tool.make("Read", { + parameters: Schema.Struct({ path: Schema.String }), + success: Schema.String + }) + const toolkit = Toolkit.make(Read) + + const parts = yield* LanguageModel.streamText({ + prompt: "use the tools", + toolkit, + disableToolCallResolution: true, + toolCallValidation: "deferred" + }).pipe( + Stream.runCollect, + Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")), + Effect.provide(toolkit.toLayer({ Read: () => Effect.succeed("") })), + Effect.provide(layer) + ) + + assert.deepStrictEqual( + parts.filter((part) => part.type === "tool-call").map(({ name, params }) => ({ name, params })), + [{ name: "Write", params: { path: "a" } }] + ) + })) + for (const blockType of ["tool_use", "server_tool_use"] as const) { it.effect(`fails with AiError for malformed streamed ${blockType} JSON`, () => Effect.gen(function*() { @@ -660,6 +738,72 @@ describe("AnthropicLanguageModel", () => { assert.strictEqual(text.delta, "Hello") })) + + it.effect("emits response metadata for a fallback block", () => + Effect.gen(function*() { + const fallback = { + type: "fallback", + from: { model: "claude-opus-5-5" }, + to: { model: "claude-opus-4-8" }, + trigger: { type: "refusal", category: "cyber" } + } as const + 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-opus-4-8", + content: [], + stop_reason: null, + stop_sequence: null, + usage: { + cache_creation: null, + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + input_tokens: 10, + output_tokens: 0, + service_tier: null + } + } + }, + { type: "content_block_start", index: 0, content_block: fallback }, + { type: "content_block_stop", index: 0 }, + { + 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-opus-5-5")), + Effect.provide(layer) + ) + const metadata = globalThis.Array.from(parts).find((part) => + part.type === "response-metadata" && part.metadata.anthropic?.fallback !== undefined + ) + assert.strictEqual(metadata?.type === "response-metadata" ? metadata.modelId : undefined, "claude-opus-4-8") + assert.deepStrictEqual( + metadata?.type === "response-metadata" ? metadata.metadata.anthropic?.fallback : undefined, + fallback + ) + })) }) describe("generateText", () => { @@ -706,6 +850,79 @@ describe("AnthropicLanguageModel", () => { })) } + it.effect("emits response metadata for a fallback block and keeps fallback usage iterations", () => + Effect.gen(function*() { + const fallback = { + type: "fallback", + from: { model: "claude-opus-5-5" }, + to: { model: "claude-opus-4-8" }, + trigger: { type: "refusal", category: "cyber" } + } as const + const iterations = [ + { + type: "message", + model: "claude-opus-5-5", + cache_creation: null, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + input_tokens: 10, + output_tokens: 0 + }, + { + type: "fallback_message", + model: "claude-opus-4-8", + cache_creation: null, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + input_tokens: 10, + output_tokens: 5 + } + ] as const + 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-opus-4-8", + content: [fallback, { type: "text", text: "Hello" }], + stop_reason: "end_turn", + stop_sequence: null, + usage: { + cache_creation: null, + cache_creation_input_tokens: null, + cache_read_input_tokens: null, + input_tokens: 10, + output_tokens: 5, + service_tier: null, + iterations + } + })) + ) + )) + ) + + const response = yield* LanguageModel.generateText({ prompt: "Hello" }).pipe( + Effect.provide(AnthropicLanguageModel.model("claude-opus-5-5")), + Effect.provide(layer) + ) + const metadata = response.content.find((part) => + part.type === "response-metadata" && part.metadata.anthropic?.fallback !== undefined + ) + assert.strictEqual(metadata?.type === "response-metadata" ? metadata.modelId : undefined, "claude-opus-4-8") + assert.deepStrictEqual( + metadata?.type === "response-metadata" ? metadata.metadata.anthropic?.fallback : undefined, + fallback + ) + const finish = response.content.find((part) => part.type === "finish") + assert.deepStrictEqual( + finish?.type === "finish" ? finish.metadata.anthropic?.usage?.iterations : undefined, + iterations + ) + })) + it.effect("reports total input tokens and cache usage on the span", () => Effect.gen(function*() { const spans: Array = [] diff --git a/repos/effect/packages/ai/anthropic/tsconfig.json b/packages/ai/anthropic/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/anthropic/tsconfig.json rename to packages/ai/anthropic/tsconfig.json diff --git a/repos/effect/packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts b/packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts rename to packages/ai/anthropic/typetest/AnthropicLanguageModel.tst.ts diff --git a/packages/ai/cloudflare/CHANGELOG.md b/packages/ai/cloudflare/CHANGELOG.md new file mode 100644 index 000000000..8428dc82f --- /dev/null +++ b/packages/ai/cloudflare/CHANGELOG.md @@ -0,0 +1,41 @@ +# @effect/ai-cloudflare + +## 4.0.5 + +### Patch Changes + +- [#9121](https://github.com/Effect-TS/effect/pull/9121) [`bd2c7a3`](https://github.com/Effect-TS/effect/commit/bd2c7a3b8b94859a29b1064f87713dae0b1598f9) Thanks @jem-computer! - Support images in `CloudflareDecisionModel`. Byte arrays, base64 strings, and `data:` URLs are sent to Clef. Remote URLs fail with `AiError.InvalidUserInputError`. + +- [#9124](https://github.com/Effect-TS/effect/pull/9124) [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e) Thanks @jem-computer! - Support structured decision instructions, descriptions, and score legends in TypeSafe, Cloudflare, and OpenRouter. `Decision.rate` accepts labeled descriptions; `Rate.criteria` entries are now `{ label, description }` objects instead of strings. +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + +## 4.0.2 + +### Patch Changes + +- [#8770](https://github.com/Effect-TS/effect/pull/8770) [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa) Thanks @fubhy! - Tag every module and directly importable export with an explicit `@stability`. `@stability stable` follows strict semver, and `@stability unstable` may receive breaking changes in minor releases. +- Updated dependencies [[`26c1089`](https://github.com/Effect-TS/effect/commit/26c108919f73b92aa8889e367d3a5a9161a652f1), [`6c59de7`](https://github.com/Effect-TS/effect/commit/6c59de782b9c5b5cdb53fcde0455984bbfd9ff63), [`467d3ac`](https://github.com/Effect-TS/effect/commit/467d3aceff3e8c98ba7dd4ffef9a267a8f14d105), [`375c1b0`](https://github.com/Effect-TS/effect/commit/375c1b021c6a3d794e798112d5486821586173f3), [`45a70ce`](https://github.com/Effect-TS/effect/commit/45a70ced8ca1c4f1e6b489e9fe3e050bf3364058), [`fb757e3`](https://github.com/Effect-TS/effect/commit/fb757e3eaef9ddfb9f63e5db788d12af8209be91), [`716e358`](https://github.com/Effect-TS/effect/commit/716e358e0ed263ea26da20f834cea69b6d5b1584), [`af114e6`](https://github.com/Effect-TS/effect/commit/af114e664b1f2f3f50794b1176f3531c2070cda6), [`d0b3862`](https://github.com/Effect-TS/effect/commit/d0b3862b6525d09277b8fb48d981beb200ad09f5), [`7064da9`](https://github.com/Effect-TS/effect/commit/7064da92f2716f3e8d57e31695ad7d50c7ca2023), [`eee0eeb`](https://github.com/Effect-TS/effect/commit/eee0eeb1e40e47cfa6a4dfb0f07bb037c0f1ce02), [`b9d6581`](https://github.com/Effect-TS/effect/commit/b9d65819314d7eedca17bbafec6ae6377cab6740), [`7a721ce`](https://github.com/Effect-TS/effect/commit/7a721cee6b6b251642a6cc495c3fbd92a9ae56ff), [`a0cb4c2`](https://github.com/Effect-TS/effect/commit/a0cb4c25d86f61c8522ad24d6abf418e23342204), [`3cb395e`](https://github.com/Effect-TS/effect/commit/3cb395e15156aaa7e36f0bbfaaebe61a3966a319), [`540ecd1`](https://github.com/Effect-TS/effect/commit/540ecd1a94742aeacfb9ef39ebd0cbf7e61a6dfa), [`aefe05a`](https://github.com/Effect-TS/effect/commit/aefe05aea1d6f2fb9da0f313752a4efee5779eb3), [`c381fbc`](https://github.com/Effect-TS/effect/commit/c381fbc0451e450f7a650eaca848635b8c967302), [`c6f2179`](https://github.com/Effect-TS/effect/commit/c6f2179d3d104f934905d273668ce0a0b6871ef9), [`0aeb7fd`](https://github.com/Effect-TS/effect/commit/0aeb7fd6f5fad365f957f321721781a096fe4f1c), [`2fbecc8`](https://github.com/Effect-TS/effect/commit/2fbecc82c3817fb0a5fc12883091f343496695c4), [`7095539`](https://github.com/Effect-TS/effect/commit/7095539c2328b07bdefddb1a708338f3325baea6), [`46b75b1`](https://github.com/Effect-TS/effect/commit/46b75b1a7c8d4e1883454e5adc52ebaec992e535), [`99b6b45`](https://github.com/Effect-TS/effect/commit/99b6b45aff2d7b1a1ec65f2c282c927681cbf676), [`e4cb9f1`](https://github.com/Effect-TS/effect/commit/e4cb9f1f58e18f443d883863240ad19dca4bffde), [`2e03688`](https://github.com/Effect-TS/effect/commit/2e03688cdf32194a30cc43bdcc85700fe211a6db), [`b817ebe`](https://github.com/Effect-TS/effect/commit/b817ebefc0fc88d2bcfbef803bdf27298888df5e), [`4153144`](https://github.com/Effect-TS/effect/commit/4153144de56b785409efadae655b4889452d0e10), [`2b3c63c`](https://github.com/Effect-TS/effect/commit/2b3c63c14f89c6d82f9829e214151da12f7931f6), [`bd00773`](https://github.com/Effect-TS/effect/commit/bd00773b252970e576ffe0cce17b84c10ce3c81f), [`146e86a`](https://github.com/Effect-TS/effect/commit/146e86a6894ee2485b43db22dfe767822829ded4), [`44defad`](https://github.com/Effect-TS/effect/commit/44defad43f1f4c396fcd9373484be191506d7df8), [`22d6f4d`](https://github.com/Effect-TS/effect/commit/22d6f4dcd413d86ed81719b6cd5678cb5c88048f), [`7d8d497`](https://github.com/Effect-TS/effect/commit/7d8d4979c6c3e9448c83b1e34c21735e76e5bb2e), [`98a5ed7`](https://github.com/Effect-TS/effect/commit/98a5ed7603de413c0939c9ae66503ebe462846cf), [`d350f8e`](https://github.com/Effect-TS/effect/commit/d350f8e7ba7bb68aff2e029fbca7d7a9017fdd60), [`bd96be0`](https://github.com/Effect-TS/effect/commit/bd96be0f1c9d842e381ed437ec319c5401cbb9be), [`f6af4b6`](https://github.com/Effect-TS/effect/commit/f6af4b6d24b866c1fe665a7af5562b329160a39d), [`7234786`](https://github.com/Effect-TS/effect/commit/723478668709ccefcec406522e2fce2431c653c9), [`1f74b62`](https://github.com/Effect-TS/effect/commit/1f74b62b6c918bdea3e4416e701c490fcf6866ce), [`42763ce`](https://github.com/Effect-TS/effect/commit/42763ce0b43aa9006a7d572a56b0955477dd4e2b), [`5630677`](https://github.com/Effect-TS/effect/commit/5630677ff5df3e868f8edaf8c67504f6b7f277ad), [`a0e1b87`](https://github.com/Effect-TS/effect/commit/a0e1b871b445a7dadbae33bca3644119e1375710), [`f5716ad`](https://github.com/Effect-TS/effect/commit/f5716ad55d84dfa4aa935008ba7e336dff5bf3bb), [`26931d0`](https://github.com/Effect-TS/effect/commit/26931d0fee36119a2e8b61480c0b1137a5f55c03), [`972c80f`](https://github.com/Effect-TS/effect/commit/972c80fb023a18bd8259b62f227420b2042ed3a9), [`efebfa9`](https://github.com/Effect-TS/effect/commit/efebfa9cf9fb21ad8ac8156ecf6ca555ca55efa3), [`a3b4cf3`](https://github.com/Effect-TS/effect/commit/a3b4cf3e2b35b336d05116ec464c4883c7aaf878), [`3ed997b`](https://github.com/Effect-TS/effect/commit/3ed997b1f43cd022840013a53ec914f4cf63de12), [`7b396bd`](https://github.com/Effect-TS/effect/commit/7b396bd3b50d07457f5c55d84628f93f98215a42), [`c07c144`](https://github.com/Effect-TS/effect/commit/c07c144b1a7c17fc908cd386b186bd951614c10f), [`8903b68`](https://github.com/Effect-TS/effect/commit/8903b68addaded54f78d9cff34821665f583c40f), [`a84aa4e`](https://github.com/Effect-TS/effect/commit/a84aa4e863dd1f03cc1316e52357b689bad4bade), [`88917eb`](https://github.com/Effect-TS/effect/commit/88917ebb2e30aee2d03e24766b3e8624309b664e), [`50a5686`](https://github.com/Effect-TS/effect/commit/50a5686ab6ab0627572ed86a34288560cb9f8321), [`c419a0e`](https://github.com/Effect-TS/effect/commit/c419a0edaab68290d53fea7543e22eb1506dcf23), [`d85a309`](https://github.com/Effect-TS/effect/commit/d85a3095af16b8f69b623f0436fe57935cda86cf), [`7e7ede6`](https://github.com/Effect-TS/effect/commit/7e7ede67d838a10bf7b9324a6fcc0070cc8241d2), [`1803500`](https://github.com/Effect-TS/effect/commit/18035009c92bf4aed95c92b0d34392042aa66469), [`495740d`](https://github.com/Effect-TS/effect/commit/495740d9d0134bde99a24e0dc96fdb197002beec), [`985860d`](https://github.com/Effect-TS/effect/commit/985860df71322250ec93daf5d776dcf27f10be29), [`e36abbb`](https://github.com/Effect-TS/effect/commit/e36abbba72674cfe366747ae52639448e48da1ec), [`c6086ee`](https://github.com/Effect-TS/effect/commit/c6086ee5a2fc4176943ac5a3062000e0cb4c082c), [`0ac6ac8`](https://github.com/Effect-TS/effect/commit/0ac6ac813c26c0745b3258a50f921b5ceeb0c2c4), [`90a1bae`](https://github.com/Effect-TS/effect/commit/90a1bae8987dfcf94db7302a109d48b3a3ce098c), [`b1d200c`](https://github.com/Effect-TS/effect/commit/b1d200c40a1dad69def51ebdbf0a1a612a12b8ac), [`1ba8521`](https://github.com/Effect-TS/effect/commit/1ba85215823b3ad9fe725619ecca4c2c39f3548b), [`1b35732`](https://github.com/Effect-TS/effect/commit/1b35732952db4e5600462bab154a1cd382e4bccf), [`763b445`](https://github.com/Effect-TS/effect/commit/763b445f553d96a49045fd2163c87d9305fa6761), [`00f1cc7`](https://github.com/Effect-TS/effect/commit/00f1cc783f5e90e747ba0dad98468311b76eeb30), [`d4f4bc8`](https://github.com/Effect-TS/effect/commit/d4f4bc8b83c303c74a85e5f4b9277833b631fcd0), [`f11526a`](https://github.com/Effect-TS/effect/commit/f11526aea2c65fdd110a1ddce13c510da08a9dea), [`8781388`](https://github.com/Effect-TS/effect/commit/87813882fafcd8e7ac6231f825c139df64636d23), [`5326527`](https://github.com/Effect-TS/effect/commit/53265279182da264091a278013dfd7fc430e89a2), [`8409eb4`](https://github.com/Effect-TS/effect/commit/8409eb45925e23c10a9fb5b9bd31baaafca25d81), [`c5cf8ce`](https://github.com/Effect-TS/effect/commit/c5cf8ce40217e84bcd0f53c0125d9e3213e45a4e), [`684ccdc`](https://github.com/Effect-TS/effect/commit/684ccdc20b7803f3c3be054f336fbf8c1ed78304), [`33303ea`](https://github.com/Effect-TS/effect/commit/33303ea6347d9db747d57f0b3cfa400b0c9225d1), [`b79fcb8`](https://github.com/Effect-TS/effect/commit/b79fcb8b628d4800a6dcadcad76befa4d21b9562), [`888a189`](https://github.com/Effect-TS/effect/commit/888a18915cabb00b9d81a118569f3f7c4d911dfb), [`9b8200d`](https://github.com/Effect-TS/effect/commit/9b8200da0fc8d60ece93f3c9f60f584436dcf43e), [`82303c1`](https://github.com/Effect-TS/effect/commit/82303c1bc81e954cf518173ee264efa027aff93f), [`76fcd89`](https://github.com/Effect-TS/effect/commit/76fcd898c57a633ab993af1326e57d7f2ece7ca6), [`f31e6b7`](https://github.com/Effect-TS/effect/commit/f31e6b7fd2d85baaed21ec38e9ebf308f6b18a15), [`332d363`](https://github.com/Effect-TS/effect/commit/332d363f07ad1d83ef533f9e73688572b4777d75), [`a6c7dcb`](https://github.com/Effect-TS/effect/commit/a6c7dcb042893ef654ee3ff10950d4958b7b1cc3)]: + - effect@4.0.2 + +## 4.0.1 + +### Patch Changes + +- [#8677](https://github.com/Effect-TS/effect/pull/8677) [`480bba2`](https://github.com/Effect-TS/effect/commit/480bba2dfebba5b0e58695a9837895dfb65895ee) Thanks @danieljvdm! - Add a Cloudflare Workers AI decision provider for Clef and Clef-flash. +- Updated dependencies [[`ed2cc2f`](https://github.com/Effect-TS/effect/commit/ed2cc2f322dfd24da550c8d0ac811c3130b79d74), [`58a2ee8`](https://github.com/Effect-TS/effect/commit/58a2ee8e0db8e0a90bae83c6cceeff6916772fee), [`bc44526`](https://github.com/Effect-TS/effect/commit/bc445262eeaaba6f7f28a154023a590e193e547d), [`073bb47`](https://github.com/Effect-TS/effect/commit/073bb475d1e84e09cdcd8303a4a558f2a73d8396), [`2ec021e`](https://github.com/Effect-TS/effect/commit/2ec021eff7fbd2206adc3e8bf4e803ed4bf07d6a), [`cad1118`](https://github.com/Effect-TS/effect/commit/cad111867a666cebe8ff9bf2237a997fa6e4cb14), [`1672d9c`](https://github.com/Effect-TS/effect/commit/1672d9ca8da3e5b536bf7afb73365cccc6c01f53), [`a81e4e6`](https://github.com/Effect-TS/effect/commit/a81e4e6cb3dfbf01b1c53c1c877fddeaced8a278), [`f5c2a91`](https://github.com/Effect-TS/effect/commit/f5c2a91621401a018b770446d35ef99806c5561f), [`498e72b`](https://github.com/Effect-TS/effect/commit/498e72ba14d0191d6bf66cc0e931c229a1ff442f), [`cabf123`](https://github.com/Effect-TS/effect/commit/cabf1230723e92684a4b0b836ccfde25c657f9f1), [`a5b1d4e`](https://github.com/Effect-TS/effect/commit/a5b1d4e4cae01dbe30890d3796f846bda00ad2ae), [`878cb33`](https://github.com/Effect-TS/effect/commit/878cb33f4f5f5a31d2c80a9ddb7d62a611230e1c), [`5e6f756`](https://github.com/Effect-TS/effect/commit/5e6f7568a4c895eab3087249b191c5dcba5e73ef), [`18dfb98`](https://github.com/Effect-TS/effect/commit/18dfb98cf6d2aeedbab8ef5734f9bc273a82d56e), [`04706ef`](https://github.com/Effect-TS/effect/commit/04706eff5da94b4d475b99eab8b0256bed97b080), [`f157482`](https://github.com/Effect-TS/effect/commit/f15748273a89afff4b05d5e6bec22c567c2a3cda), [`b5a2d4c`](https://github.com/Effect-TS/effect/commit/b5a2d4c1d62c9620a68d72b7f20248c69ef7663b), [`1c18c68`](https://github.com/Effect-TS/effect/commit/1c18c688df10b697c10283160dba143e049f3d50), [`43416c4`](https://github.com/Effect-TS/effect/commit/43416c4142f973f4988440716eb54a498659cd3a), [`d6ac8f9`](https://github.com/Effect-TS/effect/commit/d6ac8f923a33ff298a87f31230735e71d2d082e9), [`1d9fb8c`](https://github.com/Effect-TS/effect/commit/1d9fb8c540a11326650baa0866a17951665189e7), [`20dcd50`](https://github.com/Effect-TS/effect/commit/20dcd5060581077b46325a433f787ba10a56963d), [`42e5750`](https://github.com/Effect-TS/effect/commit/42e5750f7e2fbf349cfe45452d7cc9dce98d6fe9), [`888e326`](https://github.com/Effect-TS/effect/commit/888e326a3180aea9b117fe7c97aace5dd527596e), [`0b52bb4`](https://github.com/Effect-TS/effect/commit/0b52bb42fac5aecb8bcb8d899293c641b62c3f66), [`e823e69`](https://github.com/Effect-TS/effect/commit/e823e693773aa163c0081af00a5236118446c6ae), [`962647b`](https://github.com/Effect-TS/effect/commit/962647b6a16000f882e6999f001c513a8cccc711), [`43fdcdc`](https://github.com/Effect-TS/effect/commit/43fdcdc6c81b6ad22d54061d6c6c45d9243d7c33), [`1f89b8e`](https://github.com/Effect-TS/effect/commit/1f89b8ec64e6362da3eaaff8cbb235df88d3441c), [`e1a521a`](https://github.com/Effect-TS/effect/commit/e1a521a6886a281abee016ff9aa57c3b172057c8)]: + - effect@4.0.1 diff --git a/repos/effect/packages/ai/cloudflare/README.md b/packages/ai/cloudflare/README.md similarity index 69% rename from repos/effect/packages/ai/cloudflare/README.md rename to packages/ai/cloudflare/README.md index ed3b8a026..35a9d0f27 100644 --- a/repos/effect/packages/ai/cloudflare/README.md +++ b/packages/ai/cloudflare/README.md @@ -37,8 +37,23 @@ const program = DecisionModel.decide(Triage, { }).pipe(Effect.provide(Clef)) ``` +Pass `images` to include pictures with the input. Clef shows them to the model before the state: + +```ts +declare const screenshotBytes: Uint8Array + +const withScreenshot = DecisionModel.decide(Triage, { + input: "Customers report this checkout error.", + images: [{ mediaType: "image/png", data: screenshotBytes }] +}) +``` + +Image `data` may be a `Uint8Array`, a base64 string, or a `data:` URL. Remote URLs fail with `AiError.InvalidUserInputError` because Clef does not fetch them, so download the image first. The API accepts up to 4 PNG, JPEG, or WebP images, each at most 4 MiB and 16 megapixels, with 8 MiB of decoded images in total and a 13 MiB request body. + Use `"clef"` for the larger model. `CloudflareDecisionModel.model` provides a model descriptor with Cloudflare provider metadata. `CloudflareClient.layer` accepts an explicit account ID and redacted API token; `apiUrl` and `transformClient` customize HTTP access. +Instructions and descriptions can be strings, JSON objects, or JSON arrays. A rating level with a structured description takes an explicit `label`, which answers use. + The client unwraps Cloudflare's REST envelope and maps failures to `AiError`. It does not retry automatically. Rate-limit errors include `Retry-After` when provided. The decision model validates answers and rescales small probability drift from four-decimal rounding. Cloudflare limits each request to 64 questions, classification to 2–255 choices, and ratings to 2–10 levels. See the [Clef API documentation](https://developers.cloudflare.com/workers-ai/models/clef/) for request limits. diff --git a/repos/effect/packages/ai/cloudflare/package.json b/packages/ai/cloudflare/package.json similarity index 98% rename from repos/effect/packages/ai/cloudflare/package.json rename to packages/ai/cloudflare/package.json index b7e707957..d8c9a520a 100644 --- a/repos/effect/packages/ai/cloudflare/package.json +++ b/packages/ai/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-cloudflare", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "A Cloudflare Workers AI decision provider for Effect", diff --git a/repos/effect/packages/ai/cloudflare/src/CloudflareClient.ts b/packages/ai/cloudflare/src/CloudflareClient.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/CloudflareClient.ts rename to packages/ai/cloudflare/src/CloudflareClient.ts diff --git a/repos/effect/packages/ai/cloudflare/src/CloudflareDecisionModel.ts b/packages/ai/cloudflare/src/CloudflareDecisionModel.ts similarity index 69% rename from repos/effect/packages/ai/cloudflare/src/CloudflareDecisionModel.ts rename to packages/ai/cloudflare/src/CloudflareDecisionModel.ts index 769875dbb..7b1e28e04 100644 --- a/repos/effect/packages/ai/cloudflare/src/CloudflareDecisionModel.ts +++ b/packages/ai/cloudflare/src/CloudflareDecisionModel.ts @@ -4,9 +4,11 @@ * @stability unstable * @since 4.0.0 */ +import * as AiError from "effect/ai/AiError" import * as DecisionModel from "effect/ai/DecisionModel" import * as AiModel from "effect/ai/Model" import * as Effect from "effect/Effect" +import * as Base64 from "effect/encoding/Base64" import * as Layer from "effect/Layer" import { CloudflareClient } from "./CloudflareClient.ts" import type * as CloudflareSchema from "./CloudflareSchema.ts" @@ -35,6 +37,16 @@ export const model = ( /** * Builds a decision service with tolerance for four-decimal probability rounding. * + * **Details** + * + * Byte arrays and base64 strings are sent as base64 with their media type. + * `data:` URLs are sent unchanged. The API enforces image limits. + * + * **Gotchas** + * + * - Remote URLs fail with `AiError.InvalidUserInputError` because Clef does + * not fetch them. + * * @stability unstable * @category constructors * @since 4.0.0 @@ -46,7 +58,8 @@ export const make = Effect.fnUntraced( const client = yield* CloudflareClient return yield* DecisionModel.make({ probabilityPrecision: 4, - decide: Effect.fnUntraced(function*({ state, decisions }) { + supportsImages: true, + decide: Effect.fnUntraced(function*({ state, decisions, images }) { const questions: Record = Object.create(null) for (const [key, decision] of Object.entries(decisions)) { switch (decision._tag) { @@ -54,14 +67,32 @@ export const make = Effect.fnUntraced( questions[key] = { type: "choice", instructions: decision.instructions, criteria: decision.criteria } break case "Rate": - questions[key] = { type: "score", instructions: decision.instructions, criteria: decision.criteria } + questions[key] = { + type: "score", + instructions: decision.instructions, + criteria: decision.criteria.map((level) => level.description) + } break case "Probability": questions[key] = { type: "noul", instructions: decision.instructions, criteria: decision.criteria } break } } - const response = yield* client.createDecisions({ model: options.model, state, questions }) + if (images?.some((image) => image.data instanceof URL && image.data.protocol !== "data:")) { + return yield* Effect.fail(AiError.make({ + module: "CloudflareDecisionModel", + method: "decide", + reason: new AiError.InvalidUserInputError({ + description: "Cloudflare Clef accepts base64 or data URL images, not remote URLs" + }) + })) + } + const response = yield* client.createDecisions({ + model: options.model, + state, + questions, + ...(images === undefined ? undefined : { images: images.map(encodeImage) }) + }) const answers: Record = Object.create(null) for (const [key, decision] of Object.entries(decisions)) { const answer = response.answers[key] @@ -79,7 +110,7 @@ export const make = Effect.fnUntraced( const probabilities: Record = Object.create(null) for (let index = 0; index < levels.length; index++) { const probability = answer.probabilities[String(index)] - if (probability !== undefined) probabilities[levels[index]] = probability + if (probability !== undefined) probabilities[levels[index].label] = probability } answers[key] = { _tag: "Rate", rating: answer.score, probabilities, confidence: answer.confidence } break @@ -98,6 +129,12 @@ export const make = Effect.fnUntraced( } ) +const encodeImage = (image: DecisionModel.Image): typeof CloudflareSchema.Image.Encoded => + image.data instanceof URL ? image.data.href : { + content_type: image.mediaType, + base64: typeof image.data === "string" ? image.data : Base64.encode(image.data) + } + /** * Provides DecisionModel using an existing CloudflareClient. * diff --git a/repos/effect/packages/ai/cloudflare/src/CloudflareSchema.ts b/packages/ai/cloudflare/src/CloudflareSchema.ts similarity index 62% rename from repos/effect/packages/ai/cloudflare/src/CloudflareSchema.ts rename to packages/ai/cloudflare/src/CloudflareSchema.ts index 3ae972da0..d712ddf80 100644 --- a/repos/effect/packages/ai/cloudflare/src/CloudflareSchema.ts +++ b/packages/ai/cloudflare/src/CloudflareSchema.ts @@ -6,6 +6,8 @@ */ import * as Schema from "effect/Schema" +const Entry = Schema.Union([Schema.String, Schema.JsonObject, Schema.Array(Schema.Json)]) + /** * A classification question. * @@ -15,8 +17,8 @@ import * as Schema from "effect/Schema" */ export const ChoiceQuestion = Schema.Struct({ type: Schema.Literal("choice"), - instructions: Schema.String, - criteria: Schema.Record(Schema.String, Schema.String) + instructions: Entry, + criteria: Schema.Record(Schema.String, Schema.NullOr(Entry)) }) /** @@ -28,8 +30,8 @@ export const ChoiceQuestion = Schema.Struct({ */ export const ScoreQuestion = Schema.Struct({ type: Schema.Literal("score"), - instructions: Schema.String, - criteria: Schema.Array(Schema.String) + instructions: Entry, + criteria: Schema.Array(Schema.Json) }) /** @@ -41,12 +43,13 @@ export const ScoreQuestion = Schema.Struct({ */ export const NoulQuestion = Schema.Struct({ type: Schema.Literal("noul"), - instructions: Schema.String, - criteria: Schema.optional(Schema.Struct({ false: Schema.String, true: Schema.String })) + instructions: Entry, + criteria: Schema.optional(Schema.Struct({ false: Schema.Json, true: Schema.Json })) }) /** - * A Clef classification, rating, or probability question. + * A Clef question with string, JSON object, or JSON array instructions. + * Choice descriptions also accept `null`; Score and Noul descriptions accept any JSON value. * * @stability unstable * @category schemas @@ -55,7 +58,32 @@ export const NoulQuestion = Schema.Struct({ export const Question = Schema.Union([ChoiceQuestion, ScoreQuestion, NoulQuestion]) /** - * A batch of questions about text or JSON state. + * An image as a `data:` URL or as base64 data with its media type. Clef + * rejects remote URLs. + * + * @stability unstable + * @category schemas + * @since 4.0.5 + */ +export const Image = Schema.Union([ + Schema.String, + Schema.Struct({ + content_type: Schema.Union([ + Schema.Literals(["image/png", "image/jpeg", "image/webp"]), + Schema.StringForLiteralAutocomplete + ]), + base64: Schema.String + }) +]) + +/** + * A batch of questions about text or JSON state, with optional images shown + * to the model before the state. + * + * **Details** + * + * Clef accepts up to 4 images of at most 4 MiB and 16 megapixels each, 8 MiB + * of decoded images in total, and 13 MiB per request body. * * @stability unstable * @category schemas @@ -64,7 +92,8 @@ export const Question = Schema.Union([ChoiceQuestion, ScoreQuestion, NoulQuestio export const DecisionsRequest = Schema.Struct({ model: Schema.Union([Schema.Literals(["clef", "clef-flash"]), Schema.StringForLiteralAutocomplete]), state: Schema.Json, - questions: Schema.Record(Schema.String, Question) + questions: Schema.Record(Schema.String, Question), + images: Schema.optional(Schema.Array(Image)) }) /** @@ -82,7 +111,7 @@ export const ChoiceAnswer = Schema.Struct({ }) /** - * A zero-based rating and index-keyed distribution. + * A zero-based rating and index-keyed distribution. `legend` maps indices to descriptions. * * @stability unstable * @category schemas @@ -93,7 +122,7 @@ export const ScoreAnswer = Schema.Struct({ score: Schema.Number, probabilities: Schema.Record(Schema.String, Schema.Number), confidence: Schema.Number, - legend: Schema.Record(Schema.String, Schema.String) + legend: Schema.Record(Schema.String, Schema.Json) }) /** diff --git a/repos/effect/packages/ai/cloudflare/src/index.ts b/packages/ai/cloudflare/src/index.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/index.ts rename to packages/ai/cloudflare/src/index.ts diff --git a/repos/effect/packages/ai/cloudflare/src/internal/errors.ts b/packages/ai/cloudflare/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/src/internal/errors.ts rename to packages/ai/cloudflare/src/internal/errors.ts diff --git a/repos/effect/packages/ai/cloudflare/test/CloudflareClient.test.ts b/packages/ai/cloudflare/test/CloudflareClient.test.ts similarity index 100% rename from repos/effect/packages/ai/cloudflare/test/CloudflareClient.test.ts rename to packages/ai/cloudflare/test/CloudflareClient.test.ts diff --git a/repos/effect/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts b/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts similarity index 68% rename from repos/effect/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts rename to packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts index ff4c27d79..ce641e22c 100644 --- a/repos/effect/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts +++ b/packages/ai/cloudflare/test/CloudflareDecisionModel.test.ts @@ -72,6 +72,16 @@ const clientLayer = ( const layer = (answers: unknown) => CloudflareDecisionModel.layer({ model: "clef" }).pipe(Layer.provide(clientLayer(answers))) +const Structured = Decision.make({ + input: Schema.String, + decisions: { + severity: Decision.rate({ + instructions: ["How severe?"], + criteria: ["Minor", { label: "Critical", description: { impact: "outage" } }] + }) + } +}) + describe("CloudflareDecisionModel", () => { it.effect("sends every decision as a question and maps the answers", () => Effect.gen(function*() { @@ -104,7 +114,7 @@ describe("CloudflareDecisionModel", () => { severity: { type: "score", instructions: Triage.decisions.severity.instructions, - criteria: Triage.decisions.severity.criteria + criteria: ["Minor", "Major", "Critical"] }, urgent: { type: "noul", @@ -172,4 +182,73 @@ describe("CloudflareDecisionModel", () => { assert.strictEqual(error.reason._tag, "InvalidOutputError") }).pipe(Effect.provide(layer(invalid)))) } + + it.effect("sends byte arrays, base64 strings, and data URLs as images", () => { + const dataUrl = "data:image/webp;base64,UklGRg==" + return Effect.gen(function*() { + const result = yield* DecisionModel.decide(Triage, { + input, + images: [ + { mediaType: "image/png", data: new Uint8Array([137, 80, 78, 71]) }, + { mediaType: "image/jpeg", data: "/9j/4AAQ" }, + { mediaType: "image/webp", data: new URL(dataUrl) } + ] + }) + assert.strictEqual(result.answers.urgent.probability, 0.99) + }).pipe( + Effect.provide(CloudflareDecisionModel.layer({ model: "clef" })), + Effect.provide(clientLayer(answers, (request) => { + assert.strictEqual(request.body._tag, "Uint8Array") + if (request.body._tag === "Uint8Array") { + assert.deepStrictEqual(JSON.parse(new TextDecoder().decode(request.body.body)).images, [ + { content_type: "image/png", base64: "iVBORw==" }, + { content_type: "image/jpeg", base64: "/9j/4AAQ" }, + dataUrl + ]) + } + })) + ) + }) + + it.effect("rejects remote image URLs without sending a request", () => { + let requests = 0 + return Effect.gen(function*() { + const error = yield* Effect.flip(DecisionModel.decide(Triage, { + input, + images: [{ mediaType: "image/png", data: new URL("https://example.com/cat.png") }] + })) + assert.strictEqual(error.reason._tag, "InvalidUserInputError") + assert.strictEqual(requests, 0) + }).pipe( + Effect.provide(CloudflareDecisionModel.layer({ model: "clef" })), + Effect.provide(clientLayer(answers, () => { + requests++ + })) + ) + }) + + it.effect("sends structured descriptions and maps score indices to labels", () => + Effect.gen(function*() { + const result = yield* DecisionModel.decide(Structured, { input: "Checkout is down" }) + assert.strictEqual(result.answers.severity.label, "Critical") + assert.deepStrictEqual({ ...result.answers.severity.probabilities }, { Minor: 0.1, Critical: 0.9 }) + }).pipe( + Effect.provide(CloudflareDecisionModel.layer({ model: "clef" })), + Effect.provide(clientLayer({ + severity: { + type: "score", + score: 0.9, + probabilities: { "0": 0.1, "1": 0.9 }, + confidence: 0.9, + legend: { "0": "Minor", "1": { impact: "outage" } } + } + }, (request) => { + assert.strictEqual(request.body._tag, "Uint8Array") + if (request.body._tag === "Uint8Array") { + assert.deepStrictEqual(JSON.parse(new TextDecoder().decode(request.body.body)).questions, { + severity: { type: "score", instructions: ["How severe?"], criteria: ["Minor", { impact: "outage" }] } + }) + } + })) + )) }) diff --git a/repos/effect/packages/ai/cloudflare/tsconfig.json b/packages/ai/cloudflare/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/cloudflare/tsconfig.json rename to packages/ai/cloudflare/tsconfig.json diff --git a/repos/effect/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts b/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts similarity index 59% rename from repos/effect/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts rename to packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts index 880636e3e..76ea7d0f7 100644 --- a/repos/effect/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts +++ b/packages/ai/cloudflare/typetest/CloudflareDecisionModel.tst.ts @@ -18,3 +18,27 @@ describe("Cloudflare model identifiers", () => { expect(CloudflareDecisionModel.model).type.toBeCallableWith("clef-flash") }) }) + +describe("Cloudflare images", () => { + it("accepts data URLs and base64 images with any media type", () => { + expect(client.createDecisions).type.toBeCallableWith({ + model, + state: "Checkout is down", + questions: {}, + images: [ + "data:image/png;base64,iVBORw==", + { content_type: "image/png", base64: "iVBORw==" }, + { content_type: model, base64: "iVBORw==" } + ] + }) + }) + + it("rejects images without base64 data", () => { + expect(client.createDecisions).type.not.toBeCallableWith({ + model, + state: "Checkout is down", + questions: {}, + images: [{ content_type: "image/png" }] + }) + }) +}) diff --git a/repos/effect/packages/ai/openai-compat/CHANGELOG.md b/packages/ai/openai-compat/CHANGELOG.md similarity index 92% rename from repos/effect/packages/ai/openai-compat/CHANGELOG.md rename to packages/ai/openai-compat/CHANGELOG.md index d588c5c9a..915ba4c40 100644 --- a/repos/effect/packages/ai/openai-compat/CHANGELOG.md +++ b/packages/ai/openai-compat/CHANGELOG.md @@ -1,5 +1,43 @@ # @effect/ai-openai-compat +## 4.0.5 + +### Patch Changes + +- [#9168](https://github.com/Effect-TS/effect/pull/9168) [`a0ad8d3`](https://github.com/Effect-TS/effect/commit/a0ad8d3a4fce6b0db1315b2787aa6dd64d227d3d) Thanks @danieljvdm! - Emit each streamed chat completion event's parts as one chunk instead of creating an inner stream per event. + +- [#9140](https://github.com/Effect-TS/effect/pull/9140) [`91f4c74`](https://github.com/Effect-TS/effect/commit/91f4c74fe8f41a9897927e9bc2c4b814924c85e0) Thanks @tim-smart! - Use `"openai-compat"` instead of `"openai"` as the provider key for error metadata, prompt part options, and response part metadata, so `@effect/ai-openai-compat` no longer shares module augmentation keys with `@effect/ai-openai`. + +- [#9167](https://github.com/Effect-TS/effect/pull/9167) [`f9f4335`](https://github.com/Effect-TS/effect/commit/f9f43352d6b718623fdc0f29e2297785a8d6a073) Thanks @danieljvdm! - Avoid creating a child stream for each SSE event when streaming chat completions. + +- [#9144](https://github.com/Effect-TS/effect/pull/9144) [`738bc87`](https://github.com/Effect-TS/effect/commit/738bc873a779179a9c9f0e675b51b2f074796598) Thanks @danieljvdm! - Avoid intermediate content allocations for single text-part user messages. +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- [#9008](https://github.com/Effect-TS/effect/pull/9008) [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d) Thanks @effect-bot! - Add `toolCallValidation: "deferred"` for callers using `disableToolCallResolution: true`. Model-generated tool calls return with `unknown` parameters, including invalid parameters and undeclared tool names, even without a toolkit. Supported by the Anthropic, OpenAI, and OpenAI-compatible providers. Strict validation remains the default; provider-executed calls remain validated. + +- [#9011](https://github.com/Effect-TS/effect/pull/9011) [`844a77b`](https://github.com/Effect-TS/effect/commit/844a77b18c03b4faa386cf04bf94908d3d37abc7) Thanks @tim-smart! - Replay assistant text, reasoning, and tool calls in Chat Completions requests even when their parts carry item IDs. Remove unused Responses API include bookkeeping and the unsupported `store` and `conversation` configuration options. Neither option is sent to the provider. + +- [#9005](https://github.com/Effect-TS/effect/pull/9005) [`42aceb9`](https://github.com/Effect-TS/effect/commit/42aceb966a558e5c3edd3ece857ef2f1ef738384) Thanks @effect-bot! - Replay assistant reasoning alongside text and tool calls in a single chat message, preserving the provider’s reasoning field (defaulting to `reasoning_content`). + +- [#9054](https://github.com/Effect-TS/effect/pull/9054) [`8aee3b9`](https://github.com/Effect-TS/effect/commit/8aee3b9caa796aa79316c82c7c83f98c89dc49f5) Thanks @danieljvdm! - Convert each tool's parameter schema once per request, instead of again for every returned tool call. +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- [#8916](https://github.com/Effect-TS/effect/pull/8916) [`950ea4e`](https://github.com/Effect-TS/effect/commit/950ea4eeb22785f437112976f399726814549ff3) Thanks @edizferit! - Preserve tool call `extra_content` from OpenAI-compatible providers, so Gemini receives its thought signatures on follow-up tool turns. It is exposed as `metadata.openai.extraContent` on tool call response parts and sent back from the `openai.extraContent` tool call prompt option. + +- [#8952](https://github.com/Effect-TS/effect/pull/8952) [`b63a4f9`](https://github.com/Effect-TS/effect/commit/b63a4f9ac75a277d53eb43eed5dfb0f793a96f6e) Thanks @jayhemnani9910! - Ignore empty `content` deltas in streamed chat completions. Providers such as Ollama send `content: ""` with every reasoning chunk, which split a single reasoning block into one part per token and opened the text part before the reasoning finished. +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/ai/openai-compat/README.md b/packages/ai/openai-compat/README.md similarity index 100% rename from repos/effect/packages/ai/openai-compat/README.md rename to packages/ai/openai-compat/README.md diff --git a/repos/effect/packages/ai/openai-compat/package.json b/packages/ai/openai-compat/package.json similarity index 98% rename from repos/effect/packages/ai/openai-compat/package.json rename to packages/ai/openai-compat/package.json index 361b19cb8..c908e4b93 100644 --- a/repos/effect/packages/ai/openai-compat/package.json +++ b/packages/ai/openai-compat/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openai-compat", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "An OpenAI compat integration for Effect", diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts b/packages/ai/openai-compat/src/OpenAiClient.ts similarity index 98% rename from repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts rename to packages/ai/openai-compat/src/OpenAiClient.ts index 8551f926e..1c535b6da 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts +++ b/packages/ai/openai-compat/src/OpenAiClient.ts @@ -21,6 +21,7 @@ import * as HttpClientRequest from "effect/http/HttpClientRequest" import * as HttpClientResponse from "effect/http/HttpClientResponse" import * as Layer from "effect/Layer" import * as Redacted from "effect/Redacted" +import * as Result from "effect/Result" import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import * as Errors from "./internal/errors.ts" @@ -216,9 +217,9 @@ export const make = Effect.fnUntraced( const stream = response.stream.pipe( Stream.decodeText(), Stream.pipeThroughChannel(Sse.decode()), - Stream.flatMap((event) => { + Stream.filterMap((event) => { const data = decodeChatCompletionSseData(event.data) - return Stream.fromIterable(data !== undefined ? [data] : []) + return data !== undefined ? Result.succeed(data) : Result.failVoid }), Stream.takeUntil((event) => event === "[DONE]"), Stream.catchTags({ @@ -531,6 +532,7 @@ type FunctionCall = { readonly call_id: string readonly name: string readonly arguments: string + readonly extra_content?: JsonObject | undefined readonly status?: MessageStatus | undefined } @@ -978,6 +980,7 @@ export type ChatCompletionRequestToolCall = { readonly name: string readonly arguments: string } + readonly extra_content?: JsonObject | undefined } /** * Message shapes accepted by the chat completions endpoint. @@ -991,6 +994,15 @@ export type ChatCompletionRequestMessage = readonly role: "system" | "developer" | "user" | "assistant" readonly content: string | ReadonlyArray | null readonly tool_calls?: ReadonlyArray | undefined + /** + * Assistant reasoning replayed for providers that read it from + * `reasoning_content`. + */ + readonly reasoning_content?: string | undefined + /** + * Assistant reasoning replayed for providers that read it from `reasoning`. + */ + readonly reasoning?: string | undefined } | { readonly role: "tool" @@ -1135,6 +1147,7 @@ const ChatCompletionToolCall = Schema.Struct({ id: Schema.optionalKey(Schema.String), index: Schema.optionalKey(Schema.Int), type: Schema.optionalKey(Schema.String), + extra_content: Schema.optionalKey(Schema.NullOr(Schema.JsonObject)), function: Schema.optionalKey(ChatCompletionToolFunction) }) @@ -1143,6 +1156,7 @@ const ChatCompletionToolCallDelta = Schema.Struct({ id: Schema.optionalKey(Schema.NullOr(Schema.String)), index: Schema.optionalKey(Schema.Int), type: Schema.optionalKey(Schema.String), + extra_content: Schema.optionalKey(Schema.NullOr(Schema.JsonObject)), function: Schema.optionalKey(ChatCompletionToolFunctionDelta) }) diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts b/packages/ai/openai-compat/src/OpenAiConfig.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts rename to packages/ai/openai-compat/src/OpenAiConfig.ts diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts b/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts rename to packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts b/packages/ai/openai-compat/src/OpenAiError.ts similarity index 88% rename from repos/effect/packages/ai/openai-compat/src/OpenAiError.ts rename to packages/ai/openai-compat/src/OpenAiError.ts index ad317c58e..074c66765 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts +++ b/packages/ai/openai-compat/src/OpenAiError.ts @@ -77,7 +77,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface RateLimitErrorMetadata { - readonly openai?: OpenAiRateLimitMetadata | null + readonly "openai-compat"?: OpenAiRateLimitMetadata | null } /** @@ -88,7 +88,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface QuotaExhaustedErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -99,7 +99,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface AuthenticationErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -110,7 +110,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface ContentPolicyErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -121,7 +121,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface InvalidRequestErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -132,7 +132,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface InternalProviderErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -143,7 +143,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface InvalidOutputErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -154,7 +154,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface StructuredOutputErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -165,7 +165,7 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface UnsupportedSchemaErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } /** @@ -176,6 +176,6 @@ declare module "effect/ai/AiError" { * @since 4.0.0 */ export interface UnknownErrorMetadata { - readonly openai?: OpenAiErrorMetadata | null + readonly "openai-compat"?: OpenAiErrorMetadata | null } } diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts b/packages/ai/openai-compat/src/OpenAiLanguageModel.ts similarity index 78% rename from repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts rename to packages/ai/openai-compat/src/OpenAiLanguageModel.ts index b8ba73d30..2f73db229 100644 --- a/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts +++ b/packages/ai/openai-compat/src/OpenAiLanguageModel.ts @@ -16,6 +16,7 @@ import { toCodecOpenAI } from "effect/ai/OpenAiStructuredOutput" import type * as Prompt from "effect/ai/Prompt" import type * as Response from "effect/ai/Response" import * as Tool from "effect/ai/Tool" +import * as Arr from "effect/Array" import * as Context from "effect/Context" import * as DateTime from "effect/DateTime" import * as Effect from "effect/Effect" @@ -32,23 +33,20 @@ import * as Schema from "effect/Schema" import * as AST from "effect/SchemaAST" import * as Stream from "effect/Stream" import type { Span } from "effect/Tracer" -import type { DeepMutable, Simplify } from "effect/Types" +import type { Simplify } from "effect/Types" import * as InternalUtilities from "./internal/utilities.ts" import { type Annotation, type ChatCompletionContentPart, + type ChatCompletionRequestMessage, type ChatCompletionRequestToolCall, type CreateResponse, type CreateResponse200, type CreateResponse200Sse, type CreateResponseRequestJson, - type IncludeEnum, type InputContent, - type InputItem, type MessageStatus, OpenAiClient, - type ReasoningItem, - type SummaryTextContent, type TextResponseFormatConfiguration, type Tool as OpenAiClientTool, type UnknownChatCompletionEvent @@ -66,7 +64,7 @@ type ImageDetail = "auto" | "low" | "high" type ConfigOptions = Simplify< & Partial< - Omit + Omit > & { /** @@ -137,7 +135,7 @@ declare module "effect/ai/Prompt" { /** * Provider-specific file options for OpenAI-compatible APIs. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`. */ @@ -156,7 +154,7 @@ declare module "effect/ai/Prompt" { /** * Provider-specific reasoning options for OpenAI-compatible APIs. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The ID of the item to reference. */ @@ -167,6 +165,12 @@ declare module "effect/ai/Prompt" { * parameter. */ readonly encryptedContent?: string | null + /** + * The chat message field the reasoning was returned under. Replayed + * reasoning is sent back under the same field, defaulting to + * `reasoning_content`. + */ + readonly reasoningField?: "reasoning" | "reasoning_content" | null } | null } @@ -181,7 +185,7 @@ declare module "effect/ai/Prompt" { /** * Provider-specific tool-call options for OpenAI-compatible APIs. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The ID of the item to reference. */ @@ -190,6 +194,10 @@ declare module "effect/ai/Prompt" { * The status to send for the tool-call item. */ readonly status?: MessageStatus | null + /** + * Provider-specific extra content to echo back with the tool call. + */ + readonly extraContent?: Schema.JsonObject | null } | null } @@ -204,7 +212,7 @@ declare module "effect/ai/Prompt" { /** * Provider-specific tool-result options for OpenAI-compatible APIs. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The ID of the item to reference. */ @@ -227,7 +235,7 @@ declare module "effect/ai/Prompt" { /** * Provider-specific text options for OpenAI-compatible APIs. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The ID of the item to reference. */ @@ -256,7 +264,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the text part. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the text part. */ @@ -289,7 +297,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the streamed text start. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the streamed text part. */ @@ -308,7 +316,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the streamed text end. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the streamed text part. */ @@ -331,7 +339,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the reasoning part. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the reasoning part. */ @@ -340,6 +348,10 @@ declare module "effect/ai/Response" { * Encrypted reasoning content that can be sent back in later requests. */ readonly encryptedContent?: string | null + /** + * The chat message field the provider returned the reasoning under. + */ + readonly reasoningField?: "reasoning" | "reasoning_content" | null } | null } @@ -354,7 +366,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the streamed reasoning start. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the reasoning part. */ @@ -363,6 +375,10 @@ declare module "effect/ai/Response" { * Encrypted reasoning content that can be sent back in later requests. */ readonly encryptedContent?: string | null + /** + * The chat message field the provider returned the reasoning under. + */ + readonly reasoningField?: "reasoning" | "reasoning_content" | null } | null } @@ -377,7 +393,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the streamed reasoning delta. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the reasoning part. */ @@ -396,7 +412,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the streamed reasoning end. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the reasoning part. */ @@ -419,11 +435,15 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned for the tool call. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The OpenAI item ID associated with the tool call. */ readonly itemId?: string | null + /** + * Provider-specific extra content returned with the tool call. + */ + readonly extraContent?: Schema.JsonObject | null } | null } @@ -438,7 +458,7 @@ declare module "effect/ai/Response" { /** * Provider-specific citation metadata for OpenAI-compatible APIs. */ - readonly openai?: + readonly "openai-compat"?: | { /** * Identifies a citation to an uploaded file. @@ -495,7 +515,7 @@ declare module "effect/ai/Response" { /** * Provider-specific URL citation metadata for OpenAI-compatible APIs. */ - readonly openai?: { + readonly "openai-compat"?: { /** * Identifies a citation to a URL. */ @@ -522,7 +542,7 @@ declare module "effect/ai/Response" { /** * Provider-specific metadata returned when generation finishes. */ - readonly openai?: { + readonly "openai-compat"?: { /** * The service tier reported by the OpenAI-compatible provider. */ @@ -604,17 +624,16 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig readonly config: typeof Config.Service readonly options: LanguageModel.ProviderOptions readonly toolNameMapper: Tool.NameMapper - }): Effect.fn.Return { - const include = new Set() - const capabilities = getModelCapabilities(config.model!) + }): Effect.fn.Return<{ + readonly request: CreateResponseRequestJson + readonly toolCodecs: ToolCodecs + }, AiError.AiError> { const messages = yield* prepareMessages({ config, options, - capabilities, - include, toolNameMapper }) - const { toolChoice, tools } = yield* prepareTools({ + const { toolChoice, toolCodecs, tools } = yield* prepareTools({ config, options, toolNameMapper @@ -626,8 +645,6 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig const { fileIdPrefixes: _fip, strictJsonSchema: _sjs, ...apiConfig } = config const request: CreateResponse = { ...apiConfig, - input: messages, - include: include.size > 0 ? Array.from(include) : null, text: { verbosity: config.text?.verbosity ?? null, format: responseFormat @@ -635,7 +652,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig ...(tools !== undefined ? { tools } : undefined), ...(toolChoice !== undefined ? { tool_choice: toolChoice } : undefined) } - return toChatCompletionsRequest(request) + return { request: toChatCompletionsRequest(request, messages), toolCodecs } } ) @@ -645,7 +662,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig function*(options) { const config = yield* makeConfig const toolNameMapper = new Tool.NameMapper(options.tools) - const request = yield* makeRequest({ config, options, toolNameMapper }) + const { request, toolCodecs } = yield* makeRequest({ config, options, toolNameMapper }) annotateRequest(options.span, request) const [rawResponse, response] = yield* client.createResponse(request) annotateResponse(options.span, rawResponse) @@ -653,7 +670,8 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig options, rawResponse, response, - toolNameMapper + toolNameMapper, + toolCodecs }) } ), @@ -661,14 +679,15 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig function*(options) { const config = yield* makeConfig const toolNameMapper = new Tool.NameMapper(options.tools) - const request = yield* makeRequest({ config, options, toolNameMapper }) + const { request, toolCodecs } = yield* makeRequest({ config, options, toolNameMapper }) annotateRequest(options.span, request) const [response, stream] = yield* client.createResponseStream(request) return yield* makeStreamResponse({ options, stream, response, - toolNameMapper + toolNameMapper, + toolCodecs }) }, (effect, options) => @@ -758,27 +777,13 @@ const prepareMessages = Effect.fnUntraced( function*>({ config, options, - capabilities, - include, toolNameMapper }: { readonly config: typeof Config.Service readonly options: LanguageModel.ProviderOptions - readonly include: Set - readonly capabilities: ModelCapabilities readonly toolNameMapper: Tool.NameMapper - }): Effect.fn.Return, AiError.AiError> { - const hasConversation = Predicate.isNotNullish(config.conversation) - - // Handle Included Features - if (config.top_logprobs !== undefined) { - include.add("message.output_text.logprobs") - } - if (config.store === false && capabilities.isReasoningModel) { - include.add("reasoning.encrypted_content") - } - - const messages: Array = [] + }): Effect.fn.Return, AiError.AiError> { + const messages: Array = [] for (const message of options.prompt.content) { switch (message.role) { @@ -847,155 +852,62 @@ const prepareMessages = Effect.fnUntraced( } } - messages.push({ role: "user", content }) + messages.push({ role: "user", content: toChatMessageContent(content) }) break } case "assistant": { - const reasoningMessages: Record> = Object.create(null) + let content: string | null = null + let reasoning = "" + let reasoningField: ReasoningField | undefined + const toolCalls: Array = [] for (const part of message.content) { switch (part.type) { case "text": { - const id = getItemId(part) - - // When in conversation mode, skip items that already exist in the - // conversation context to avoid "Duplicate item found" errors - if (hasConversation && Predicate.isNotNull(id)) { - break - } - - if (config.store === true && Predicate.isNotNull(id)) { - messages.push({ type: "item_reference", id }) - break - } - - messages.push({ - id: id!, - type: "message", - role: "assistant", - status: part.options.openai?.status ?? "completed", - content: [{ - type: "output_text", - text: part.text, - annotations: part.options.openai?.annotations ?? [], - logprobs: [] - }] - }) - + content = (content ?? "") + part.text break } case "reasoning": { - const id = getItemId(part) - const encryptedContent = getEncryptedContent(part) - - if (hasConversation && Predicate.isNotNull(id)) { - break - } - - if (Predicate.isNotNull(id)) { - const message = reasoningMessages[id] - - if (config.store === true) { - // Use item references to refer to reasoning (single reference) - // when the first part is encountered - if (Predicate.isUndefined(message)) { - messages.push({ type: "item_reference", id }) - - // Store unused reasoning message to mark its id as used - reasoningMessages[id] = { - type: "reasoning", - id, - summary: [] - } - } - } else { - const summaryParts: Array = [] - - if (part.text.length > 0) { - summaryParts.push({ type: "summary_text", text: part.text }) - } - - if (Predicate.isUndefined(message)) { - reasoningMessages[id] = { - type: "reasoning", - id, - summary: summaryParts, - encrypted_content: encryptedContent ?? null - } - - messages.push(reasoningMessages[id]) - } else { - message.summary.push(...summaryParts) - - // Update encrypted content to enable setting it in the - // last summary part - if (Predicate.isNotNull(encryptedContent)) { - message.encrypted_content = encryptedContent - } - } - } - } - + reasoning += part.text + reasoningField ??= part.options["openai-compat"]?.reasoningField ?? undefined break } case "tool-call": { - const id = getItemId(part) - const status = getStatus(part) - - if (hasConversation && Predicate.isNotNull(id)) { - break - } - - if (config.store && Predicate.isNotNull(id)) { - messages.push({ type: "item_reference", id }) - break - } - if (part.providerExecuted) { break } - const toolName = toolNameMapper.getProviderName(part.name) + const extraContent = getExtraContent(part) - messages.push({ - type: "function_call", - name: toolName, - call_id: part.id, - arguments: JSON.stringify(part.params), - ...(Predicate.isNotNull(id) ? { id } : {}), - ...(Predicate.isNotNull(status) ? { status } : {}) + toolCalls.push({ + id: part.id, + type: "function", + function: { + name: toolNameMapper.getProviderName(part.name), + arguments: JSON.stringify(part.params) + }, + ...(Predicate.isNotNull(extraContent) ? { extra_content: extraContent } : undefined) }) - break } - - // Assistant tool-result parts are always provider executed - case "tool-result": { - // Skip execution denied results - these have no corresponding - // item in OpenAI's store - if ( - Predicate.hasProperty(part.result, "type") && - part.result.type === "execution-denied" - ) { - break - } - - if (hasConversation) { - break - } - - if (config.store === true) { - const id = getItemId(part) ?? part.id - messages.push({ type: "item_reference", id }) - } - } } } + // Text, reasoning and tool calls from one prompt message are sent as a + // single chat message so reasoning stays attached to its tool calls + if (content !== null || toolCalls.length > 0) { + messages.push({ + role: "assistant", + content, + ...(reasoning.length > 0 ? { [reasoningField ?? "reasoning_content"]: reasoning } : undefined), + ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : undefined) + }) + } + break } @@ -1005,13 +917,10 @@ const prepareMessages = Effect.fnUntraced( continue } - const status = getStatus(part) - messages.push({ - type: "function_call_output", - call_id: part.id, - output: typeof part.result === "string" ? part.result : JSON.stringify(part.result), - ...(Predicate.isNotNull(status) ? { status } : {}) + role: "tool", + tool_call_id: part.id, + content: typeof part.result === "string" ? part.result : JSON.stringify(part.result) }) } @@ -1060,6 +969,7 @@ type ActiveToolCall = { readonly id: string name: string arguments: string + extraContent: Schema.JsonObject | undefined } const makeResponse = Effect.fnUntraced( @@ -1067,12 +977,14 @@ const makeResponse = Effect.fnUntraced( options, rawResponse, response, - toolNameMapper + toolNameMapper, + toolCodecs }: { readonly options: LanguageModel.ProviderOptions readonly rawResponse: CreateResponse200 readonly response: HttpClientResponse.HttpClientResponse readonly toolNameMapper: Tool.NameMapper + readonly toolCodecs: ToolCodecs }): Effect.fn.Return< Array, AiError.AiError @@ -1093,9 +1005,10 @@ const makeResponse = Effect.fnUntraced( const message = choice?.message if (message !== undefined) { - const reasoning = message.reasoning ?? message.reasoning_content - if (Predicate.isNotNullish(reasoning) && reasoning.length > 0) { - parts.push({ type: "reasoning", text: reasoning }) + const reasoningField = getResponseReasoningField(message) + const reasoning = reasoningField !== undefined ? message[reasoningField] : undefined + if (reasoningField !== undefined && Predicate.isNotNullish(reasoning) && reasoning.length > 0) { + parts.push({ type: "reasoning", text: reasoning, metadata: { "openai-compat": { reasoningField } } }) } if ( @@ -1121,14 +1034,19 @@ const makeResponse = Effect.fnUntraced( }) }) }) - const params = yield* transformToolCallParams(options.tools, toolName, toolParams) + const params = yield* transformToolCallParams(options, toolName, toolParams, toolCodecs) hasToolCalls = true parts.push({ type: "tool-call", id: toolId, name: toolName, params, - metadata: { openai: { ...makeItemIdMetadata(toolCall.id) } } + metadata: { + "openai-compat": { + ...makeItemIdMetadata(toolCall.id), + ...makeExtraContentMetadata(toolCall.extra_content ?? undefined) + } + } }) } } @@ -1145,7 +1063,7 @@ const makeResponse = Effect.fnUntraced( reason: finishReason, usage: getUsage(rawResponse.usage), response: buildHttpResponseDetails(response), - ...(serviceTier !== undefined && { metadata: { openai: { serviceTier } } }) + ...(serviceTier !== undefined && { metadata: { "openai-compat": { serviceTier } } }) }) return parts @@ -1157,12 +1075,14 @@ const makeStreamResponse = Effect.fnUntraced( options, stream, response, - toolNameMapper + toolNameMapper, + toolCodecs }: { readonly options: LanguageModel.ProviderOptions readonly stream: Stream.Stream readonly response: HttpClientResponse.HttpClientResponse readonly toolNameMapper: Tool.NameMapper + readonly toolCodecs: ToolCodecs }): Effect.fn.Return< Stream.Stream, AiError.AiError @@ -1187,7 +1107,7 @@ const makeStreamResponse = Effect.fnUntraced( parts.push({ type: "reasoning-end", id: reasoningId, - metadata: { openai: { ...makeItemIdMetadata(reasoningId) } } + metadata: { "openai-compat": { ...makeItemIdMetadata(reasoningId) } } }) } @@ -1195,7 +1115,7 @@ const makeStreamResponse = Effect.fnUntraced( parts.push({ type: "text-end", id: textId, - metadata: { openai: { ...makeItemIdMetadata(textId) } } + metadata: { "openai-compat": { ...makeItemIdMetadata(textId) } } }) } @@ -1213,14 +1133,19 @@ const makeStreamResponse = Effect.fnUntraced( }) }) }) - const params = yield* transformToolCallParams(options.tools, toolCall.name, parsedParams) + const params = yield* transformToolCallParams(options, toolCall.name, parsedParams, toolCodecs) parts.push({ type: "tool-params-end", id: toolCall.id }) parts.push({ type: "tool-call", id: toolCall.id, name: toolCall.name, params, - metadata: { openai: { ...makeItemIdMetadata(toolCall.id) } } + metadata: { + "openai-compat": { + ...makeItemIdMetadata(toolCall.id), + ...makeExtraContentMetadata(toolCall.extraContent) + } + } }) hasToolCalls = true } @@ -1232,7 +1157,7 @@ const makeStreamResponse = Effect.fnUntraced( usage: getUsage(usage), response: buildHttpResponseDetails(response), ...(normalizedServiceTier !== undefined - ? { metadata: { openai: { serviceTier: normalizedServiceTier } } } + ? { metadata: { "openai-compat": { serviceTier: normalizedServiceTier } } } : undefined) }) return parts @@ -1269,26 +1194,29 @@ const makeStreamResponse = Effect.fnUntraced( return parts } - const reasoningDelta = choice.delta?.reasoning ?? choice.delta?.reasoning_content - if (Predicate.isNotNullish(reasoningDelta) && reasoningDelta.length > 0) { + const reasoningField = Predicate.isNotNullish(choice.delta) + ? getResponseReasoningField(choice.delta) + : undefined + const reasoningDelta = reasoningField !== undefined ? choice.delta?.[reasoningField] : undefined + if (reasoningField !== undefined && Predicate.isNotNullish(reasoningDelta) && reasoningDelta.length > 0) { if (!reasoningStarted) { reasoningStarted = true parts.push({ type: "reasoning-start", id: reasoningId, - metadata: { openai: { ...makeItemIdMetadata(reasoningId) } } + metadata: { "openai-compat": { ...makeItemIdMetadata(reasoningId), reasoningField } } }) } parts.push({ type: "reasoning-delta", id: reasoningId, delta: reasoningDelta }) } - if (choice.delta?.content !== undefined && Predicate.isNotNull(choice.delta.content)) { + if (Predicate.isNotNullish(choice.delta?.content) && choice.delta.content.length > 0) { if (reasoningStarted) { reasoningStarted = false parts.push({ type: "reasoning-end", id: reasoningId, - metadata: { openai: { ...makeItemIdMetadata(reasoningId) } } + metadata: { "openai-compat": { ...makeItemIdMetadata(reasoningId) } } }) } @@ -1297,7 +1225,7 @@ const makeStreamResponse = Effect.fnUntraced( parts.push({ type: "text-start", id: textId, - metadata: { openai: { ...makeItemIdMetadata(textId) } } + metadata: { "openai-compat": { ...makeItemIdMetadata(textId) } } }) } parts.push({ type: "text-delta", id: textId, delta: choice.delta.content }) @@ -1319,12 +1247,16 @@ const makeStreamResponse = Effect.fnUntraced( activeToolCalls[toolIndex] = { id: toolId, name: toolName, - arguments: argumentsDelta + arguments: argumentsDelta, + extraContent: deltaTool.extra_content ?? undefined } parts.push({ type: "tool-params-start", id: toolId, name: toolName }) } else { activeToolCall.name = toolName activeToolCall.arguments = `${activeToolCall.arguments}${argumentsDelta}` + if (Predicate.isNotNullish(deltaTool.extra_content)) { + activeToolCall.extraContent = deltaTool.extra_content + } } if (argumentsDelta.length > 0) { @@ -1339,7 +1271,8 @@ const makeStreamResponse = Effect.fnUntraced( return parts })), - Stream.flattenIterable + Stream.filter(Arr.isArrayNonEmpty), + Stream.flattenArray ) } ) @@ -1399,7 +1332,7 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) => }) } if (part.type === "finish") { - const serviceTier = (part.metadata as any)?.openai?.serviceTier as string | undefined + const serviceTier = (part.metadata as any)?.["openai-compat"]?.serviceTier as string | undefined addGenAIAnnotations(span, { response: { finishReasons: [part.reason] @@ -1442,26 +1375,27 @@ const tryJsonSchema = (schema: S, method: string) = catch: (error) => unsupportedSchemaError(error, method) }) -const tryToolJsonSchema = (tool: T, method: string) => - Effect.try({ - try: () => Tool.getJsonSchema(tool, { transformer: toCodecOpenAI }), - catch: (error) => unsupportedSchemaError(error, method) - }) +type ToolCodec = ReturnType["codec"] +type ToolCodecs = ReadonlyMap -const transformToolCallParams = Effect.fnUntraced(function*>( - tools: Tools, +const transformToolCallParams = Effect.fnUntraced(function*( + options: LanguageModel.ProviderOptions, toolName: string, - toolParams: unknown + toolParams: unknown, + toolCodecs: ToolCodecs ): Effect.fn.Return { - const tool = tools.find((tool) => tool.name === toolName) + const tool = options.tools.find((tool) => tool.name === toolName) if (Predicate.isUndefined(tool)) { + if (options.toolCallValidation === "deferred") { + return toolParams + } return yield* AiError.make({ module: "OpenAiLanguageModel", method: "makeResponse", reason: new AiError.ToolNotFoundError({ toolName, - availableTools: tools.map((tool) => tool.name) + availableTools: options.tools.map((tool) => tool.name) }) }) } @@ -1470,7 +1404,7 @@ const transformToolCallParams = Effect.fnUntraced(function* | undefined readonly toolChoice: OpenAiToolChoice | undefined + readonly toolCodecs: ToolCodecs }, AiError.AiError> { + const toolCodecs = new Map() + // Return immediately if no tools are in the toolkit if (options.tools.length === 0) { - return { tools: undefined, toolChoice: undefined } + return { tools: undefined, toolChoice: undefined, toolCodecs } } const tools: Array = [] @@ -1518,7 +1455,15 @@ const prepareTools = Effect.fnUntraced(function* + if (Tool.isDynamic(tool) && tool.jsonSchema !== undefined) { + parameters = tool.jsonSchema + } else { + const prepared = yield* tryCodecTransform(tool.parametersSchema, "prepareTools") + parameters = prepared.jsonSchema + // Reused to normalize this tool's calls in the response. + toolCodecs.set(tool, prepared.codec) + } tools.push({ type: "function", name: tool.name, @@ -1553,11 +1498,13 @@ const prepareTools = Effect.fnUntraced(function* { - const messages = toChatMessages(payload.input) +const toChatCompletionsRequest = ( + payload: CreateResponse, + messages: ReadonlyArray +): CreateResponseRequestJson => { const responseFormat = toChatResponseFormat(payload.text?.format) const tools = payload.tools !== undefined ? payload.tools.map(toChatTool).filter((tool): tool is NonNullable> => @@ -1719,112 +1666,6 @@ const toChatTool = ( return undefined } -const toChatMessages = ( - input: CreateResponse["input"] -): Array => { - if (Predicate.isUndefined(input)) { - return [] - } - - if (typeof input === "string") { - return [{ role: "user", content: input }] - } - - const messages: Array = [] - - for (const item of input) { - if (Predicate.hasProperty(item, "type") && item.type === "function_call") { - const previous = messages.at(-1) - const toolCall = toChatToolCall(item) - if (previous?.role === "assistant" && previous.tool_calls !== undefined) { - messages[messages.length - 1] = { - ...previous, - tool_calls: [...previous.tool_calls, toolCall] - } - } else { - messages.push({ - role: "assistant", - content: null, - tool_calls: [toolCall] - }) - } - } else { - messages.push(...toChatMessagesFromItem(item)) - } - } - - return messages -} - -const toChatMessagesFromItem = ( - item: InputItem -): Array => { - if (Predicate.hasProperty(item, "type") && item.type === "message") { - return [{ - role: item.role, - content: toAssistantChatMessageContent(item.content) - }] - } - - if (Predicate.hasProperty(item, "role")) { - return [{ - role: item.role, - content: toChatMessageContent(item.content) - }] - } - - switch (item.type) { - case "function_call": { - return [{ - role: "assistant", - content: null, - tool_calls: [toChatToolCall(item)] - }] - } - - case "function_call_output": { - return [{ - role: "tool", - tool_call_id: item.call_id, - content: stringifyJson(item.output) - }] - } - - default: { - return [] - } - } -} - -const toChatToolCall = ( - item: Extract -): ChatCompletionRequestToolCall => ({ - id: item.call_id, - type: "function", - function: { - name: item.name, - arguments: item.arguments - } -}) - -const toAssistantChatMessageContent = ( - content: ReadonlyArray<{ - readonly type: string - readonly [x: string]: unknown - }> -): string => { - let text = "" - for (const part of content) { - if (part.type === "output_text" && typeof part.text === "string") { - text += part.text - } - if (part.type === "refusal" && typeof part.refusal === "string") { - text += part.refusal - } - } - return text -} - const toChatMessageContent = ( content: string | ReadonlyArray ): string | ReadonlyArray => { @@ -1832,6 +1673,10 @@ const toChatMessageContent = ( return content } + if (content.length === 1 && content[0].type === "input_text") { + return content[0].text + } + const parts: Array = [] for (const part of content) { @@ -1882,11 +1727,6 @@ const toChatMessageContent = ( return parts } -const stringifyJson = (value: unknown): string => - typeof value === "string" - ? value - : JSON.stringify(value) - // ============================================================================= // Utilities // ============================================================================= @@ -1894,27 +1734,34 @@ const stringifyJson = (value: unknown): string => const isFileId = (data: string, config: typeof Config.Service): boolean => config.fileIdPrefixes != null && config.fileIdPrefixes.some((prefix) => data.startsWith(prefix)) -const getItemId = ( - part: - | Prompt.TextPart - | Prompt.ReasoningPart - | Prompt.ToolCallPart - | Prompt.ToolResultPart -): string | null => part.options.openai?.itemId ?? null -const getStatus = ( - part: - | Prompt.TextPart - | Prompt.ToolCallPart - | Prompt.ToolResultPart -): MessageStatus | null => part.options.openai?.status ?? null -const getEncryptedContent = ( - part: Prompt.ReasoningPart -): string | null => part.options.openai?.encryptedContent ?? null - -const getImageDetail = (part: Prompt.FilePart): ImageDetail => part.options.openai?.imageDetail ?? "auto" +const getExtraContent = ( + part: Prompt.ToolCallPart +): Schema.JsonObject | null => part.options["openai-compat"]?.extraContent ?? null + +/** + * The chat message field carrying reasoning text. Providers differ: DeepSeek, + * Kimi and GLM use `reasoning_content`, while Groq uses `reasoning` and + * rejects `reasoning_content` on request messages. + */ +type ReasoningField = "reasoning" | "reasoning_content" + +const getResponseReasoningField = (message: { + readonly reasoning?: string | null | undefined + readonly reasoning_content?: string | null | undefined +}): ReasoningField | undefined => + Predicate.isNotNullish(message.reasoning) + ? "reasoning" + : Predicate.isNotNullish(message.reasoning_content) + ? "reasoning_content" + : undefined + +const getImageDetail = (part: Prompt.FilePart): ImageDetail => part.options["openai-compat"]?.imageDetail ?? "auto" const makeItemIdMetadata = (itemId: string | undefined) => itemId !== undefined ? { itemId } : undefined +const makeExtraContentMetadata = (extraContent: Schema.JsonObject | undefined) => + extraContent !== undefined ? { extraContent } : undefined + const normalizeServiceTier = ( serviceTier: string | undefined ): "default" | "auto" | "flex" | "scale" | "priority" | null | undefined => { @@ -1951,54 +1798,6 @@ const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: { return { type: "text" } }) -interface ModelCapabilities { - readonly isReasoningModel: boolean - readonly systemMessageMode: "remove" | "system" | "developer" - readonly supportsFlexProcessing: boolean - readonly supportsPriorityProcessing: boolean - /** - * Allow temperature, topP, logProbs when reasoningEffort is none. - */ - readonly supportsNonReasoningParameters: boolean -} - -const getModelCapabilities = (modelId: string): ModelCapabilities => { - const supportsFlexProcessing = modelId.startsWith("o3") || - modelId.startsWith("o4-mini") || - (modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat")) - - const supportsPriorityProcessing = modelId.startsWith("gpt-4") || - modelId.startsWith("gpt-5-mini") || - (modelId.startsWith("gpt-5") && - !modelId.startsWith("gpt-5-nano") && - !modelId.startsWith("gpt-5-chat")) || - modelId.startsWith("o3") || - modelId.startsWith("o4-mini") - - // Use allowlist approach: only known reasoning models should use 'developer' role - // This prevents issues with fine-tuned models, third-party models, and custom models - const isReasoningModel = modelId.startsWith("o1") || - modelId.startsWith("o3") || - modelId.startsWith("o4-mini") || - modelId.startsWith("codex-mini") || - modelId.startsWith("computer-use-preview") || - (modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat")) - - // https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility - // GPT-5.1 and GPT-5.2 support temperature, topP, logProbs when reasoningEffort is none - const supportsNonReasoningParameters = modelId.startsWith("gpt-5.1") || modelId.startsWith("gpt-5.2") - - const systemMessageMode = isReasoningModel ? "developer" : "system" - - return { - supportsFlexProcessing, - supportsPriorityProcessing, - isReasoningModel, - systemMessageMode, - supportsNonReasoningParameters - } -} - const getUsage = (usage: CreateResponse200["usage"]): Response.Usage => { if (Predicate.isNullish(usage)) { return { diff --git a/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts b/packages/ai/openai-compat/src/OpenAiTelemetry.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts rename to packages/ai/openai-compat/src/OpenAiTelemetry.ts diff --git a/repos/effect/packages/ai/openai-compat/src/index.ts b/packages/ai/openai-compat/src/index.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/index.ts rename to packages/ai/openai-compat/src/index.ts diff --git a/repos/effect/packages/ai/openai-compat/src/internal/errors.ts b/packages/ai/openai-compat/src/internal/errors.ts similarity index 97% rename from repos/effect/packages/ai/openai-compat/src/internal/errors.ts rename to packages/ai/openai-compat/src/internal/errors.ts index 4fef55b9d..b80a6b413 100644 --- a/repos/effect/packages/ai/openai-compat/src/internal/errors.ts +++ b/packages/ai/openai-compat/src/internal/errors.ts @@ -230,7 +230,7 @@ export const mapStatusCodeToReason = ({ status, headers, message, metadata, http case 400: return new AiError.InvalidRequestError({ description: errorDescription, - metadata: { openai: metadata }, + metadata: { "openai-compat": metadata }, http }) case 401: @@ -250,14 +250,14 @@ export const mapStatusCodeToReason = ({ status, headers, message, metadata, http case 404: return new AiError.InvalidRequestError({ description: errorDescription, - metadata: { openai: metadata }, + metadata: { "openai-compat": metadata }, http }) case 409: case 422: return new AiError.InvalidRequestError({ description: errorDescription, - metadata: { openai: metadata }, + metadata: { "openai-compat": metadata }, http }) case 429: { @@ -268,7 +268,7 @@ export const mapStatusCodeToReason = ({ status, headers, message, metadata, http metadata.errorType?.includes("exhausted") ) { return new AiError.QuotaExhaustedError({ - metadata: { openai: metadata }, + metadata: { "openai-compat": metadata }, http }) } @@ -276,7 +276,7 @@ export const mapStatusCodeToReason = ({ status, headers, message, metadata, http return new AiError.RateLimitError({ retryAfter, metadata: { - openai: { + "openai-compat": { ...metadata, ...rateLimitMetadata } diff --git a/repos/effect/packages/ai/openai-compat/src/internal/utilities.ts b/packages/ai/openai-compat/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/src/internal/utilities.ts rename to packages/ai/openai-compat/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts b/packages/ai/openai-compat/test/OpenAiClient.test.ts similarity index 99% rename from repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts rename to packages/ai/openai-compat/test/OpenAiClient.test.ts index ac3e031b8..8cbd49581 100644 --- a/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts +++ b/packages/ai/openai-compat/test/OpenAiClient.test.ts @@ -184,7 +184,7 @@ describe("OpenAiClient", () => { ] })))) - it.effect("drops invalid JSON and continues streaming", () => + it.effect("drops invalid JSON and streams valid events until [DONE]", () => Effect.gen(function*() { const client = yield* OpenAiClient.OpenAiClient @@ -215,7 +215,8 @@ describe("OpenAiClient", () => { }] }) }\n\n`, - "data: [DONE]\n\n" + "data: [DONE]\n\n", + "data: {\"ignored\":\"after completion\"}\n\n" ].join("") })))) diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts b/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts rename to packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts b/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts similarity index 78% rename from repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts rename to packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts index 43a2dc4ae..c1ae663ba 100644 --- a/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts +++ b/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts @@ -5,8 +5,102 @@ import { type AiError, LanguageModel, Prompt, Tool, Toolkit } from "effect/ai" import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" describe("OpenAiLanguageModel", () => { + for (const mode of ["complete", "stream"] as const) { + it.effect(`prepares selected tool codecs once per ${mode} request and normalizes repeated calls`, () => + Effect.gen(function*() { + let conversions = 0 + const Env = Schema.Record(Schema.String, Schema.String).check( + Schema.makeFilter(() => true, { + representation: { id: "test/compat/toolCodec", payload: null }, + toJsonSchema: () => { + conversions++ + return { minItems: 1 } + } + }) + ) + const ReadEnv = Tool.make("ReadEnv", { parameters: Schema.Struct({ env: Env }) }) + const Unselected = Tool.make("Unselected", { + parameters: Schema.Struct({ opaque: Schema.declare((input): input is string => typeof input === "string") }) + }) + const toolkit = Toolkit.make(ReadEnv, Unselected) + const toolkitLayer = toolkit.toLayer({ + ReadEnv: () => Effect.void, + Unselected: () => Effect.void + }) + const calls = ["call_1", "call_2"].map((id) => ({ + id, + type: "function" as const, + function: { name: "ReadEnv", arguments: JSON.stringify({ env: [{ 0: "PATH", 1: "/usr/bin" }] }) } + })) + const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => + Effect.gen(function*() { + const body = yield* getRequestBody(request) + assert.deepStrictEqual(body.tools.map((tool: any) => tool.function.name), ["ReadEnv"]) + assert.strictEqual(body.tools[0].function.parameters.properties.env.type, "array") + return mode === "complete" + ? jsonResponse( + request, + makeChatCompletion({ + choices: [{ + index: 0, + finish_reason: "tool_calls", + message: { role: "assistant", content: null, tool_calls: calls } + }] + }) + ) + : sseResponse(request, [{ + id: "chatcmpl_env", + object: "chat.completion.chunk", + model: "gpt-4o-mini", + created: 1, + choices: [{ + index: 0, + delta: { tool_calls: calls.map((call, index) => ({ ...call, index })) }, + finish_reason: "tool_calls" + }] + }, "[DONE]"]) + }) + ) + )) + ) + for (let request = 1; request <= 2; request++) { + const options = { + prompt: "read the environment", + toolkit, + toolChoice: { oneOf: ["ReadEnv"] as Array<"ReadEnv"> }, + disableToolCallResolution: true + } + const calls = yield* (mode === "complete" + ? LanguageModel.generateText(options).pipe( + Effect.map((result) => result.toolCalls.map((part) => ({ id: part.id, params: part.params }))) + ) + : LanguageModel.streamText(options).pipe( + Stream.runCollect, + Effect.map((parts) => + parts.filter((part) => part.type === "tool-call").map((part) => ({ id: part.id, params: part.params })) + ) + )).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(toolkitLayer), + Effect.provide(layer) + ) + assert.deepStrictEqual( + calls, + [ + { id: "call_1", params: { env: { PATH: "/usr/bin" } } }, + { id: "call_2", params: { env: { PATH: "/usr/bin" } } } + ] + ) + assert.strictEqual(conversions, request) + } + })) + } + describe("generateText", () => { - it.effect("sends model in request and decodes text output", () => + it.effect("serializes text-only user messages and decodes text output", () => Effect.gen(function*() { let capturedRequest: HttpClientRequest.HttpClientRequest | undefined @@ -32,7 +126,15 @@ describe("OpenAiLanguageModel", () => { )) ) - const result = yield* LanguageModel.generateText({ prompt: "hello" }).pipe( + const result = yield* LanguageModel.generateText({ + prompt: Prompt.make([ + { role: "user", content: [Prompt.textPart({ text: "hello" })] }, + { role: "user", content: [Prompt.textPart({ text: "" })] }, + { role: "user", content: [Prompt.textPart({ text: " Hello, 世界!\nGoodbye. " })] }, + { role: "user", content: [Prompt.textPart({ text: "first" }), Prompt.textPart({ text: "second" })] }, + { role: "user", content: [] } + ]) + }).pipe( Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), Effect.provide(layer) ) @@ -45,7 +147,13 @@ describe("OpenAiLanguageModel", () => { const requestBody = yield* getRequestBody(capturedRequest) assert.strictEqual(requestBody.model, "gpt-4o-mini") - assert.strictEqual(requestBody.messages[0]?.content, "hello") + assert.deepStrictEqual(requestBody.messages, [ + { role: "user", content: "hello" }, + { role: "user", content: "" }, + { role: "user", content: " Hello, 世界!\nGoodbye. " }, + { role: "user", content: "first\nsecond" }, + { role: "user", content: "" } + ]) })) it.effect("routes invalid tool call params through failureMode: return without failing the effect", () => @@ -633,6 +741,53 @@ describe("OpenAiLanguageModel", () => { ]) })) + it.effect("echoes tool call extra_content in follow-up requests", () => + Effect.gen(function*() { + const extraContent = { google: { thought_signature: "signature-1" } } + const requests: Array = [] + + const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => { + requests.push(request) + return Effect.succeed(jsonResponse( + request, + requests.length > 1 ? makeChatCompletion() : makeChatCompletion({ + choices: [{ + index: 0, + finish_reason: "tool_calls", + message: { + role: "assistant", + content: null, + tool_calls: [{ + id: "call_1", + type: "function", + extra_content: extraContent, + function: { name: "TestTool", arguments: JSON.stringify({ input: "hello" }) } + }] + } + }] + }) + )) + }) + )) + ) + + const generate = (prompt: Prompt.Prompt) => + LanguageModel.generateText({ prompt, toolkit: TestToolkit, disableToolCallResolution: true }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(layer) + ) + + const prompt = Prompt.make("use the tool") + const first = yield* generate(prompt) + yield* generate(Prompt.concat(prompt, Prompt.fromResponseParts(first.content))) + + const followUpBody = yield* getRequestBody(requests[1]) + assert.deepStrictEqual(followUpBody.messages[1].tool_calls[0].extra_content, extraContent) + })) + it.effect("converts dynamic tools to function type", () => Effect.gen(function*() { let capturedRequest: HttpClientRequest.HttpClientRequest | undefined @@ -918,6 +1073,168 @@ describe("OpenAiLanguageModel", () => { assert.strictEqual(reasoning.text, "I should greet the user.") assert.strictEqual(result.text, "Hello!") })) + + it.effect("returns invalid and undeclared tool calls when tool call validation is deferred", () => + Effect.gen(function*() { + const client = makeHttpClient((request) => + Effect.succeed(jsonResponse( + request, + makeChatCompletion({ + choices: [{ + index: 0, + finish_reason: "tool_calls", + message: { + role: "assistant", + content: null, + tool_calls: [ + { id: "call_1", type: "function", function: { name: "TestTool", arguments: "{\"input\":42}" } }, + { id: "call_2", type: "function", function: { name: "Write", arguments: "{\"path\":\"a\"}" } } + ] + } + }] + }) + )) + ) + + const result = yield* LanguageModel.generateText({ + prompt: "use the tools", + toolkit: TestToolkit, + disableToolCallResolution: true, + toolCallValidation: "deferred" + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(TestToolkitLayer), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + assert.deepStrictEqual(result.toolCalls.map(({ name, params }) => ({ name, params })), [ + { name: "TestTool", params: { input: 42 } }, + { name: "Write", params: { path: "a" } } + ]) + })) + + it.effect("replays reasoning alongside assistant text and tool calls", () => + Effect.gen(function*() { + let capturedRequest: HttpClientRequest.HttpClientRequest | undefined + + const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => { + capturedRequest = request + return Effect.succeed(jsonResponse(request, makeChatCompletion())) + }) + )) + ) + + const prompt = Prompt.make([ + { role: "user", content: "What is in a.txt?" }, + { + role: "assistant", + content: [ + { type: "reasoning", text: "The user wants a.txt; I should read it." }, + { type: "text", text: "Let me read it." }, + { type: "tool-call", id: "call_1", name: "TestTool", params: { input: "a.txt" } } + ] + }, + { + role: "tool", + content: [{ type: "tool-result", id: "call_1", name: "TestTool", isFailure: false, result: "hello" }] + } + ]) + + yield* LanguageModel.generateText({ prompt, toolkit: TestToolkit, disableToolCallResolution: true }).pipe( + Effect.provide(OpenAiLanguageModel.model("deepseek-reasoner")), + Effect.provide(layer) + ) + + assert.isDefined(capturedRequest) + if (capturedRequest === undefined) { + return + } + + const requestBody = yield* getRequestBody(capturedRequest) + assert.deepStrictEqual(requestBody.messages, [ + { role: "user", content: "What is in a.txt?" }, + { + role: "assistant", + content: "Let me read it.", + reasoning_content: "The user wants a.txt; I should read it.", + tool_calls: [{ + id: "call_1", + type: "function", + function: { name: "TestTool", arguments: JSON.stringify({ input: "a.txt" }) } + }] + }, + { role: "tool", tool_call_id: "call_1", content: "hello" } + ]) + })) + + it.effect("replays decoded Groq reasoning under its original field alongside text and tool calls", () => + Effect.gen(function*() { + const requests: Array = [] + + const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => { + requests.push(request) + return Effect.succeed(jsonResponse( + request, + requests.length > 1 ? makeChatCompletion() : makeChatCompletion({ + choices: [{ + index: 0, + finish_reason: "tool_calls", + message: { + role: "assistant", + reasoning: "Need the file", + content: "Let me read it.", + tool_calls: [{ + id: "call_1", + type: "function", + function: { name: "TestTool", arguments: JSON.stringify({ input: "a.txt" }) } + }] + } + }] + }) + )) + }) + )) + ) + + const generate = (prompt: Prompt.Prompt) => + LanguageModel.generateText({ prompt, toolkit: TestToolkit, disableToolCallResolution: true }).pipe( + Effect.provide(OpenAiLanguageModel.model("openai/gpt-oss-120b")), + Effect.provide(layer) + ) + + const prompt = Prompt.make("What is in a.txt?") + const first = yield* generate(prompt) + yield* generate(Prompt.concat( + Prompt.concat(prompt, Prompt.fromResponseParts(first.content)), + Prompt.make([{ + role: "tool", + content: [{ type: "tool-result", id: "call_1", name: "TestTool", isFailure: false, result: "hello" }] + }]) + )) + + const followUpBody = yield* getRequestBody(requests[1]) + assert.deepStrictEqual(followUpBody.messages, [ + { role: "user", content: "What is in a.txt?" }, + { + role: "assistant", + content: "Let me read it.", + reasoning: "Need the file", + tool_calls: [{ + id: "call_1", + type: "function", + function: { name: "TestTool", arguments: JSON.stringify({ input: "a.txt" }) } + }] + }, + { role: "tool", tool_call_id: "call_1", content: "hello" } + ]) + })) }) describe("generateObject", () => { @@ -1025,6 +1342,88 @@ describe("OpenAiLanguageModel", () => { }) describe("streamText", () => { + it.effect("replays streamed assistant text and tool calls with store enabled", () => + Effect.gen(function*() { + const requests: Array = [] + + const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + makeHttpClient((request) => { + requests.push(request) + return Effect.succeed( + requests.length > 1 + ? jsonResponse(request, makeChatCompletion()) + : sseResponse(request, [ + { + id: "chatcmpl_store_1", + object: "chat.completion.chunk", + model: "gpt-4o-mini", + created: 1, + choices: [{ + index: 0, + delta: { + content: "Let me read it.", + tool_calls: [{ + index: 0, + id: "call_1", + type: "function", + function: { name: "TestTool", arguments: JSON.stringify({ input: "a.txt" }) } + }] + }, + finish_reason: "tool_calls" + }] + }, + "[DONE]" + ]) + ) + }) + )) + ) + + const model = OpenAiLanguageModel.model("gpt-4o-mini", { store: true }) + const prompt = Prompt.make("What is in a.txt?") + const parts = yield* LanguageModel.streamText({ + prompt, + toolkit: TestToolkit, + disableToolCallResolution: true + }).pipe( + Stream.runCollect, + Effect.provide(model), + Effect.provide(layer) + ) + + yield* LanguageModel.generateText({ + prompt: Prompt.concat( + Prompt.concat(prompt, Prompt.fromResponseParts(parts)), + Prompt.make([{ + role: "tool", + content: [{ type: "tool-result", id: "call_1", name: "TestTool", isFailure: false, result: "hello" }] + }]) + ), + toolkit: TestToolkit, + disableToolCallResolution: true + }).pipe( + Effect.provide(model), + Effect.provide(layer) + ) + + const followUpBody = yield* getRequestBody(requests[1]) + assert.deepStrictEqual(followUpBody.messages, [ + { role: "user", content: "What is in a.txt?" }, + { + role: "assistant", + content: "Let me read it.", + tool_calls: [{ + id: "call_1", + type: "function", + function: { name: "TestTool", arguments: JSON.stringify({ input: "a.txt" }) } + }] + }, + { role: "tool", tool_call_id: "call_1", content: "hello" } + ]) + })) + it.effect("handles chat completion stream chunks", () => Effect.gen(function*() { const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( @@ -1066,6 +1465,51 @@ describe("OpenAiLanguageModel", () => { } })) + it.effect("streams undeclared tool calls when tool call validation is deferred", () => + Effect.gen(function*() { + const client = makeHttpClient((request) => + Effect.succeed(sseResponse(request, [ + { + id: "chatcmpl_undeclared", + object: "chat.completion.chunk", + model: "gpt-4o-mini", + created: 1, + choices: [{ + index: 0, + delta: { + tool_calls: [{ + index: 0, + id: "call_1", + type: "function", + function: { name: "Write", arguments: JSON.stringify({ path: "a" }) } + }] + }, + finish_reason: "tool_calls" + }] + }, + "[DONE]" + ])) + ) + + const parts = yield* LanguageModel.streamText({ + prompt: "use the tools", + toolkit: TestToolkit, + disableToolCallResolution: true, + toolCallValidation: "deferred" + }).pipe( + Stream.runCollect, + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(TestToolkitLayer), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + assert.deepStrictEqual( + parts.filter((part) => part.type === "tool-call").map(({ name, params }) => ({ name, params })), + [{ name: "Write", params: { path: "a" } }] + ) + })) + it.effect("emits text when streamed tool_calls is null", () => Effect.gen(function*() { const layer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( @@ -1110,6 +1554,7 @@ describe("OpenAiLanguageModel", () => { it.effect("preserves streamed text and tool args with nullable delta fields", () => Effect.gen(function*() { + const extraContent = { google: { thought_signature: "signature-1" } } const chunk = (delta: Record) => ({ id: "chatcmpl_nullable_delta_fields", object: "chat.completion.chunk", @@ -1129,11 +1574,17 @@ describe("OpenAiLanguageModel", () => { index: 0, id: "call_1", type: "function", + extra_content: extraContent, function: { name: "TestTool", arguments: "" } }] }), chunk({ - tool_calls: [{ index: 0, id: null, function: { name: null, arguments: "{\"in" } }] + tool_calls: [{ + index: 0, + id: null, + extra_content: null, + function: { name: null, arguments: "{\"in" } + }] }), chunk({ tool_calls: [{ index: 0, id: null, function: { name: null, arguments: "put\":\"hel" } }] @@ -1179,6 +1630,7 @@ describe("OpenAiLanguageModel", () => { } assert.strictEqual(toolCall.id, "call_1") assert.deepStrictEqual(toolCall.params, { input: "hello" }) + assert.deepStrictEqual(toolCall.metadata["openai-compat"] as unknown, { itemId: "call_1", extraContent }) })) it.effect("decodes streamed tool call params with the OpenAI codec", () => @@ -1294,7 +1746,7 @@ describe("OpenAiLanguageModel", () => { params: { action: localShellAction }, providerExecuted: false, options: { - openai: { + "openai-compat": { itemId: "ls_call_1" } } @@ -1712,10 +2164,11 @@ describe("OpenAiLanguageModel", () => { HttpClient.HttpClient, makeHttpClient((request) => Effect.succeed(sseResponse(request, [ - chunk({ reasoning: "Let me think" }), - chunk({ reasoning: " about this." }), + chunk({ content: "", reasoning: "Let me think" }), + chunk({ content: "", reasoning: " about this." }), chunk({ content: "Hello" }), - chunk({ content: " there" }, "stop"), + chunk({ content: " there" }), + chunk({ content: "" }, "stop"), "[DONE]" ])) ) diff --git a/repos/effect/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts b/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/test/OpenAiTelemetry.test.ts rename to packages/ai/openai-compat/test/OpenAiTelemetry.test.ts diff --git a/repos/effect/packages/ai/openai-compat/tsconfig.json b/packages/ai/openai-compat/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/openai-compat/tsconfig.json rename to packages/ai/openai-compat/tsconfig.json diff --git a/repos/effect/packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts b/packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts similarity index 100% rename from repos/effect/packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts rename to packages/ai/openai-compat/typetest/OpenAiConfig.tst.ts diff --git a/repos/effect/packages/ai/openai/CHANGELOG.md b/packages/ai/openai/CHANGELOG.md similarity index 92% rename from repos/effect/packages/ai/openai/CHANGELOG.md rename to packages/ai/openai/CHANGELOG.md index e7637f174..1ed5bd3f2 100644 --- a/repos/effect/packages/ai/openai/CHANGELOG.md +++ b/packages/ai/openai/CHANGELOG.md @@ -1,5 +1,52 @@ # @effect/ai-openai +## 4.0.5 + +### Patch Changes + +- [#9140](https://github.com/Effect-TS/effect/pull/9140) [`91f4c74`](https://github.com/Effect-TS/effect/commit/91f4c74fe8f41a9897927e9bc2c4b814924c85e0) Thanks @tim-smart! - Add `OpenAiDecisionModel`, a `DecisionModel` backed by OpenAI's Decisions API (`gpt-6-luna`), and `OpenAiClient.createDecisions`. Refused decisions fail the call with `AiError.ContentPolicyError`, and remote image URLs fail with `AiError.InvalidUserInputError`. + +- [#9097](https://github.com/Effect-TS/effect/pull/9097) [`68caf1d`](https://github.com/Effect-TS/effect/commit/68caf1d642e882d2882624d9227b7d6414a1065f) Thanks @adam-r-kowalski! - Stop Responses streams at the `data: [DONE]` sentinel sent by some OpenAI-compatible servers instead of failing to decode it. + +- [#9115](https://github.com/Effect-TS/effect/pull/9115) [`5d018e1`](https://github.com/Effect-TS/effect/commit/5d018e1fb171d9a2e8658305ee6cb232d67b0ae5) Thanks @danieljvdm! - Preserve the `ultrafast` service tier in finish metadata. + +- [#9124](https://github.com/Effect-TS/effect/pull/9124) [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e) Thanks @jem-computer! - Support structured decision instructions, descriptions, and score legends in TypeSafe, Cloudflare, and OpenRouter. `Decision.rate` accepts labeled descriptions; `Rate.criteria` entries are now `{ label, description }` objects instead of strings. + +- [#9100](https://github.com/Effect-TS/effect/pull/9100) [`51c079b`](https://github.com/Effect-TS/effect/commit/51c079b90b6d4453601efc2d0402cdfc6b7533f2) Thanks @tim-smart! - Accept omitted `pattern` fields in OpenAI `find_in_page` web-search actions so streams do not fail validation. The public `WebSearchActionFind.pattern` property is now optional (`string | undefined` when read); explicit null and non-string values remain invalid. +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- [#9008](https://github.com/Effect-TS/effect/pull/9008) [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d) Thanks @effect-bot! - Add `toolCallValidation: "deferred"` for callers using `disableToolCallResolution: true`. Model-generated tool calls return with `unknown` parameters, including invalid parameters and undeclared tool names, even without a toolkit. Supported by the Anthropic, OpenAI, and OpenAI-compatible providers. Strict validation remains the default; provider-executed calls remain validated. + +- [#9067](https://github.com/Effect-TS/effect/pull/9067) [`984b4ab`](https://github.com/Effect-TS/effect/commit/984b4abfb2a804659e63b72f345ead0c1e511caa) Thanks @tim-smart! - Regenerate the OpenAI client from a pinned canonical specification. Fix composed schemas that collapsed to `Schema.Never` and preserve keys in maps and free-form objects. + + Public type changes in `Generated`: + + - Remove Realtime WebSocket event exports no longer referenced by HTTP operations. Rename multipart request bodies to `*Multipart` or `*FormData`. + - Require fields specified upstream, including `CreateImageRequest.model`, `AdminApiKey.expires_at`, `ProjectApiKey.owner_project_access`, `AssignedRoleDetails.assignment_sources` and `VoiceResource.type`. + - Type maps and free-form objects as records instead of `{}`. `Metadata` is now `{ readonly [x: string]: string } | null`. + - Make `FineTuningJob` a union of `dpo`, `reinforcement` and `hyperparameters` variants. Use `WebSearchCallStatus` for `WebSearchToolCall.status`. + +- [#9075](https://github.com/Effect-TS/effect/pull/9075) [`de38e53`](https://github.com/Effect-TS/effect/commit/de38e53651c8f6b36573ee038d0292e199e231b7) Thanks @tim-smart! - Accept OpenAI web search `find_in_page` actions without a URL while still requiring the search pattern. +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- [`43bb382`](https://github.com/Effect-TS/effect/commit/43bb382f68a62d759fca7b26f14a82bcaa49be0d) Thanks @tim-smart! - Preserve explicit `include` values and default unknown OpenAI models to reasoning. + + - Merge explicit and automatic includes without duplicates. + - Treat models as reasoning except for `gpt-3*`, `gpt-4*`, `chatgpt-*`, `chat-latest`, and `gpt--chat*`. Fine-tunes follow their base model. Reasoning models use the `developer` role and request encrypted reasoning when item references are disabled or WebSockets are used. OpenAI returns encrypted reasoning by default with `store: false`; the include is retained for compatibility. + - Add `reasoningModel` to override detection. Use `reasoningModel: false` for incompatible custom deployments to select the `system` role and disable the automatic encrypted reasoning include. Explicit includes are still preserved. +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/ai/openai/README.md b/packages/ai/openai/README.md similarity index 100% rename from repos/effect/packages/ai/openai/README.md rename to packages/ai/openai/README.md diff --git a/repos/effect/packages/ai/openai/codegen.yaml b/packages/ai/openai/codegen.yaml similarity index 77% rename from repos/effect/packages/ai/openai/codegen.yaml rename to packages/ai/openai/codegen.yaml index 4c9ed89bf..c8265791b 100644 --- a/repos/effect/packages/ai/openai/codegen.yaml +++ b/packages/ai/openai/codegen.yaml @@ -1,5 +1,7 @@ # yaml-language-server: $schema=../../tools/ai-codegen/codegen.schema.json -spec: https://app.stainless.com/api/spec/documented/openai/openapi.documented.yml +# To update, pin the latest openai/openai-openapi commit and regenerate from the repo root: +# nix develop -c node packages/tools/ai-codegen/src/bin.ts generate --provider openai +spec: https://raw.githubusercontent.com/openai/openai-openapi/e95c0fe615f45a19e8878af923a729948445a6bd/openapi.yaml output: src/Generated.ts name: OpenAiClient header: | @@ -8,20 +10,20 @@ header: | * @since 1.0.0 */ patches: - - '[{"op":"replace","path":"/openapi","value":"3.0.0"}]' - - '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/user/nullable","value":true}]' - - '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/safety_identifier/nullable","value":true}]' - - '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/prompt_cache_key/nullable","value":true}]' - - '[{"op":"add","path":"/components/schemas/Response/allOf/2/properties/usage/nullable","value":true}]' - - '[{"op":"remove","path":"/components/schemas/ResponseFunctionCallArgumentsDoneEvent/required/2"}]' - - '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/prompt_cache_retention/anyOf/0/enum/1","value":"in_memory"}]' + # Preserve the optional `name` field removed upstream. + - '[{"op":"add","path":"/components/schemas/ResponseFunctionCallArgumentsDoneEvent/properties/name","value":{"type":"string","description":"The name of the function that was called."}}]' + # Keep cache_write_tokens and access_programs optional for older and OpenAI-compatible servers. + - '[{"op":"replace","path":"/components/schemas/ResponseUsage/properties/input_tokens_details/required","value":["cached_tokens"]}]' + - '[{"op":"replace","path":"/components/schemas/Response/allOf/2/required","value":["id","object","created_at","error","incomplete_details","instructions","model","tools","output","parallel_tool_calls","metadata","tool_choice","temperature","top_p"]}]' + # Also accept the legacy `in-memory` spelling - '[{"op":"add","path":"/components/schemas/PromptCacheRetentionEnum/enum/1","value":"in-memory"}]' - '[{"op":"replace","path":"/components/schemas/OpenAIFile/properties/expires_at","value":{"anyOf":[{"type":"integer","format":"unixtime","description":"The Unix timestamp (in seconds) for when the file will expire."},{"type":"null"}]}}]' - - '[{"op":"add","path":"/components/schemas/OpenAIFile/properties/status_details/nullable","value":true}]' + - '[{"op":"replace","path":"/components/schemas/OpenAIFile/properties/status_details/type","value":["string","null"]}]' # Remove when the upstream schema includes named API sources: https://github.com/openai/openai-openapi/issues/573 - '[{"op":"replace","path":"/components/schemas/WebSearchActionSearch/properties/sources/items","value":{"anyOf":[{"type":"object","title":"Web search source","description":"A source used in the search.\n","properties":{"type":{"type":"string","enum":["url"],"description":"The type of source. Always `url`.\n","x-stainless-const":true},"url":{"type":"string","format":"uri","description":"The URL of the source.\n"}},"required":["type","url"]},{"type":"object","title":"Web search API source","description":"An API source used in the search.\n","properties":{"type":{"type":"string","enum":["api"],"description":"The type of source. Always `api`.\n","x-stainless-const":true},"name":{"type":"string","description":"The name of the API source, such as `oai-weather`, `oai-sports`, or `oai-finance`.\n"}},"required":["type","name"]}]}}]' - # OpenAI may omit action (https://github.com/openai/openai-openapi/issues/572) or report incomplete. - - '[{"op":"replace","path":"/components/schemas/WebSearchToolCall/required","value":["id","type","status"]},{"op":"replace","path":"/components/schemas/WebSearchToolCall/properties/status","value":{"type":"string","enum":["in_progress","searching","completed","failed","incomplete"],"description":"The status of the web search tool call.\n"}}]' + # OpenAI may omit the find-in-page URL: https://github.com/openai/openai-openapi/issues/571 + # Also tolerate omitted patterns without substituting a value. + - '[{"op":"replace","path":"/components/schemas/WebSearchActionFind/required","value":["type"]}]' # Add missing keepalive stream event support - '[{"op":"add","path":"/components/schemas/ResponseKeepAliveEvent","value":{"type":"object","title":"Keep alive","description":"A keepalive event emitted during long-running response streams.","properties":{"type":{"type":"string","enum":["keepalive"],"description":"The type of the keepalive event. Always `keepalive`.","default":"keepalive","x-stainless-const":true},"sequence_number":{"type":"integer","description":"The sequence number of this keepalive event."}},"required":["type","sequence_number"]}}]' - '[{"op":"add","path":"/components/schemas/ResponseStreamEvent/anyOf/-","value":{"$ref":"#/components/schemas/ResponseKeepAliveEvent"}}]' diff --git a/repos/effect/packages/ai/openai/package.json b/packages/ai/openai/package.json similarity index 98% rename from repos/effect/packages/ai/openai/package.json rename to packages/ai/openai/package.json index c041c153b..f9a91a928 100644 --- a/repos/effect/packages/ai/openai/package.json +++ b/packages/ai/openai/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openai", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "An OpenAI provider integration for Effect AI SDK", diff --git a/packages/ai/openai/src/Generated.ts b/packages/ai/openai/src/Generated.ts new file mode 100644 index 000000000..fc81ebddd --- /dev/null +++ b/packages/ai/openai/src/Generated.ts @@ -0,0 +1,69150 @@ +/** + * @stability unstable + * @since 1.0.0 + */ + +import * as Data from "effect/Data" +import * as Effect from "effect/Effect" +import * as Sse from "effect/encoding/Sse" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" +import type { SchemaError } from "effect/Schema" +import * as Schema from "effect/Schema" +import * as Stream from "effect/Stream" +// recursive declarations +export type CompoundFilter = { + readonly "type": "and" | "or" + readonly "filters": ReadonlyArray +} +export const CompoundFilter = Schema.suspend((): Schema.Codec => __recursive_CompoundFilter) +export type BetaCompoundFilter = { + readonly "type": "and" | "or" + readonly "filters": ReadonlyArray +} +export const BetaCompoundFilter = Schema.suspend((): Schema.Codec => __recursive_BetaCompoundFilter) +// non-recursive definitions +export type AssistantToolsCode = { readonly "type": "code_interpreter" } +export const AssistantToolsCode = Schema.Struct({ + "type": Schema.Literal("code_interpreter").annotate({ + "description": "The type of tool being defined: `code_interpreter`" + }) +}).annotate({ "title": "Code interpreter tool", "identifier": "AssistantToolsCode" }) +export type FileSearchRanker = "auto" | "default_2024_08_21" +export const FileSearchRanker = Schema.Literals(["auto", "default_2024_08_21"]).annotate({ + "description": "The ranker to use for the file search. If not specified will use the `auto` ranker.", + "identifier": "FileSearchRanker" +}) +export type FunctionParameters = { readonly [x: string]: Schema.Json } +export const FunctionParameters = Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + .annotate({ + "description": + "The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://developers.openai.com/api/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.\n\nOmitting `parameters` defines a function with an empty parameter list.", + "identifier": "FunctionParameters" + }) +export type Metadata = { readonly [x: string]: string } | null +export const Metadata = Schema.Union([ + Schema.Record(Schema.String, Schema.String).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.\n" + }), + Schema.Null +]).annotate({ "identifier": "Metadata" }) +export type ResponseFormatText = { readonly "type": "text" } +export const ResponseFormatText = Schema.Struct({ + "type": Schema.Literal("text").annotate({ + "description": "The type of response format being defined. Always `text`." + }) +}).annotate({ + "title": "Text", + "description": "Default response format. Used to generate text responses.\n", + "identifier": "ResponseFormatText" +}) +export type ResponseFormatJsonObject = { readonly "type": "json_object" } +export const ResponseFormatJsonObject = Schema.Struct({ + "type": Schema.Literal("json_object").annotate({ + "description": "The type of response format being defined. Always `json_object`." + }) +}).annotate({ + "title": "JSON object", + "description": + "JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n", + "identifier": "ResponseFormatJsonObject" +}) +export type ResponseFormatJsonSchemaSchema = { readonly [x: string]: Schema.Json } +export const ResponseFormatJsonSchemaSchema = Schema.Record( + Schema.String, + Schema.Json.annotate({ "expected": "JSON value" }) +).annotate({ + "title": "JSON schema", + "description": + "The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n", + "identifier": "ResponseFormatJsonSchemaSchema" +}) +export type _MisalignmentErrorType = + | string + | "potentially_unintended_data_transfer" + | "potentially_unintended_data_access" + | "potentially_unintended_destructive_activity" + | "other" +export const _MisalignmentErrorType = Schema.Union([ + Schema.String, + Schema.Literals([ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other" + ]) +]).annotate({ "identifier": "_MisalignmentErrorType" }) +export type _MisalignmentSteer = { readonly "message": string } +export const _MisalignmentSteer = Schema.Struct({ + "message": Schema.String.annotate({ "description": "The public continuation instruction." }) +}).annotate({ "identifier": "_MisalignmentSteer" }) +export type AssistantSupportedModels = + | "gpt-5" + | "gpt-5-mini" + | "gpt-5-nano" + | "gpt-5-2025-08-07" + | "gpt-5-mini-2025-08-07" + | "gpt-5-nano-2025-08-07" + | "gpt-4.1" + | "gpt-4.1-mini" + | "gpt-4.1-nano" + | "gpt-4.1-2025-04-14" + | "gpt-4.1-mini-2025-04-14" + | "gpt-4.1-nano-2025-04-14" + | "o3-mini" + | "o3-mini-2025-01-31" + | "o1" + | "o1-2024-12-17" + | "gpt-4o" + | "gpt-4o-2024-11-20" + | "gpt-4o-2024-08-06" + | "gpt-4o-2024-05-13" + | "gpt-4o-mini" + | "gpt-4o-mini-2024-07-18" + | "gpt-4.5-preview" + | "gpt-4.5-preview-2025-02-27" + | "gpt-4-turbo" + | "gpt-4-turbo-2024-04-09" + | "gpt-4-0125-preview" + | "gpt-4-turbo-preview" + | "gpt-4-1106-preview" + | "gpt-4-vision-preview" + | "gpt-4" + | "gpt-4-0314" + | "gpt-4-0613" + | "gpt-4-32k" + | "gpt-4-32k-0314" + | "gpt-4-32k-0613" + | "gpt-3.5-turbo" + | "gpt-3.5-turbo-16k" + | "gpt-3.5-turbo-0613" + | "gpt-3.5-turbo-1106" + | "gpt-3.5-turbo-0125" + | "gpt-3.5-turbo-16k-0613" +export const AssistantSupportedModels = Schema.Literals([ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613" +]).annotate({ "identifier": "AssistantSupportedModels" }) +export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null +export const ReasoningEffort = Schema.Union([ + Schema.Literals(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).annotate({ + "description": + "Constrains effort on reasoning for reasoning models. Currently supported\nvalues are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.\nReducing reasoning effort can result in faster responses and fewer tokens\nused on reasoning in a response. Not all reasoning models support every\nvalue. See the\n[reasoning guide](https://developers.openai.com/api/docs/guides/reasoning)\nfor model-specific support.\n" + }), + Schema.Null +]).annotate({ "identifier": "ReasoningEffort" }) +export type DeleteAssistantResponse = { + readonly "id": string + readonly "deleted": boolean + readonly "object": "assistant.deleted" +} +export const DeleteAssistantResponse = Schema.Struct({ + "id": Schema.String, + "deleted": Schema.Boolean, + "object": Schema.Literal("assistant.deleted") +}).annotate({ "identifier": "DeleteAssistantResponse" }) +export type VoiceIdsShared = + | string + | "alloy" + | "ash" + | "ballad" + | "coral" + | "echo" + | "sage" + | "shimmer" + | "verse" + | "marin" + | "cedar" +export const VoiceIdsShared = Schema.Union([ + Schema.String, + Schema.Literals(["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]) +]).annotate({ "identifier": "VoiceIdsShared" }) +export type SpeechAudioDeltaEvent = { readonly "type": "speech.audio.delta"; readonly "audio": string } +export const SpeechAudioDeltaEvent = Schema.Struct({ + "type": Schema.Literal("speech.audio.delta").annotate({ + "description": "The type of the event. Always `speech.audio.delta`.\n" + }), + "audio": Schema.String.annotate({ "description": "A chunk of Base64-encoded audio data.\n" }) +}).annotate({ + "description": "Emitted for each chunk of audio data generated during speech synthesis.", + "identifier": "SpeechAudioDeltaEvent" +}) +export type SpeechAudioDoneEvent = { + readonly "type": "speech.audio.done" + readonly "usage": { + readonly "input_tokens": number + readonly "output_tokens": number + readonly "total_tokens": number + } +} +export const SpeechAudioDoneEvent = Schema.Struct({ + "type": Schema.Literal("speech.audio.done").annotate({ + "description": "The type of the event. Always `speech.audio.done`.\n" + }), + "usage": Schema.Struct({ + "input_tokens": Schema.Number.annotate({ "description": "Number of input tokens in the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_tokens": Schema.Number.annotate({ "description": "Number of output tokens generated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "total_tokens": Schema.Number.annotate({ "description": "Total number of tokens used (input + output)." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "Token usage statistics for the request.\n" }) +}).annotate({ + "description": "Emitted when the speech synthesis is complete and all audio has been streamed.", + "identifier": "SpeechAudioDoneEvent" +}) +export type __ClientMultipartFile = globalThis.File | globalThis.Blob +export const __ClientMultipartFile = Schema.instanceOf(globalThis.Blob, { expected: "File | Blob" }) +export type AudioResponseFormat = "json" | "text" | "srt" | "verbose_json" | "vtt" | "diarized_json" +export const AudioResponseFormat = Schema.Literals(["json", "text", "srt", "verbose_json", "vtt", "diarized_json"]) + .annotate({ + "description": + "The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, the only supported format is `json`. For `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and `diarized_json`, with `diarized_json` required to receive speaker annotations.\n", + "identifier": "AudioResponseFormat" + }) +export type TranscriptionInclude = "logprobs" +export const TranscriptionInclude = Schema.Literal("logprobs").annotate({ "identifier": "TranscriptionInclude" }) +export type VadConfig = { + readonly "type": "server_vad" + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + readonly "threshold"?: number +} +export const VadConfig = Schema.Struct({ + "type": Schema.Literal("server_vad").annotate({ + "description": "Must be set to `server_vad` to enable manual chunking using server side VAD." + }), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Amount of audio to include before the VAD detected speech (in \nmilliseconds).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Duration of silence to detect speech stop (in milliseconds).\nWith shorter values the model will respond more quickly, \nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Sensitivity threshold (0.0 to 1.0) for voice activity detection. A \nhigher threshold will require louder audio to activate the model, and \nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ) +}).annotate({ "identifier": "VadConfig" }) +export type TranscriptionLanguage = { readonly "code": string } +export const TranscriptionLanguage = Schema.Struct({ + "code": Schema.String.annotate({ "description": "The code of a language detected in the audio." }) +}).annotate({ "description": "A language detected in transcribed audio.", "identifier": "TranscriptionLanguage" }) +export type TranscriptTextUsageTokens = { + readonly "type": "tokens" + readonly "input_tokens": number + readonly "input_token_details"?: { readonly "text_tokens"?: number; readonly "audio_tokens"?: number } + readonly "output_tokens": number + readonly "total_tokens": number +} +export const TranscriptTextUsageTokens = Schema.Struct({ + "type": Schema.Literal("tokens").annotate({ + "description": "The type of the usage object. Always `tokens` for this variant." + }), + "input_tokens": Schema.Number.annotate({ "description": "Number of input tokens billed for this request." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "input_token_details": Schema.optionalKey( + Schema.Struct({ + "text_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Number of text tokens billed for this request." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "audio_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Number of audio tokens billed for this request." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ) + }).annotate({ "description": "Details about the input tokens billed for this request." }) + ), + "output_tokens": Schema.Number.annotate({ "description": "Number of output tokens generated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "total_tokens": Schema.Number.annotate({ "description": "Total number of tokens used (input + output)." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Token Usage", + "description": "Usage statistics for models billed by token usage.", + "identifier": "TranscriptTextUsageTokens" +}) +export type TranscriptTextUsageDuration = { readonly "type": "duration"; readonly "seconds": number } +export const TranscriptTextUsageDuration = Schema.Struct({ + "type": Schema.Literal("duration").annotate({ + "description": "The type of the usage object. Always `duration` for this variant." + }), + "seconds": Schema.Number.annotate({ "description": "Duration of the input audio in seconds.", "format": "double" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })) +}).annotate({ + "title": "Duration Usage", + "description": "Usage statistics for models billed by audio input duration.", + "identifier": "TranscriptTextUsageDuration" +}) +export type TranscriptionDiarizedSegment = { + readonly "type": "transcript.text.segment" + readonly "id": string + readonly "start": number + readonly "end": number + readonly "text": string + readonly "speaker": string +} +export const TranscriptionDiarizedSegment = Schema.Struct({ + "type": Schema.Literal("transcript.text.segment").annotate({ + "description": "The type of the segment. Always `transcript.text.segment`.\n" + }), + "id": Schema.String.annotate({ "description": "Unique identifier for the segment." }), + "start": Schema.Number.annotate({ "description": "Start timestamp of the segment in seconds.", "format": "double" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "end": Schema.Number.annotate({ "description": "End timestamp of the segment in seconds.", "format": "double" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "text": Schema.String.annotate({ "description": "Transcript text for this segment." }), + "speaker": Schema.String.annotate({ + "description": + "Speaker label for this segment. When known speakers are provided, the label matches `known_speaker_names[]`. Otherwise speakers are labeled sequentially using capital letters (`A`, `B`, ...).\n" + }) +}).annotate({ + "description": "A segment of diarized transcript text with speaker metadata.", + "identifier": "TranscriptionDiarizedSegment" +}) +export type TranscriptionWord = { readonly "word": string; readonly "start": number; readonly "end": number } +export const TranscriptionWord = Schema.Struct({ + "word": Schema.String.annotate({ "description": "The text content of the word." }), + "start": Schema.Number.annotate({ "description": "Start time of the word in seconds.", "format": "double" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "end": Schema.Number.annotate({ "description": "End time of the word in seconds.", "format": "double" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) +}).annotate({ "identifier": "TranscriptionWord" }) +export type TranscriptionSegment = { + readonly "id": number + readonly "seek": number + readonly "start": number + readonly "end": number + readonly "text": string + readonly "tokens": ReadonlyArray + readonly "temperature": number + readonly "avg_logprob": number + readonly "compression_ratio": number + readonly "no_speech_prob": number +} +export const TranscriptionSegment = Schema.Struct({ + "id": Schema.Number.annotate({ "description": "Unique identifier of the segment." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "seek": Schema.Number.annotate({ "description": "Seek offset of the segment." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "start": Schema.Number.annotate({ "description": "Start time of the segment in seconds.", "format": "double" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "end": Schema.Number.annotate({ "description": "End time of the segment in seconds.", "format": "double" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "text": Schema.String.annotate({ "description": "Text content of the segment." }), + "tokens": Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).annotate({ + "description": "Array of token IDs for the text content." + }), + "temperature": Schema.Number.annotate({ + "description": "Temperature parameter used for generating the segment.", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "avg_logprob": Schema.Number.annotate({ + "description": "Average logprob of the segment. If the value is lower than -1, consider the logprobs failed.", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "compression_ratio": Schema.Number.annotate({ + "description": + "Compression ratio of the segment. If the value is greater than 2.4, consider the compression failed.", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "no_speech_prob": Schema.Number.annotate({ + "description": + "Probability of no speech in the segment. If the value is higher than 1.0 and the `avg_logprob` is below -1, consider this segment silent.", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) +}).annotate({ "identifier": "TranscriptionSegment" }) +export type TranscriptTextSegmentEvent = { + readonly "type": "transcript.text.segment" + readonly "id": string + readonly "start": number + readonly "end": number + readonly "text": string + readonly "speaker": string +} +export const TranscriptTextSegmentEvent = Schema.Struct({ + "type": Schema.Literal("transcript.text.segment").annotate({ + "description": "The type of the event. Always `transcript.text.segment`." + }), + "id": Schema.String.annotate({ "description": "Unique identifier for the segment." }), + "start": Schema.Number.annotate({ "description": "Start timestamp of the segment in seconds.", "format": "double" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "end": Schema.Number.annotate({ "description": "End timestamp of the segment in seconds.", "format": "double" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "text": Schema.String.annotate({ "description": "Transcript text for this segment." }), + "speaker": Schema.String.annotate({ "description": "Speaker label for this segment." }) +}).annotate({ + "description": + "Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with `stream` set to `true` and `response_format` set to `diarized_json`.\n", + "identifier": "TranscriptTextSegmentEvent" +}) +export type TranscriptTextDeltaEvent = { + readonly "type": "transcript.text.delta" + readonly "delta": string + readonly "logprobs"?: ReadonlyArray< + { readonly "token"?: string; readonly "logprob"?: number; readonly "bytes"?: ReadonlyArray } + > + readonly "segment_id"?: string +} +export const TranscriptTextDeltaEvent = Schema.Struct({ + "type": Schema.Literal("transcript.text.delta").annotate({ + "description": "The type of the event. Always `transcript.text.delta`.\n" + }), + "delta": Schema.String.annotate({ "description": "The text delta that was additionally transcribed.\n" }), + "logprobs": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "token": Schema.optionalKey( + Schema.String.annotate({ "description": "The token that was used to generate the log probability.\n" }) + ), + "logprob": Schema.optionalKey( + Schema.Number.annotate({ "description": "The log probability of the token.\n" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ), + "bytes": Schema.optionalKey( + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).annotate({ + "description": "The bytes that were used to generate the log probability.\n" + }) + ) + })).annotate({ + "description": + "The log probabilities of the delta. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.\n" + }) + ), + "segment_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "Identifier of the diarized segment that this delta belongs to. Only present when using `gpt-4o-transcribe-diarize`.\n" + }) + ) +}).annotate({ + "description": + "Emitted when there is an additional text delta. This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.", + "identifier": "TranscriptTextDeltaEvent" +}) +export type CreateTranslationResponseJson = { readonly "text": string } +export const CreateTranslationResponseJson = Schema.Struct({ "text": Schema.String }).annotate({ + "identifier": "CreateTranslationResponseJson" +}) +export type VoiceConsentResource = { + readonly "object": "audio.voice_consent" + readonly "id": string + readonly "name": string + readonly "language": string + readonly "created_at": number +} +export const VoiceConsentResource = Schema.Struct({ + "object": Schema.Literal("audio.voice_consent").annotate({ + "description": "The object type, which is always `audio.voice_consent`." + }), + "id": Schema.String.annotate({ "description": "The consent recording identifier." }), + "name": Schema.String.annotate({ "description": "The label provided when the consent recording was uploaded." }), + "language": Schema.String.annotate({ + "description": "The BCP 47 language tag for the consent phrase (for example, `en-US`)." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the consent recording was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "Voice consent", + "description": "A consent recording used to authorize creation of a custom voice.", + "identifier": "VoiceConsentResource" +}) +export type UpdateVoiceConsentRequest = { readonly "name": string } +export const UpdateVoiceConsentRequest = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The updated label for this consent recording." }) +}).annotate({ "identifier": "UpdateVoiceConsentRequest" }) +export type VoiceConsentDeletedResource = { + readonly "id": string + readonly "object": "audio.voice_consent" + readonly "deleted": boolean +} +export const VoiceConsentDeletedResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The consent recording identifier." }), + "object": Schema.Literal("audio.voice_consent"), + "deleted": Schema.Boolean +}).annotate({ "identifier": "VoiceConsentDeletedResource" }) +export type VoiceResource = { + readonly "object": "audio.voice" + readonly "id": string + readonly "type": "audio_sample" + readonly "name": string + readonly "created_at": number +} +export const VoiceResource = Schema.Struct({ + "object": Schema.Literal("audio.voice").annotate({ + "description": "The object type, which is always `audio.voice`." + }), + "id": Schema.String.annotate({ "description": "The voice identifier, which can be referenced in API endpoints." }), + "type": Schema.Literal("audio_sample").annotate({ "description": "How the voice was created." }), + "name": Schema.String.annotate({ "description": "The name of the voice." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the voice was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "Voice", + "description": "A custom voice that can be used for audio output.", + "identifier": "VoiceResource" +}) +export type BatchError = { + readonly "code"?: string + readonly "message"?: string + readonly "param"?: string | null + readonly "line"?: number | null +} +export const BatchError = Schema.Struct({ + "code": Schema.optionalKey(Schema.String.annotate({ "description": "An error code identifying the error type." })), + "message": Schema.optionalKey( + Schema.String.annotate({ "description": "A human-readable message providing more details about the error." }) + ), + "param": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the parameter that caused the error, if applicable." }), + Schema.Null + ]) + ), + "line": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The line number of the input file where the error occurred, if applicable." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BatchError" }) +export type BatchRequestCounts = { readonly "total": number; readonly "completed": number; readonly "failed": number } +export const BatchRequestCounts = Schema.Struct({ + "total": Schema.Number.annotate({ "description": "Total number of requests in the batch." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "completed": Schema.Number.annotate({ "description": "Number of requests that have been completed successfully." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "failed": Schema.Number.annotate({ "description": "Number of requests that have failed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "The request counts for different statuses within the batch.", + "identifier": "BatchRequestCounts" +}) +export type BatchFileExpirationAfter = { readonly "anchor": "created_at"; readonly "seconds": number } +export const BatchFileExpirationAfter = Schema.Struct({ + "anchor": Schema.Literal("created_at").annotate({ + "description": + "Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`. Note that the anchor is the file creation time, not the time the batch is created." + }), + "seconds": Schema.Number.annotate({ + "description": + "The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(3600).annotate({ "expected": "a value greater than or equal to 3600" }) + ).check(Schema.isLessThanOrEqualTo(2592000).annotate({ "expected": "a value less than or equal to 2592000" })) +}).annotate({ + "title": "File expiration policy", + "description": "The expiration policy for the output and/or error file that are generated for a batch.", + "identifier": "BatchFileExpirationAfter" +}) +export type ChatCompletionMessageToolCall = { + readonly "id": string + readonly "type": "function" + readonly "function": { readonly "name": string; readonly "arguments": string } +} +export const ChatCompletionMessageToolCall = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the tool call." }), + "type": Schema.Literal("function").annotate({ + "description": "The type of the tool. Currently, only `function` is supported." + }), + "function": Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the function to call." }), + "arguments": Schema.String.annotate({ + "description": + "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + }) + }).annotate({ "description": "The function that the model called." }) +}).annotate({ + "title": "Function tool call", + "description": "A call to a function tool created by the model.\n", + "identifier": "ChatCompletionMessageToolCall" +}) +export type ChatCompletionMessageCustomToolCall = { + readonly "id": string + readonly "type": "custom" + readonly "custom": { readonly "name": string; readonly "input": string } +} +export const ChatCompletionMessageCustomToolCall = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the tool call." }), + "type": Schema.Literal("custom").annotate({ "description": "The type of the tool. Always `custom`." }), + "custom": Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the custom tool to call." }), + "input": Schema.String.annotate({ "description": "The input for the custom tool call generated by the model." }) + }).annotate({ "description": "The custom tool that the model called." }) +}).annotate({ + "title": "Custom tool call", + "description": "A call to a custom tool created by the model.\n", + "identifier": "ChatCompletionMessageCustomToolCall" +}) +export type ChatCompletionTokenLogprob = { + readonly "token": string + readonly "logprob": number + readonly "bytes": ReadonlyArray | null + readonly "top_logprobs": ReadonlyArray< + { readonly "token": string; readonly "logprob": number; readonly "bytes": ReadonlyArray | null } + > +} +export const ChatCompletionTokenLogprob = Schema.Struct({ + "token": Schema.String.annotate({ "description": "The token." }), + "logprob": Schema.Number.annotate({ + "description": + "The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "bytes": Schema.Union([ + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).annotate({ + "description": + "A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token." + }), + Schema.Null + ]), + "top_logprobs": Schema.Array(Schema.Struct({ + "token": Schema.String.annotate({ "description": "The token." }), + "logprob": Schema.Number.annotate({ + "description": + "The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "bytes": Schema.Union([ + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).annotate({ + "description": + "A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token." + }), + Schema.Null + ]) + })).annotate({ + "description": + "List of the most likely tokens and their log probability, at this token position. The number of entries may be fewer than the requested `top_logprobs`." + }) +}).annotate({ "identifier": "ChatCompletionTokenLogprob" }) +export type ServiceTier = "auto" | "default" | "flex" | "scale" | "priority" | "fast" | null +export const ServiceTier = Schema.Union([ + Schema.Literals(["auto", "default", "flex", "scale", "priority", "fast"]).annotate({ + "description": + "Specifies the processing type used for serving the request.\n - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.\n - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.\n - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.\n - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.\n - When not set, the default behavior is 'auto'.\n\n When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.\n" + }), + Schema.Null +]).annotate({ "identifier": "ServiceTier" }) +export type CompletionUsage = { + readonly "completion_tokens": number + readonly "prompt_tokens": number + readonly "total_tokens": number + readonly "completion_tokens_details"?: { + readonly "accepted_prediction_tokens"?: number + readonly "audio_tokens"?: number + readonly "reasoning_tokens"?: number + readonly "text_tokens"?: number + readonly "rejected_prediction_tokens"?: number + } + readonly "prompt_tokens_details"?: { + readonly "audio_tokens"?: number + readonly "cached_tokens"?: number + readonly "text_tokens"?: number + readonly "image_tokens"?: number + readonly "cache_write_tokens"?: number + } +} +export const CompletionUsage = Schema.Struct({ + "completion_tokens": Schema.Number.annotate({ "description": "Number of tokens in the generated completion." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "prompt_tokens": Schema.Number.annotate({ "description": "Number of tokens in the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "total_tokens": Schema.Number.annotate({ + "description": "Total number of tokens used in the request (prompt + completion)." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "completion_tokens_details": Schema.optionalKey( + Schema.Struct({ + "accepted_prediction_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "When using Predicted Outputs, the number of tokens in the\nprediction that appeared in the completion.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "audio_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Audio input tokens generated by the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "reasoning_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Tokens generated by the model for reasoning." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "text_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Text output tokens generated by the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "rejected_prediction_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "When using Predicted Outputs, the number of tokens in the\nprediction that did not appear in the completion. However, like\nreasoning tokens, these tokens are still counted in the total\ncompletion tokens for purposes of billing, output, and context window\nlimits.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ "description": "Breakdown of tokens used in a completion." }) + ), + "prompt_tokens_details": Schema.optionalKey( + Schema.Struct({ + "audio_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Audio input tokens present in the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "cached_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Cached tokens present in the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "text_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Text input tokens present in the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "image_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "Image input tokens present in the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "cache_write_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The unadjusted number of prompt tokens written to cache." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ) + }).annotate({ "description": "Breakdown of tokens used in the prompt." }) + ) +}).annotate({ "description": "Usage statistics for the completion request.", "identifier": "CompletionUsage" }) +export type ModerationInputType = "text" | "image" +export const ModerationInputType = Schema.Literals(["text", "image"]).annotate({ "identifier": "ModerationInputType" }) +export type ChatCompletionModerationError = { + readonly "type": "error" + readonly "code": string + readonly "message": string +} +export const ChatCompletionModerationError = Schema.Struct({ + "type": Schema.Literal("error").annotate({ "description": "The object type, which is always `error`." }), + "code": Schema.String.annotate({ "description": "The error code." }), + "message": Schema.String.annotate({ "description": "The error message." }) +}).annotate({ + "description": "An error produced while attempting moderation.", + "identifier": "ChatCompletionModerationError" +}) +export type PromptCacheTTLEnum = "30m" +export const PromptCacheTTLEnum = Schema.Literal("30m").annotate({ "identifier": "PromptCacheTTLEnum" }) +export type PromptCacheModeEnum = "implicit" | "explicit" +export const PromptCacheModeEnum = Schema.Literals(["implicit", "explicit"]).annotate({ + "identifier": "PromptCacheModeEnum" +}) +export type PromptCacheBreakpointParam = { readonly "mode": "explicit" } +export const PromptCacheBreakpointParam = Schema.Struct({ + "mode": Schema.Literal("explicit").annotate({ "description": "The breakpoint mode. Always `explicit`." }) +}).annotate({ + "title": "Prompt cache breakpoint", + "description": + "Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.", + "identifier": "PromptCacheBreakpointParam" +}) +export type ChatCompletionRequestMessageContentPartRefusal = { readonly "type": "refusal"; readonly "refusal": string } +export const ChatCompletionRequestMessageContentPartRefusal = Schema.Struct({ + "type": Schema.Literal("refusal").annotate({ "description": "The type of the content part." }), + "refusal": Schema.String.annotate({ "description": "The refusal message generated by the model." }) +}).annotate({ "title": "Refusal content part", "identifier": "ChatCompletionRequestMessageContentPartRefusal" }) +export type ChatCompletionRequestFunctionMessage = { + readonly "role": "function" + readonly "content": string | null + readonly "name": string +} +export const ChatCompletionRequestFunctionMessage = Schema.Struct({ + "role": Schema.Literal("function").annotate({ + "description": "The role of the messages author, in this case `function`." + }), + "content": Schema.Union([ + Schema.String.annotate({ "description": "The contents of the function message." }), + Schema.Null + ]), + "name": Schema.String.annotate({ "description": "The name of the function to call." }) +}).annotate({ "title": "Function message", "identifier": "ChatCompletionRequestFunctionMessage" }) +export type ModelIdsShared = + | string + | "gpt-6-astra" + | "gpt-6.1-sol" + | "gpt-6-sol" + | "gpt-6-luna" + | "gpt-5.6-sol" + | "gpt-5.6-terra" + | "gpt-5.6-luna" + | "gpt-5.5" + | "gpt-5.5-2026-04-23" + | "gpt-5.4" + | "gpt-5.4-mini" + | "gpt-5.4-nano" + | "gpt-5.4-mini-2026-03-17" + | "gpt-5.4-nano-2026-03-17" + | "gpt-5.3-chat-latest" + | "gpt-5.2" + | "gpt-5.2-2025-12-11" + | "gpt-5.2-chat-latest" + | "gpt-5.2-pro" + | "gpt-5.2-pro-2025-12-11" + | "gpt-5.1" + | "gpt-5.1-2025-11-13" + | "gpt-5.1-codex" + | "gpt-5.1-mini" + | "gpt-5.1-chat-latest" + | "gpt-5" + | "gpt-5-mini" + | "gpt-5-nano" + | "gpt-5-2025-08-07" + | "gpt-5-mini-2025-08-07" + | "gpt-5-nano-2025-08-07" + | "gpt-5-chat-latest" + | "gpt-4.1" + | "gpt-4.1-mini" + | "gpt-4.1-nano" + | "gpt-4.1-2025-04-14" + | "gpt-4.1-mini-2025-04-14" + | "gpt-4.1-nano-2025-04-14" + | "o4-mini" + | "o4-mini-2025-04-16" + | "o3" + | "o3-2025-04-16" + | "o3-mini" + | "o3-mini-2025-01-31" + | "o1" + | "o1-2024-12-17" + | "o1-preview" + | "o1-preview-2024-09-12" + | "o1-mini" + | "o1-mini-2024-09-12" + | "gpt-4o" + | "gpt-4o-2024-11-20" + | "gpt-4o-2024-08-06" + | "gpt-4o-2024-05-13" + | "gpt-audio-mini" + | "gpt-audio-mini-2025-12-15" + | "gpt-4o-audio-preview" + | "gpt-4o-audio-preview-2024-10-01" + | "gpt-4o-audio-preview-2024-12-17" + | "gpt-4o-audio-preview-2025-06-03" + | "gpt-4o-mini-audio-preview" + | "gpt-4o-mini-audio-preview-2024-12-17" + | "gpt-4o-search-preview" + | "gpt-4o-mini-search-preview" + | "gpt-4o-search-preview-2025-03-11" + | "gpt-4o-mini-search-preview-2025-03-11" + | "chatgpt-4o-latest" + | "codex-mini-latest" + | "gpt-4o-mini" + | "gpt-4o-mini-2024-07-18" + | "gpt-4-turbo" + | "gpt-4-turbo-2024-04-09" + | "gpt-4-0125-preview" + | "gpt-4-turbo-preview" + | "gpt-4-1106-preview" + | "gpt-4-vision-preview" + | "gpt-4" + | "gpt-4-0314" + | "gpt-4-0613" + | "gpt-4-32k" + | "gpt-4-32k-0314" + | "gpt-4-32k-0613" + | "gpt-3.5-turbo" + | "gpt-3.5-turbo-16k" + | "gpt-3.5-turbo-0301" + | "gpt-3.5-turbo-0613" + | "gpt-3.5-turbo-1106" + | "gpt-3.5-turbo-0125" + | "gpt-3.5-turbo-16k-0613" +export const ModelIdsShared = Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-6-astra", + "gpt-6.1-sol", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613" + ]) +]).annotate({ "identifier": "ModelIdsShared" }) +export type ResponseModalities = ReadonlyArray<"text" | "audio"> | null +export const ResponseModalities = Schema.Union([ + Schema.Array(Schema.Literals(["text", "audio"])).annotate({ + "description": + "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to\n[generate audio](https://developers.openai.com/api/docs/guides/audio). To request that this model generate\nboth text and audio responses, you can use:\n\n`[\"text\", \"audio\"]`\n" + }), + Schema.Null +]).annotate({ "identifier": "ResponseModalities" }) +export type Verbosity = "low" | "medium" | "high" | null +export const Verbosity = Schema.Union([ + Schema.Literals(["low", "medium", "high"]).annotate({ + "description": + "Constrains the verbosity of the model's response. Lower values will result in\nmore concise responses, while higher values will result in more verbose responses.\nCurrently supported values are `low`, `medium`, and `high`. The default is\n`medium`.\n" + }), + Schema.Null +]).annotate({ "identifier": "Verbosity" }) +export type WebSearchLocation = { + readonly "country"?: string + readonly "region"?: string + readonly "city"?: string + readonly "timezone"?: string +} +export const WebSearchLocation = Schema.Struct({ + "country": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The two-letter \n[ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user,\ne.g. `US`.\n" + }) + ), + "region": Schema.optionalKey( + Schema.String.annotate({ "description": "Free text input for the region of the user, e.g. `California`.\n" }) + ), + "city": Schema.optionalKey( + Schema.String.annotate({ "description": "Free text input for the city of the user, e.g. `San Francisco`.\n" }) + ), + "timezone": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) \nof the user, e.g. `America/Los_Angeles`.\n" + }) + ) +}).annotate({ + "title": "Web search location", + "description": "Approximate location parameters for the search.", + "identifier": "WebSearchLocation" +}) +export type WebSearchContextSize = "low" | "medium" | "high" +export const WebSearchContextSize = Schema.Literals(["low", "medium", "high"]).annotate({ + "description": + "High level guidance for the amount of context window space to use for the \nsearch. One of `low`, `medium`, or `high`. `medium` is the default.\n", + "identifier": "WebSearchContextSize" +}) +export type ModerationMode = "score" | "block" +export const ModerationMode = Schema.Literals(["score", "block"]).annotate({ "identifier": "ModerationMode" }) +export type StopConfiguration = string | ReadonlyArray | null +export const StopConfiguration = Schema.Union([ + Schema.String, + Schema.Array(Schema.String).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxLength(4).annotate({ "expected": "a value with a length of at most 4" })), + Schema.Null +], { mode: "oneOf" }).annotate({ + "description": + "Not supported with latest reasoning models `o3` and `o4-mini`.\n\nUp to 4 sequences where the API will stop generating further tokens. The\nreturned text will not contain the stop sequence.\n", + "identifier": "StopConfiguration" +}) +export type ChatCompletionStreamOptions = { + readonly "include_usage"?: boolean + readonly "include_obfuscation"?: boolean +} | null +export const ChatCompletionStreamOptions = Schema.Union([ + Schema.Struct({ + "include_usage": Schema.optionalKey(Schema.Boolean.annotate({ + "description": + "If set, an additional chunk will be streamed before the `data: [DONE]`\nmessage. The `usage` field on this chunk shows the token usage statistics\nfor the entire request, and the `choices` field will always be an empty\narray.\n\nAll other chunks will also include a `usage` field, but with a null\nvalue. **NOTE:** If the stream is interrupted, you may not receive the\nfinal usage chunk which contains the total token usage for the request.\n" + })), + "include_obfuscation": Schema.optionalKey(Schema.Boolean.annotate({ + "description": + "When true, stream obfuscation will be enabled. Stream obfuscation adds\nrandom characters to an `obfuscation` field on streaming delta events to\nnormalize payload sizes as a mitigation to certain side-channel attacks.\nThese obfuscation fields are included by default, but add a small amount\nof overhead to the data stream. You can set `include_obfuscation` to\nfalse to optimize for bandwidth if you trust the network links between\nyour application and the OpenAI API.\n" + })) + }).annotate({ "description": "Options for streaming response. Only set this when you set `stream: true`.\n" }), + Schema.Null +]).annotate({ "identifier": "ChatCompletionStreamOptions" }) +export type CustomToolChatCompletions = { + readonly "type": "custom" + readonly "custom": { + readonly "name": string + readonly "description"?: string + readonly "format"?: { readonly "type": "text" } | { + readonly "type": "grammar" + readonly "grammar": { readonly "definition": string; readonly "syntax": "lark" | "regex" } + } + } +} +export const CustomToolChatCompletions = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "The type of the custom tool. Always `custom`." }), + "custom": Schema.Struct({ + "name": Schema.String.annotate({ + "description": "The name of the custom tool, used to identify it in tool calls." + }), + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": "Optional description of the custom tool, used to provide more context.\n" + }) + ), + "format": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "Unconstrained text format. Always `text`." }) + }).annotate({ "title": "Text format", "description": "Unconstrained free-form text." }), + Schema.Struct({ + "type": Schema.Literal("grammar").annotate({ "description": "Grammar format. Always `grammar`." }), + "grammar": Schema.Struct({ + "definition": Schema.String.annotate({ "description": "The grammar definition." }), + "syntax": Schema.Literals(["lark", "regex"]).annotate({ + "description": "The syntax of the grammar definition. One of `lark` or `regex`." + }) + }).annotate({ "title": "Grammar format", "description": "Your chosen grammar." }) + }).annotate({ "title": "Grammar format", "description": "A grammar defined by the user." }) + ], { mode: "oneOf" }).annotate({ + "description": "The input format for the custom tool. Default is unconstrained text.\n" + }) + ) + }).annotate({ "title": "Custom tool properties", "description": "Properties of the custom tool.\n" }) +}).annotate({ + "title": "Custom tool", + "description": "A custom tool that processes input using a specified format.\n", + "identifier": "CustomToolChatCompletions" +}) +export type ChatCompletionAllowedTools = { + readonly "mode": "auto" | "required" + readonly "tools": ReadonlyArray<{ readonly [x: string]: Schema.Json }> +} +export const ChatCompletionAllowedTools = Schema.Struct({ + "mode": Schema.Literals(["auto", "required"]).annotate({ + "description": + "Constrains the tools available to the model to a pre-defined set.\n\n`auto` allows the model to pick from among the allowed tools and generate a\nmessage.\n\n`required` requires the model to call one or more of the allowed tools.\n" + }), + "tools": Schema.Array( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A tool definition that the model should be allowed to call.\n" + }) + ).annotate({ + "description": + "A list of tool definitions that the model should be allowed to call.\n\nFor the Chat Completions API, the list of tool definitions might look like:\n```json\n[\n { \"type\": \"function\", \"function\": { \"name\": \"get_weather\" } },\n { \"type\": \"function\", \"function\": { \"name\": \"get_time\" } }\n]\n```\n" + }) +}).annotate({ + "title": "Allowed tools", + "description": "Constrains the tools available to the model to a pre-defined set.\n", + "identifier": "ChatCompletionAllowedTools" +}) +export type ChatCompletionNamedToolChoice = { + readonly "type": "function" + readonly "function": { readonly "name": string } +} +export const ChatCompletionNamedToolChoice = Schema.Struct({ + "type": Schema.Literal("function").annotate({ + "description": "For function calling, the type is always `function`." + }), + "function": Schema.Struct({ "name": Schema.String.annotate({ "description": "The name of the function to call." }) }) +}).annotate({ + "title": "Function tool choice", + "description": "Specifies a tool the model should use. Use to force the model to call a specific function.", + "identifier": "ChatCompletionNamedToolChoice" +}) +export type ChatCompletionNamedToolChoiceCustom = { + readonly "type": "custom" + readonly "custom": { readonly "name": string } +} +export const ChatCompletionNamedToolChoiceCustom = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "For custom tool calling, the type is always `custom`." }), + "custom": Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the custom tool to call." }) + }) +}).annotate({ + "title": "Custom tool choice", + "description": "Specifies a tool the model should use. Use to force the model to call a specific custom tool.", + "identifier": "ChatCompletionNamedToolChoiceCustom" +}) +export type ParallelToolCalls = boolean +export const ParallelToolCalls = Schema.Boolean.annotate({ + "description": + "Whether to enable [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) during tool use.", + "identifier": "ParallelToolCalls" +}) +export type ChatCompletionFunctionCallOption = { readonly "name": string } +export const ChatCompletionFunctionCallOption = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the function to call." }) +}).annotate({ + "description": + "Specifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function.\n", + "identifier": "ChatCompletionFunctionCallOption" +}) +export type ChatCompletionMessageToolCallChunk = { + readonly "index": number + readonly "id"?: string + readonly "type"?: "function" + readonly "function"?: { readonly "name"?: string; readonly "arguments"?: string } +} +export const ChatCompletionMessageToolCallChunk = Schema.Struct({ + "index": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the tool call." })), + "type": Schema.optionalKey( + Schema.Literal("function").annotate({ + "description": "The type of the tool. Currently, only `function` is supported." + }) + ), + "function": Schema.optionalKey(Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the function to call." })), + "arguments": Schema.optionalKey(Schema.String.annotate({ + "description": + "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + })) + })) +}).annotate({ "identifier": "ChatCompletionMessageToolCallChunk" }) +export type ChatCompletionDeleted = { + readonly "object": "chat.completion.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const ChatCompletionDeleted = Schema.Struct({ + "object": Schema.Literal("chat.completion.deleted").annotate({ "description": "The type of object being deleted." }), + "id": Schema.String.annotate({ "description": "The ID of the chat completion that was deleted." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the chat completion was deleted." }) +}).annotate({ "identifier": "ChatCompletionDeleted" }) +export type ChatCompletionMessageList = { + readonly "object": "list" + readonly "data": ReadonlyArray< + { + readonly "id": string + readonly "role": "user" | "assistant" | "tool" | "system" | "function" | "developer" + readonly "content": string | null + readonly "content_parts": + | ReadonlyArray< + { + readonly "type": "text" | "image_url" | "input_audio" | "file" + readonly "text"?: string | null + readonly "image_url"?: { readonly "url": string; readonly "detail"?: string | null } | null + readonly "input_audio"?: { readonly "data": string; readonly "format": string } | null + readonly "file"?: { + readonly "file_data"?: string | null + readonly "filename"?: string | null + readonly "file_id"?: string | null + } | null + } + > + | null + readonly "name"?: string | null + } + > + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ChatCompletionMessageList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ + "description": "The type of this object. It is always set to \"list\".\n" + }), + "data": Schema.Array(Schema.Struct({ + "id": Schema.String, + "role": Schema.Literals(["user", "assistant", "tool", "system", "function", "developer"]), + "content": Schema.Union([Schema.String, Schema.Null]), + "content_parts": Schema.Union([ + Schema.Array(Schema.Struct({ + "type": Schema.Literals(["text", "image_url", "input_audio", "file"]), + "text": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "image_url": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "url": Schema.String, + "detail": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) + }), + Schema.Null + ]) + ), + "input_audio": Schema.optionalKey( + Schema.Union([Schema.Struct({ "data": Schema.String, "format": Schema.String }), Schema.Null]) + ), + "file": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "file_data": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "filename": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "file_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) + }), + Schema.Null + ]) + ) + })), + Schema.Null + ]), + "name": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) + })).annotate({ "description": "An array of chat completion message objects.\n" }), + "first_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The identifier of the first chat message in the data array." + }), + "last_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The identifier of the last chat message in the data array." + }), + "has_more": Schema.Boolean.annotate({ "description": "Indicates whether there are more chat messages available." }) +}).annotate({ + "title": "ChatCompletionMessageList", + "description": "An object representing a list of chat completion messages.\n", + "identifier": "ChatCompletionMessageList" +}) +export type ContainerResource = { + readonly "id": string + readonly "object": string + readonly "name": string + readonly "created_at": number + readonly "status": string + readonly "last_active_at"?: number + readonly "expires_after"?: { readonly "anchor"?: "last_active_at"; readonly "minutes"?: number } + readonly "memory_limit"?: "1g" | "4g" | "16g" | "64g" + readonly "network_policy"?: { + readonly "type": "allowlist" | "disabled" + readonly "allowed_domains"?: ReadonlyArray + } +} +export const ContainerResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Unique identifier for the container." }), + "object": Schema.String.annotate({ "description": "The type of this object." }), + "name": Schema.String.annotate({ "description": "Name of the container." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the container was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "status": Schema.String.annotate({ "description": "Status of the container (e.g., active, deleted)." }), + "last_active_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the container was last active.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "expires_after": Schema.optionalKey( + Schema.Struct({ + "anchor": Schema.optionalKey( + Schema.Literal("last_active_at").annotate({ "description": "The reference point for the expiration." }) + ), + "minutes": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The number of minutes after the anchor before the container expires." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ + "description": + "The container will expire after this time period.\nThe anchor is the reference point for the expiration.\nThe minutes is the number of minutes after the anchor before the container expires.\n" + }) + ), + "memory_limit": Schema.optionalKey( + Schema.Literals(["1g", "4g", "16g", "64g"]).annotate({ + "description": "The memory limit configured for the container." + }) + ), + "network_policy": Schema.optionalKey( + Schema.Struct({ + "type": Schema.Literals(["allowlist", "disabled"]).annotate({ "description": "The network policy mode." }), + "allowed_domains": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "description": "Allowed outbound domains when `type` is `allowlist`." }) + ) + }).annotate({ "description": "Network access policy for the container." }) + ) +}).annotate({ "title": "The container object", "identifier": "ContainerResource" }) +export type SkillReferenceParam = { + readonly "type": "skill_reference" + readonly "skill_id": string + readonly "version"?: string +} +export const SkillReferenceParam = Schema.Struct({ + "type": Schema.Literal("skill_reference").annotate({ + "description": "References a skill created with the /v1/skills endpoint." + }), + "skill_id": Schema.String.annotate({ "description": "The ID of the referenced skill." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "version": Schema.optionalKey( + Schema.String.annotate({ + "description": "Optional skill version. Use a positive integer or 'latest'. Omit for default." + }) + ) +}).annotate({ "identifier": "SkillReferenceParam" }) +export type InlineSkillSourceParam = { + readonly "type": "base64" + readonly "media_type": "application/zip" + readonly "data": string +} +export const InlineSkillSourceParam = Schema.Struct({ + "type": Schema.Literal("base64").annotate({ + "description": "The type of the inline skill source. Must be `base64`." + }), + "media_type": Schema.Literal("application/zip").annotate({ + "description": "The media type of the inline skill payload. Must be `application/zip`." + }), + "data": Schema.String.annotate({ "description": "Base64-encoded skill zip bundle." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(70254592).annotate({ "expected": "a string with at most 70254592 code points" })) +}).annotate({ "description": "Inline skill payload", "identifier": "InlineSkillSourceParam" }) +export type ContainerNetworkPolicyDisabledParam = { readonly "type": "disabled" } +export const ContainerNetworkPolicyDisabledParam = Schema.Struct({ + "type": Schema.Literal("disabled").annotate({ "description": "Disable outbound network access. Always `disabled`." }) +}).annotate({ "identifier": "ContainerNetworkPolicyDisabledParam" }) +export type ContainerNetworkPolicyDomainSecretParam = { + readonly "domain": string + readonly "name": string + readonly "value": string +} +export const ContainerNetworkPolicyDomainSecretParam = Schema.Struct({ + "domain": Schema.String.annotate({ "description": "The domain associated with the secret." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "name": Schema.String.annotate({ "description": "The name of the secret to inject for the domain." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "value": Schema.String.annotate({ "description": "The secret value to inject for the domain." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })) +}).annotate({ "identifier": "ContainerNetworkPolicyDomainSecretParam" }) +export type ContainerFileResource = { + readonly "id": string + readonly "object": string + readonly "container_id": string + readonly "created_at": number + readonly "bytes": number + readonly "path": string + readonly "source": string +} +export const ContainerFileResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Unique identifier for the file." }), + "object": Schema.String.annotate({ "description": "The type of this object (`container.file`)." }), + "container_id": Schema.String.annotate({ "description": "The container this file belongs to." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the file was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "bytes": Schema.Number.annotate({ "description": "Size of the file in bytes." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "path": Schema.String.annotate({ "description": "Path of the file in the container." }), + "source": Schema.String.annotate({ "description": "Source of the file (e.g., `user`, `assistant`)." }) +}).annotate({ "title": "The container file object", "identifier": "ContainerFileResource" }) +export type CreateContainerFileBody = { readonly "file_id"?: string; readonly "file"?: string } +export const CreateContainerFileBody = Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "Name of the file to create." })), + "file": Schema.optionalKey( + Schema.String.annotate({ "description": "The File object (not file name) to be uploaded.\n", "format": "binary" }) + ) +}).annotate({ "identifier": "CreateContainerFileBody" }) +export type IncludeEnum = + | "file_search_call.results" + | "web_search_call.results" + | "web_search_call.action.sources" + | "message.input_image.image_url" + | "computer_call_output.output.image_url" + | "code_interpreter_call.outputs" + | "reasoning.encrypted_content" + | "message.output_text.logprobs" +export const IncludeEnum = Schema.Literals([ + "file_search_call.results", + "web_search_call.results", + "web_search_call.action.sources", + "message.input_image.image_url", + "computer_call_output.output.image_url", + "code_interpreter_call.outputs", + "reasoning.encrypted_content", + "message.output_text.logprobs" +]).annotate({ + "description": + "Specify additional output data to include in the model response. Currently supported values are:\n- `web_search_call.results`: Include the search results of the web search tool call.\n- `web_search_call.action.sources`: Include the sources of the web search tool call.\n- `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n- `file_search_call.results`: Include the search results of the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `message.output_text.logprobs`: Include logprobs with assistant messages.\n- `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program).", + "identifier": "IncludeEnum" +}) +export type MessageStatus = "in_progress" | "completed" | "incomplete" +export const MessageStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "MessageStatus" +}) +export type MessageRole = + | "unknown" + | "user" + | "assistant" + | "system" + | "critic" + | "discriminator" + | "developer" + | "tool" +export const MessageRole = Schema.Literals([ + "unknown", + "user", + "assistant", + "system", + "critic", + "discriminator", + "developer", + "tool" +]).annotate({ "identifier": "MessageRole" }) +export type PromptCacheBreakpointConfig = { readonly "mode": "explicit" } +export const PromptCacheBreakpointConfig = Schema.Struct({ + "mode": Schema.Literal("explicit").annotate({ "description": "The breakpoint mode. Always `explicit`." }) +}).annotate({ + "title": "Prompt cache breakpoint", + "description": + "Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.", + "identifier": "PromptCacheBreakpointConfig" +}) +export type FileCitationBody = { + readonly "type": "file_citation" + readonly "file_id": string + readonly "index": number + readonly "filename": string +} +export const FileCitationBody = Schema.Struct({ + "type": Schema.Literal("file_citation").annotate({ + "description": "The type of the file citation. Always `file_citation`." + }), + "file_id": Schema.String.annotate({ "description": "The ID of the file." }), + "index": Schema.Number.annotate({ + "description": "The index in the output text at which to insert the file citation." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "filename": Schema.String.annotate({ "description": "The filename of the file cited." }) +}).annotate({ "title": "File citation", "description": "A citation to a file.", "identifier": "FileCitationBody" }) +export type UrlCitationBody = { + readonly "type": "url_citation" + readonly "url": string + readonly "start_index": number + readonly "end_index": number + readonly "title": string +} +export const UrlCitationBody = Schema.Struct({ + "type": Schema.Literal("url_citation").annotate({ + "description": "The type of the URL citation. Always `url_citation`." + }), + "url": Schema.String.annotate({ "description": "The URL of the web resource.", "format": "uri" }), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the URL citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the URL citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "title": Schema.String.annotate({ "description": "The title of the web resource." }) +}).annotate({ + "title": "URL citation", + "description": "A citation for a web resource used to generate a model response.", + "identifier": "UrlCitationBody" +}) +export type ContainerFileCitationBody = { + readonly "type": "container_file_citation" + readonly "container_id": string + readonly "file_id": string + readonly "start_index": number + readonly "end_index": number + readonly "filename": string +} +export const ContainerFileCitationBody = Schema.Struct({ + "type": Schema.Literal("container_file_citation").annotate({ + "description": "The type of the container file citation. Always `container_file_citation`." + }), + "container_id": Schema.String.annotate({ "description": "The ID of the container file." }), + "file_id": Schema.String.annotate({ "description": "The ID of the file." }), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the container file citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the container file citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "filename": Schema.String.annotate({ "description": "The filename of the container file cited." }) +}).annotate({ + "title": "Container file citation", + "description": "A citation for a container file used to generate a model response.", + "identifier": "ContainerFileCitationBody" +}) +export type FilePath = { readonly "type": "file_path"; readonly "file_id": string; readonly "index": number } +export const FilePath = Schema.Struct({ + "type": Schema.Literal("file_path").annotate({ "description": "The type of the file path. Always `file_path`.\n" }), + "file_id": Schema.String.annotate({ "description": "The ID of the file.\n" }), + "index": Schema.Number.annotate({ "description": "The index of the file in the list of files.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "title": "File path", "description": "A path to a file.\n", "identifier": "FilePath" }) +export type TopLogProb = { + readonly "token": string + readonly "logprob": number + readonly "bytes": ReadonlyArray +} +export const TopLogProb = Schema.Struct({ + "token": Schema.String, + "logprob": Schema.Finite, + "bytes": Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) +}).annotate({ + "title": "Top log probability", + "description": "The top log probability of a token.", + "identifier": "TopLogProb" +}) +export type TextContent = { readonly "type": "text"; readonly "text": string } +export const TextContent = Schema.Struct({ "type": Schema.Literal("text"), "text": Schema.String }).annotate({ + "title": "Text Content", + "description": "A text content.", + "identifier": "TextContent" +}) +export type SummaryTextContent = { readonly "type": "summary_text"; readonly "text": string } +export const SummaryTextContent = Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ "description": "The type of the object. Always `summary_text`." }), + "text": Schema.String.annotate({ "description": "A summary of the reasoning output from the model so far." }) +}).annotate({ + "title": "Summary text", + "description": "A summary text from the model.", + "identifier": "SummaryTextContent" +}) +export type ReasoningTextContent = { readonly "type": "reasoning_text"; readonly "text": string } +export const ReasoningTextContent = Schema.Struct({ + "type": Schema.Literal("reasoning_text").annotate({ + "description": "The type of the reasoning text. Always `reasoning_text`." + }), + "text": Schema.String.annotate({ "description": "The reasoning text from the model." }) +}).annotate({ + "title": "Reasoning text", + "description": "Reasoning text from the model.", + "identifier": "ReasoningTextContent" +}) +export type RefusalContent = { readonly "type": "refusal"; readonly "refusal": string } +export const RefusalContent = Schema.Struct({ + "type": Schema.Literal("refusal").annotate({ "description": "The type of the refusal. Always `refusal`." }), + "refusal": Schema.String.annotate({ "description": "The refusal explanation from the model." }) +}).annotate({ "title": "Refusal", "description": "A refusal from the model.", "identifier": "RefusalContent" }) +export type ImageDetail = "low" | "high" | "auto" | "original" +export const ImageDetail = Schema.Literals(["low", "high", "auto", "original"]).annotate({ + "identifier": "ImageDetail" +}) +export type FileInputDetail = "auto" | "low" | "high" +export const FileInputDetail = Schema.Literals(["auto", "low", "high"]).annotate({ "identifier": "FileInputDetail" }) +export type MessagePhase_2 = "commentary" | "final_answer" +export const MessagePhase_2 = Schema.Literals(["commentary", "final_answer"]).annotate({ + "identifier": "MessagePhase-2" +}) +export type DirectToolCallCaller = { readonly "type": "direct" } +export const DirectToolCallCaller = Schema.Struct({ "type": Schema.Literal("direct") }).annotate({ + "identifier": "DirectToolCallCaller" +}) +export type ProgramToolCallCaller = { readonly "type": "program"; readonly "caller_id": string } +export const ProgramToolCallCaller = Schema.Struct({ + "type": Schema.Literal("program"), + "caller_id": Schema.String.annotate({ + "description": "The call ID of the program item that produced this tool call." + }) +}).annotate({ "identifier": "ProgramToolCallCaller" }) +export type DirectToolCallCallerParam = { readonly "type": "direct" } +export const DirectToolCallCallerParam = Schema.Struct({ + "type": Schema.Literal("direct").annotate({ "description": "The caller type. Always `direct`." }) +}).annotate({ "identifier": "DirectToolCallCallerParam" }) +export type ProgramToolCallCallerParam = { readonly "type": "program"; readonly "caller_id": string } +export const ProgramToolCallCallerParam = Schema.Struct({ + "type": Schema.Literal("program").annotate({ "description": "The caller type. Always `program`." }), + "caller_id": Schema.String.annotate({ + "description": "The call ID of the program item that produced this tool call." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ) +}).annotate({ "identifier": "ProgramToolCallCallerParam" }) +export type VectorStoreFileAttributes = { readonly [x: string]: string | number | boolean } | null +export const VectorStoreFileAttributes = Schema.Union([ + Schema.Record( + Schema.String, + Schema.Union([ + Schema.String.check( + Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" }) + ), + Schema.Finite, + Schema.Boolean + ], { mode: "oneOf" }) + ).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard. Keys are strings\nwith a maximum length of 64 characters. Values are strings with a maximum\nlength of 512 characters, booleans, or numbers.\n" + }).check(Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null +]).annotate({ "identifier": "VectorStoreFileAttributes" }) +export type WebSearchCallStatus = "in_progress" | "searching" | "completed" | "failed" | "incomplete" +export const WebSearchCallStatus = Schema.Literals(["in_progress", "searching", "completed", "failed", "incomplete"]) + .annotate({ "identifier": "WebSearchCallStatus" }) +export type WebSearchActionSearch = { + readonly "type": "search" + readonly "query"?: string + readonly "queries"?: ReadonlyArray + readonly "sources"?: ReadonlyArray< + { readonly "type": "url"; readonly "url": string } | { readonly "type": "api"; readonly "name": string } + > +} +export const WebSearchActionSearch = Schema.Struct({ + "type": Schema.Literal("search").annotate({ "description": "The action type.\n" }), + "query": Schema.optionalKey(Schema.String.annotate({ "description": "The search query.\n" })), + "queries": Schema.optionalKey( + Schema.Array(Schema.String.annotate({ "description": "A search query.\n" })).annotate({ + "title": "Search queries", + "description": "The search queries.\n" + }) + ), + "sources": Schema.optionalKey( + Schema.Array( + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("url").annotate({ "description": "The type of source. Always `url`.\n" }), + "url": Schema.String.annotate({ "description": "The URL of the source.\n", "format": "uri" }) + }).annotate({ "title": "Web search source", "description": "A source used in the search.\n" }), + Schema.Struct({ + "type": Schema.Literal("api").annotate({ "description": "The type of source. Always `api`.\n" }), + "name": Schema.String.annotate({ + "description": "The name of the API source, such as `oai-weather`, `oai-sports`, or `oai-finance`.\n" + }) + }).annotate({ "title": "Web search API source", "description": "An API source used in the search.\n" }) + ]) + ).annotate({ "title": "Web search sources", "description": "The sources used in the search.\n" }) + ) +}).annotate({ + "title": "Search action", + "description": "Action type \"search\" - Performs a web search query.\n", + "identifier": "WebSearchActionSearch" +}) +export type WebSearchActionOpenPage = { readonly "type": "open_page"; readonly "url"?: string | null } +export const WebSearchActionOpenPage = Schema.Struct({ + "type": Schema.Literal("open_page").annotate({ "description": "The action type.\n" }), + "url": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "format": "uri" }), Schema.Null]).annotate({ + "description": "The URL opened by the model.\n" + }) + ) +}).annotate({ + "title": "Open page action", + "description": "Action type \"open_page\" - Opens a specific URL from search results.\n", + "identifier": "WebSearchActionOpenPage" +}) +export type WebSearchActionFind = { + readonly "type": "find_in_page" + readonly "url"?: string + readonly "pattern"?: string +} +export const WebSearchActionFind = Schema.Struct({ + "type": Schema.Literal("find_in_page").annotate({ "description": "The action type.\n" }), + "url": Schema.optionalKey( + Schema.String.annotate({ "description": "The URL of the page searched for the pattern.\n", "format": "uri" }) + ), + "pattern": Schema.optionalKey( + Schema.String.annotate({ "description": "The pattern or text to search for within the page.\n" }) + ) +}).annotate({ + "title": "Find action", + "description": "Action type \"find_in_page\": Searches for a pattern within a loaded page.\n", + "identifier": "WebSearchActionFind" +}) +export type ImageGenActionEnum = "generate" | "edit" | "auto" +export const ImageGenActionEnum = Schema.Literals(["generate", "edit", "auto"]).annotate({ + "identifier": "ImageGenActionEnum" +}) +export type ImageBackground = "transparent" | "opaque" | "auto" +export const ImageBackground = Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "identifier": "ImageBackground" +}) +export type ImageOutputFormat = "png" | "webp" | "jpeg" +export const ImageOutputFormat = Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "identifier": "ImageOutputFormat" +}) +export type ClickButtonType = "left" | "right" | "wheel" | "back" | "forward" +export const ClickButtonType = Schema.Literals(["left", "right", "wheel", "back", "forward"]).annotate({ + "identifier": "ClickButtonType" +}) +export type DoubleClickAction = { + readonly "type": "double_click" + readonly "x": number + readonly "y": number + readonly "keys": ReadonlyArray | null +} +export const DoubleClickAction = Schema.Struct({ + "type": Schema.Literal("double_click").annotate({ + "description": "Specifies the event type. For a double click action, this property is always set to `double_click`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate where the double click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate where the double click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while double-clicking." }), + Schema.Null + ]) +}).annotate({ "title": "DoubleClick", "description": "A double click action.", "identifier": "DoubleClickAction" }) +export type CoordParam = { readonly "x": number; readonly "y": number } +export const CoordParam = Schema.Struct({ + "x": Schema.Number.annotate({ "description": "The x-coordinate." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Coordinate", + "description": "An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.", + "identifier": "CoordParam" +}) +export type KeyPressAction = { readonly "type": "keypress"; readonly "keys": ReadonlyArray } +export const KeyPressAction = Schema.Struct({ + "type": Schema.Literal("keypress").annotate({ + "description": "Specifies the event type. For a keypress action, this property is always set to `keypress`." + }), + "keys": Schema.Array( + Schema.String.annotate({ "description": "One of the keys the model is requesting to be pressed." }) + ).annotate({ + "description": + "The combination of keys the model is requesting to be pressed. This is an array of strings, each representing a key." + }) +}).annotate({ + "title": "KeyPress", + "description": "A collection of keypresses the model would like to perform.", + "identifier": "KeyPressAction" +}) +export type MoveParam = { + readonly "type": "move" + readonly "x": number + readonly "y": number + readonly "keys"?: ReadonlyArray | null +} +export const MoveParam = Schema.Struct({ + "type": Schema.Literal("move").annotate({ + "description": "Specifies the event type. For a move action, this property is always set to `move`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate to move to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate to move to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while moving the mouse." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Move", "description": "A mouse move action.", "identifier": "MoveParam" }) +export type ScreenshotParam = { readonly "type": "screenshot" } +export const ScreenshotParam = Schema.Struct({ + "type": Schema.Literal("screenshot").annotate({ + "description": "Specifies the event type. For a screenshot action, this property is always set to `screenshot`." + }) +}).annotate({ "title": "Screenshot", "description": "A screenshot action.", "identifier": "ScreenshotParam" }) +export type ScrollParam = { + readonly "type": "scroll" + readonly "x": number + readonly "y": number + readonly "scroll_x": number + readonly "scroll_y": number + readonly "keys"?: ReadonlyArray | null +} +export const ScrollParam = Schema.Struct({ + "type": Schema.Literal("scroll").annotate({ + "description": "Specifies the event type. For a scroll action, this property is always set to `scroll`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate where the scroll occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate where the scroll occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "scroll_x": Schema.Number.annotate({ "description": "The horizontal scroll distance." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "scroll_y": Schema.Number.annotate({ "description": "The vertical scroll distance." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while scrolling." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Scroll", "description": "A scroll action.", "identifier": "ScrollParam" }) +export type TypeParam = { readonly "type": "type"; readonly "text": string } +export const TypeParam = Schema.Struct({ + "type": Schema.Literal("type").annotate({ + "description": "Specifies the event type. For a type action, this property is always set to `type`." + }), + "text": Schema.String.annotate({ "description": "The text to type." }) +}).annotate({ "title": "Type", "description": "An action to type in text.", "identifier": "TypeParam" }) +export type WaitParam = { readonly "type": "wait" } +export const WaitParam = Schema.Struct({ + "type": Schema.Literal("wait").annotate({ + "description": "Specifies the event type. For a wait action, this property is always set to `wait`." + }) +}).annotate({ "title": "Wait", "description": "A wait action.", "identifier": "WaitParam" }) +export type ComputerCallSafetyCheckParam = { + readonly "id": string + readonly "code"?: string | null + readonly "message"?: string | null +} +export const ComputerCallSafetyCheckParam = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the pending safety check." }), + "code": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The type of the pending safety check." }), Schema.Null]) + ), + "message": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Details about the pending safety check." }), Schema.Null]) + ) +}).annotate({ + "description": "A pending safety check for the computer call.", + "identifier": "ComputerCallSafetyCheckParam" +}) +export type ComputerScreenshotImage = { + readonly "type": "computer_screenshot" + readonly "image_url"?: string + readonly "file_id"?: string +} +export const ComputerScreenshotImage = Schema.Struct({ + "type": Schema.Literal("computer_screenshot").annotate({ + "description": + "Specifies the event type. For a computer screenshot, this property is \nalways set to `computer_screenshot`.\n" + }), + "image_url": Schema.optionalKey( + Schema.String.annotate({ "description": "The URL of the screenshot image.", "format": "uri" }) + ), + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of an uploaded file that contains the screenshot." }) + ) +}).annotate({ + "description": "A computer screenshot image used with the computer use tool.\n", + "identifier": "ComputerScreenshotImage" +}) +export type ToolSearchExecutionType = "server" | "client" +export const ToolSearchExecutionType = Schema.Literals(["server", "client"]).annotate({ + "identifier": "ToolSearchExecutionType" +}) +export type FunctionCallStatus = "in_progress" | "completed" | "incomplete" +export const FunctionCallStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "FunctionCallStatus" +}) +export type CallableToolAllowedCaller = "direct" | "programmatic" +export const CallableToolAllowedCaller = Schema.Literals(["direct", "programmatic"]).annotate({ + "identifier": "CallableToolAllowedCaller" +}) +export type RankerVersionType = "auto" | "default-2024-11-15" +export const RankerVersionType = Schema.Literals(["auto", "default-2024-11-15"]).annotate({ + "identifier": "RankerVersionType" +}) +export type HybridSearchOptions = { readonly "embedding_weight": number; readonly "text_weight": number } +export const HybridSearchOptions = Schema.Struct({ + "embedding_weight": Schema.Number.annotate({ + "description": "The weight of the embedding in the reciprocal ranking fusion." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "text_weight": Schema.Number.annotate({ "description": "The weight of the text in the reciprocal ranking fusion." }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })) +}).annotate({ "identifier": "HybridSearchOptions" }) +export type ComparisonFilter = { + readonly "type": "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" + readonly "key": string + readonly "value": string | number | boolean | ReadonlyArray +} +export const ComparisonFilter = Schema.Struct({ + "type": Schema.Literals(["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]).annotate({ + "description": + "Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`.\n- `eq`: equals\n- `ne`: not equal\n- `gt`: greater than\n- `gte`: greater than or equal\n- `lt`: less than\n- `lte`: less than or equal\n- `in`: in\n- `nin`: not in\n" + }), + "key": Schema.String.annotate({ "description": "The key to compare against the value." }), + "value": Schema.Union([ + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.Array(Schema.Union([Schema.String, Schema.Finite], { mode: "oneOf" })) + ], { mode: "oneOf" }).annotate({ + "description": "The value to compare against the attribute key; supports string, number, or boolean types." + }) +}).annotate({ + "title": "Comparison Filter", + "description": + "A filter used to compare a specified attribute key to a given value using a defined comparison operation.\n", + "identifier": "ComparisonFilter" +}) +export type ComputerTool = { readonly "type": "computer" } +export const ComputerTool = Schema.Struct({ + "type": Schema.Literal("computer").annotate({ "description": "The type of the computer tool. Always `computer`." }) +}).annotate({ + "title": "Computer", + "description": + "A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).", + "identifier": "ComputerTool" +}) +export type ComputerEnvironment = "windows" | "mac" | "linux" | "ubuntu" | "browser" +export const ComputerEnvironment = Schema.Literals(["windows", "mac", "linux", "ubuntu", "browser"]).annotate({ + "identifier": "ComputerEnvironment" +}) +export type WebSearchApproximateLocation = { + readonly "type"?: "approximate" + readonly "country"?: string | null + readonly "region"?: string | null + readonly "city"?: string | null + readonly "timezone"?: string | null +} | null +export const WebSearchApproximateLocation = Schema.Union([ + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("approximate").annotate({ + "description": "The type of location approximation. Always `approximate`." + }) + ), + "country": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`." + }), + Schema.Null + ]) + ), + "region": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the region of the user, e.g. `California`." }), + Schema.Null + ]) + ), + "city": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the city of the user, e.g. `San Francisco`." }), + Schema.Null + ]) + ), + "timezone": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`." + }), + Schema.Null + ]) + ) + }).annotate({ "title": "Web search approximate location", "description": "The approximate location of the user.\n" }), + Schema.Null +]).annotate({ "identifier": "WebSearchApproximateLocation" }) +export type MCPToolFilter = { readonly "tool_names"?: ReadonlyArray; readonly "read_only"?: boolean } +export const MCPToolFilter = Schema.Struct({ + "tool_names": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "title": "MCP allowed tools", "description": "List of allowed tool names." }) + ), + "read_only": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Indicates whether or not a tool modifies data or is read-only. If an\nMCP server is [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint),\nit will match this filter.\n" + }) + ) +}).annotate({ + "title": "MCP tool filter", + "description": "A filter object to specify which tools are allowed.\n", + "identifier": "MCPToolFilter" +}) +export type ContainerMemoryLimit = "1g" | "4g" | "16g" | "64g" +export const ContainerMemoryLimit = Schema.Literals(["1g", "4g", "16g", "64g"]).annotate({ + "identifier": "ContainerMemoryLimit" +}) +export type ProgrammaticToolCallingParam = { readonly "type": "programmatic_tool_calling" } +export const ProgrammaticToolCallingParam = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The type of the tool. Always `programmatic_tool_calling`." + }) +}).annotate({ "identifier": "ProgrammaticToolCallingParam" }) +export type InputFidelity = "high" | "low" +export const InputFidelity = Schema.Literals(["high", "low"]).annotate({ + "description": + "Control how much effort the model will exert to match the style and features, especially facial features, of input images. Supports `high` and `low` on `gpt-image-1` and `gpt-image-1.5`; `gpt-image-1-mini` supports only `low`. For `gpt-image-2`, omit this parameter. Defaults to `low` on supported models.", + "identifier": "InputFidelity" +}) +export type LocalShellToolParam = { readonly "type": "local_shell" } +export const LocalShellToolParam = Schema.Struct({ + "type": Schema.Literal("local_shell").annotate({ + "description": "The type of the local shell tool. Always `local_shell`." + }) +}).annotate({ + "title": "Local shell tool", + "description": "A tool that allows the model to execute shell commands in a local environment.", + "identifier": "LocalShellToolParam" +}) +export type LocalSkillParam = { readonly "name": string; readonly "description": string; readonly "path": string } +export const LocalSkillParam = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the skill." }), + "description": Schema.String.annotate({ "description": "The description of the skill." }), + "path": Schema.String.annotate({ "description": "The path to the directory containing the skill." }) +}).annotate({ "identifier": "LocalSkillParam" }) +export type ContainerReferenceParam = { readonly "type": "container_reference"; readonly "container_id": string } +export const ContainerReferenceParam = Schema.Struct({ + "type": Schema.Literal("container_reference").annotate({ + "description": "References a container created with the /v1/containers endpoint" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the referenced container." }) +}).annotate({ "identifier": "ContainerReferenceParam" }) +export type CustomTextFormatParam = { readonly "type": "text" } +export const CustomTextFormatParam = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "Unconstrained text format. Always `text`." }) +}).annotate({ + "title": "Text format", + "description": "Unconstrained free-form text.", + "identifier": "CustomTextFormatParam" +}) +export type GrammarSyntax1 = "lark" | "regex" +export const GrammarSyntax1 = Schema.Literals(["lark", "regex"]).annotate({ "identifier": "GrammarSyntax1" }) +export type EmptyModelParam = { readonly [x: string]: Schema.Json } +export const EmptyModelParam = Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + .annotate({ "identifier": "EmptyModelParam" }) +export type ApproximateLocation = { + readonly "type": "approximate" + readonly "country"?: string | null + readonly "region"?: string | null + readonly "city"?: string | null + readonly "timezone"?: string | null +} +export const ApproximateLocation = Schema.Struct({ + "type": Schema.Literal("approximate").annotate({ + "description": "The type of location approximation. Always `approximate`." + }), + "country": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`." + }), + Schema.Null + ]) + ), + "region": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the region of the user, e.g. `California`." }), + Schema.Null + ]) + ), + "city": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the city of the user, e.g. `San Francisco`." }), + Schema.Null + ]) + ), + "timezone": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "ApproximateLocation" }) +export type SearchContextSize = "low" | "medium" | "high" +export const SearchContextSize = Schema.Literals(["low", "medium", "high"]).annotate({ + "identifier": "SearchContextSize" +}) +export type SearchContentType = "text" | "image" +export const SearchContentType = Schema.Literals(["text", "image"]).annotate({ "identifier": "SearchContentType" }) +export type FunctionCallOutputStatusEnum = "in_progress" | "completed" | "incomplete" +export const FunctionCallOutputStatusEnum = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "FunctionCallOutputStatusEnum" +}) +export type Program = { + readonly "type": "program" + readonly "id": string + readonly "call_id": string + readonly "code": string + readonly "fingerprint": string +} +export const Program = Schema.Struct({ + "type": Schema.Literal("program").annotate({ "description": "The type of the item. Always `program`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the program item." }), + "call_id": Schema.String.annotate({ "description": "The stable call ID of the program item." }), + "code": Schema.String.annotate({ "description": "The JavaScript source executed by programmatic tool calling." }), + "fingerprint": Schema.String.annotate({ + "description": "Opaque program replay fingerprint that must be round-tripped." + }) +}).annotate({ "identifier": "Program" }) +export type ProgramOutputStatus = "completed" | "incomplete" +export const ProgramOutputStatus = Schema.Literals(["completed", "incomplete"]).annotate({ + "identifier": "ProgramOutputStatus" +}) +export type CompactionBody = { + readonly "type": "compaction" + readonly "id": string + readonly "encrypted_content": string + readonly "created_by"?: string +} +export const CompactionBody = Schema.Struct({ + "type": Schema.Literal("compaction").annotate({ "description": "The type of the item. Always `compaction`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the compaction item." }), + "encrypted_content": Schema.String.annotate({ + "description": "The encrypted content that was produced by compaction." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ + "title": "Compaction item", + "description": + "A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).", + "identifier": "CompactionBody" +}) +export type CodeInterpreterOutputLogs = { readonly "type": "logs"; readonly "logs": string } +export const CodeInterpreterOutputLogs = Schema.Struct({ + "type": Schema.Literal("logs").annotate({ "description": "The type of the output. Always `logs`." }), + "logs": Schema.String.annotate({ "description": "The logs output from the code interpreter." }) +}).annotate({ + "title": "Code interpreter output logs", + "description": "The logs output from the code interpreter.", + "identifier": "CodeInterpreterOutputLogs" +}) +export type CodeInterpreterOutputImage = { readonly "type": "image"; readonly "url": string } +export const CodeInterpreterOutputImage = Schema.Struct({ + "type": Schema.Literal("image").annotate({ "description": "The type of the output. Always `image`." }), + "url": Schema.String.annotate({ + "description": "The URL of the image output from the code interpreter.", + "format": "uri" + }) +}).annotate({ + "title": "Code interpreter output image", + "description": "The image output from the code interpreter.", + "identifier": "CodeInterpreterOutputImage" +}) +export type LocalShellExecAction = { + readonly "type": "exec" + readonly "command": ReadonlyArray + readonly "timeout_ms"?: number | null + readonly "working_directory"?: string | null + readonly "env": { readonly [x: string]: string } + readonly "user"?: string | null +} +export const LocalShellExecAction = Schema.Struct({ + "type": Schema.Literal("exec").annotate({ "description": "The type of the local shell action. Always `exec`." }), + "command": Schema.Array(Schema.String).annotate({ "description": "The command to run." }), + "timeout_ms": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "Optional timeout in milliseconds for the command." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "working_directory": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Optional working directory to run the command in." }), + Schema.Null + ]) + ), + "env": Schema.Record(Schema.String, Schema.String).annotate({ + "description": "Environment variables to set for the command." + }), + "user": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional user to run the command as." }), Schema.Null]) + ) +}).annotate({ + "title": "Local shell exec action", + "description": "Execute a shell command on the server.", + "identifier": "LocalShellExecAction" +}) +export type LocalShellToolCallOutput = { + readonly "type": "local_shell_call_output" + readonly "id": string + readonly "output": string + readonly "status"?: "in_progress" | "completed" | "incomplete" | null + readonly "call_id": Schema.Json +} +export const LocalShellToolCallOutput = Schema.Struct({ + "type": Schema.Literal("local_shell_call_output").annotate({ + "description": "The type of the local shell tool call output. Always `local_shell_call_output`.\n" + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "output": Schema.String.annotate({ "description": "A JSON string of the output of the local shell tool call.\n" }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the item. One of `in_progress`, `completed`, or `incomplete`.\n" + }), + Schema.Null + ]) + ), + "call_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "title": "Local shell call output", + "description": "The output of a local shell tool call.\n", + "identifier": "LocalShellToolCallOutput" +}) +export type FunctionShellAction = { + readonly "commands": ReadonlyArray + readonly "timeout_ms": number | null + readonly "max_output_length": number | null +} +export const FunctionShellAction = Schema.Struct({ + "commands": Schema.Array(Schema.String.annotate({ "description": "A list of commands to run." })), + "timeout_ms": Schema.Union([ + Schema.Number.annotate({ "description": "Optional timeout in milliseconds for the commands." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]), + "max_output_length": Schema.Union([ + Schema.Number.annotate({ "description": "Optional maximum number of characters to return from each command." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) +}).annotate({ + "title": "Shell exec action", + "description": "Execute a shell command.", + "identifier": "FunctionShellAction" +}) +export type FunctionShellCallStatus = "in_progress" | "completed" | "incomplete" +export const FunctionShellCallStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "FunctionShellCallStatus" +}) +export type LocalEnvironmentResource = { readonly "type": "local" } +export const LocalEnvironmentResource = Schema.Struct({ + "type": Schema.Literal("local").annotate({ "description": "The environment type. Always `local`." }) +}).annotate({ + "title": "Local Environment", + "description": "Represents the use of a local environment to perform shell actions.", + "identifier": "LocalEnvironmentResource" +}) +export type ContainerReferenceResource = { readonly "type": "container_reference"; readonly "container_id": string } +export const ContainerReferenceResource = Schema.Struct({ + "type": Schema.Literal("container_reference").annotate({ + "description": "The environment type. Always `container_reference`." + }), + "container_id": Schema.String +}).annotate({ + "title": "Container Reference", + "description": "Represents a container created with /v1/containers.", + "identifier": "ContainerReferenceResource" +}) +export type FunctionShellCallOutputStatusEnum = "in_progress" | "completed" | "incomplete" +export const FunctionShellCallOutputStatusEnum = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "FunctionShellCallOutputStatusEnum" +}) +export type FunctionShellCallOutputTimeoutOutcome = { readonly "type": "timeout" } +export const FunctionShellCallOutputTimeoutOutcome = Schema.Struct({ + "type": Schema.Literal("timeout").annotate({ "description": "The outcome type. Always `timeout`." }) +}).annotate({ + "title": "Shell call timeout outcome", + "description": "Indicates that the shell call exceeded its configured time limit.", + "identifier": "FunctionShellCallOutputTimeoutOutcome" +}) +export type FunctionShellCallOutputExitOutcome = { readonly "type": "exit"; readonly "exit_code": number } +export const FunctionShellCallOutputExitOutcome = Schema.Struct({ + "type": Schema.Literal("exit").annotate({ "description": "The outcome type. Always `exit`." }), + "exit_code": Schema.Number.annotate({ "description": "Exit code from the shell process." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Shell call exit outcome", + "description": "Indicates that the shell commands finished and returned an exit code.", + "identifier": "FunctionShellCallOutputExitOutcome" +}) +export type ApplyPatchCallStatus = "in_progress" | "completed" +export const ApplyPatchCallStatus = Schema.Literals(["in_progress", "completed"]).annotate({ + "identifier": "ApplyPatchCallStatus" +}) +export type ApplyPatchCreateFileOperation = { + readonly "type": "create_file" + readonly "path": string + readonly "diff": string +} +export const ApplyPatchCreateFileOperation = Schema.Struct({ + "type": Schema.Literal("create_file").annotate({ "description": "Create a new file with the provided diff." }), + "path": Schema.String.annotate({ "description": "Path of the file to create." }), + "diff": Schema.String.annotate({ "description": "Diff to apply." }) +}).annotate({ + "title": "Apply patch create file operation", + "description": "Instruction describing how to create a file via the apply_patch tool.", + "identifier": "ApplyPatchCreateFileOperation" +}) +export type ApplyPatchDeleteFileOperation = { readonly "type": "delete_file"; readonly "path": string } +export const ApplyPatchDeleteFileOperation = Schema.Struct({ + "type": Schema.Literal("delete_file").annotate({ "description": "Delete the specified file." }), + "path": Schema.String.annotate({ "description": "Path of the file to delete." }) +}).annotate({ + "title": "Apply patch delete file operation", + "description": "Instruction describing how to delete a file via the apply_patch tool.", + "identifier": "ApplyPatchDeleteFileOperation" +}) +export type ApplyPatchUpdateFileOperation = { + readonly "type": "update_file" + readonly "path": string + readonly "diff": string +} +export const ApplyPatchUpdateFileOperation = Schema.Struct({ + "type": Schema.Literal("update_file").annotate({ "description": "Update an existing file with the provided diff." }), + "path": Schema.String.annotate({ "description": "Path of the file to update." }), + "diff": Schema.String.annotate({ "description": "Diff to apply." }) +}).annotate({ + "title": "Apply patch update file operation", + "description": "Instruction describing how to update a file via the apply_patch tool.", + "identifier": "ApplyPatchUpdateFileOperation" +}) +export type ApplyPatchCallOutputStatus = "completed" | "failed" +export const ApplyPatchCallOutputStatus = Schema.Literals(["completed", "failed"]).annotate({ + "identifier": "ApplyPatchCallOutputStatus" +}) +export type MCPListToolsTool = { + readonly "name": string + readonly "description"?: string | null + readonly "input_schema": { readonly [x: string]: Schema.Json } + readonly "annotations"?: { readonly [x: string]: Schema.Json } | null +} +export const MCPListToolsTool = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the tool.\n" }), + "description": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The description of the tool.\n" }), Schema.Null]) + ), + "input_schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The JSON schema describing the tool's input.\n" + }), + "annotations": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Additional annotations about the tool.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP list tools tool", + "description": "A tool available on an MCP server.\n", + "identifier": "MCPListToolsTool" +}) +export type MCPApprovalRequest = { + readonly "type": "mcp_approval_request" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string +} +export const MCPApprovalRequest = Schema.Struct({ + "type": Schema.Literal("mcp_approval_request").annotate({ + "description": "The type of the item. Always `mcp_approval_request`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the approval request.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server making the request.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of arguments for the tool.\n" }) +}).annotate({ + "title": "MCP approval request", + "description": "A request for human approval of a tool invocation.\n", + "identifier": "MCPApprovalRequest" +}) +export type MCPApprovalResponseResource = { + readonly "type": "mcp_approval_response" + readonly "id": string + readonly "approval_request_id": string + readonly "approve": boolean + readonly "reason"?: string | null + readonly "request_id": Schema.Json +} +export const MCPApprovalResponseResource = Schema.Struct({ + "type": Schema.Literal("mcp_approval_response").annotate({ + "description": "The type of the item. Always `mcp_approval_response`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the approval response\n" }), + "approval_request_id": Schema.String.annotate({ "description": "The ID of the approval request being answered.\n" }), + "approve": Schema.Boolean.annotate({ "description": "Whether the request was approved.\n" }), + "reason": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional reason for the decision.\n" }), Schema.Null]) + ), + "request_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "title": "MCP approval response", + "description": "A response to an MCP approval request.\n", + "identifier": "MCPApprovalResponseResource" +}) +export type MCPProtocolError = { + readonly "type": "mcp_protocol_error" + readonly "code": number + readonly "message": string +} +export const MCPProtocolError = Schema.Struct({ + "type": Schema.Literal("mcp_protocol_error"), + "code": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "message": Schema.String +}).annotate({ "identifier": "MCPProtocolError" }) +export type MCPToolExecutionError = { readonly "type": "mcp_tool_execution_error"; readonly "content": Schema.Json } +export const MCPToolExecutionError = Schema.Struct({ + "type": Schema.Literal("mcp_tool_execution_error"), + "content": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ "identifier": "MCPToolExecutionError" }) +export type HTTPError = { readonly "type": "http_error"; readonly "code": number; readonly "message": string } +export const HTTPError = Schema.Struct({ + "type": Schema.Literal("http_error"), + "code": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "message": Schema.String +}).annotate({ "identifier": "HTTPError" }) +export type MCPToolCallStatus = "in_progress" | "completed" | "incomplete" | "calling" | "failed" +export const MCPToolCallStatus = Schema.Literals(["in_progress", "completed", "incomplete", "calling", "failed"]) + .annotate({ "identifier": "MCPToolCallStatus" }) +export type MessagePhase = "commentary" | "final_answer" +export const MessagePhase = Schema.Literals(["commentary", "final_answer"]).annotate({ + "description": + "Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`).\nFor models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend\nphase on all assistant messages — dropping it can degrade performance. Not used for user messages.\n", + "identifier": "MessagePhase" +}) +export type FunctionCallItemStatus = "in_progress" | "completed" | "incomplete" +export const FunctionCallItemStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "FunctionCallItemStatus" +}) +export type DetailEnum = "low" | "high" | "auto" | "original" +export const DetailEnum = Schema.Literals(["low", "high", "auto", "original"]).annotate({ "identifier": "DetailEnum" }) +export type FileDetailEnum = "auto" | "low" | "high" +export const FileDetailEnum = Schema.Literals(["auto", "low", "high"]).annotate({ "identifier": "FileDetailEnum" }) +export type FunctionShellActionParam = { + readonly "commands": ReadonlyArray + readonly "timeout_ms"?: number | null + readonly "max_output_length"?: number | null +} +export const FunctionShellActionParam = Schema.Struct({ + "commands": Schema.Array(Schema.String).annotate({ + "description": "Ordered shell commands for the execution environment to run." + }), + "timeout_ms": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Maximum wall-clock time in milliseconds to allow the shell commands to run." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "max_output_length": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Maximum number of UTF-8 characters to capture from combined stdout and stderr output." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) +}).annotate({ + "title": "Shell action", + "description": "Commands and limits describing how to run the shell tool call.", + "identifier": "FunctionShellActionParam" +}) +export type FunctionShellCallItemStatus = "in_progress" | "completed" | "incomplete" +export const FunctionShellCallItemStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "title": "Shell call status", + "description": "Status values reported for shell tool calls.", + "identifier": "FunctionShellCallItemStatus" +}) +export type FunctionShellCallOutputTimeoutOutcomeParam = { readonly "type": "timeout" } +export const FunctionShellCallOutputTimeoutOutcomeParam = Schema.Struct({ + "type": Schema.Literal("timeout").annotate({ "description": "The outcome type. Always `timeout`." }) +}).annotate({ + "title": "Shell call timeout outcome", + "description": "Indicates that the shell call exceeded its configured time limit.", + "identifier": "FunctionShellCallOutputTimeoutOutcomeParam" +}) +export type FunctionShellCallOutputExitOutcomeParam = { readonly "type": "exit"; readonly "exit_code": number } +export const FunctionShellCallOutputExitOutcomeParam = Schema.Struct({ + "type": Schema.Literal("exit").annotate({ "description": "The outcome type. Always `exit`." }), + "exit_code": Schema.Number.annotate({ "description": "The exit code returned by the shell process." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Shell call exit outcome", + "description": "Indicates that the shell commands finished and returned an exit code.", + "identifier": "FunctionShellCallOutputExitOutcomeParam" +}) +export type ApplyPatchCallStatusParam = "in_progress" | "completed" +export const ApplyPatchCallStatusParam = Schema.Literals(["in_progress", "completed"]).annotate({ + "title": "Apply patch call status", + "description": "Status values reported for apply_patch tool calls.", + "identifier": "ApplyPatchCallStatusParam" +}) +export type ApplyPatchCreateFileOperationParam = { + readonly "type": "create_file" + readonly "path": string + readonly "diff": string +} +export const ApplyPatchCreateFileOperationParam = Schema.Struct({ + "type": Schema.Literal("create_file").annotate({ "description": "The operation type. Always `create_file`." }), + "path": Schema.String.annotate({ "description": "Path of the file to create relative to the workspace root." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "diff": Schema.String.annotate({ "description": "Unified diff content to apply when creating the file." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ) +}).annotate({ + "title": "Apply patch create file operation", + "description": "Instruction for creating a new file via the apply_patch tool.", + "identifier": "ApplyPatchCreateFileOperationParam" +}) +export type ApplyPatchDeleteFileOperationParam = { readonly "type": "delete_file"; readonly "path": string } +export const ApplyPatchDeleteFileOperationParam = Schema.Struct({ + "type": Schema.Literal("delete_file").annotate({ "description": "The operation type. Always `delete_file`." }), + "path": Schema.String.annotate({ "description": "Path of the file to delete relative to the workspace root." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) +}).annotate({ + "title": "Apply patch delete file operation", + "description": "Instruction for deleting an existing file via the apply_patch tool.", + "identifier": "ApplyPatchDeleteFileOperationParam" +}) +export type ApplyPatchUpdateFileOperationParam = { + readonly "type": "update_file" + readonly "path": string + readonly "diff": string +} +export const ApplyPatchUpdateFileOperationParam = Schema.Struct({ + "type": Schema.Literal("update_file").annotate({ "description": "The operation type. Always `update_file`." }), + "path": Schema.String.annotate({ "description": "Path of the file to update relative to the workspace root." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "diff": Schema.String.annotate({ "description": "Unified diff content to apply to the existing file." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ) +}).annotate({ + "title": "Apply patch update file operation", + "description": "Instruction for updating an existing file via the apply_patch tool.", + "identifier": "ApplyPatchUpdateFileOperationParam" +}) +export type ApplyPatchCallOutputStatusParam = "completed" | "failed" +export const ApplyPatchCallOutputStatusParam = Schema.Literals(["completed", "failed"]).annotate({ + "title": "Apply patch call output status", + "description": "Outcome values reported for apply_patch tool call outputs.", + "identifier": "ApplyPatchCallOutputStatusParam" +}) +export type CompactionTriggerItemParam = { readonly "id"?: string | null; readonly "type": "compaction_trigger" } +export const CompactionTriggerItemParam = Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The unique ID of this compaction trigger." }), Schema.Null]) + ), + "type": Schema.Literal("compaction_trigger").annotate({ + "description": "The type of the item. Always `compaction_trigger`." + }) +}).annotate({ + "title": "Compaction trigger", + "description": "Compacts the current context. Must be the final input item.", + "identifier": "CompactionTriggerItemParam" +}) +export type ItemReferenceParam = { readonly "type"?: "item_reference" | null; readonly "id": string } +export const ItemReferenceParam = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Union([ + Schema.Literal("item_reference").annotate({ + "description": "The type of item to reference. Always `item_reference`." + }), + Schema.Null + ]) + ), + "id": Schema.String.annotate({ "description": "The ID of the item to reference." }) +}).annotate({ + "title": "Item reference", + "description": "An internal identifier for an item to reference.", + "identifier": "ItemReferenceParam" +}) +export type ProgramItemParam = { + readonly "id": string + readonly "type": "program" + readonly "call_id": string + readonly "code": string + readonly "fingerprint": string +} +export const ProgramItemParam = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of this program item." }), + "type": Schema.Literal("program").annotate({ "description": "The item type. Always `program`." }), + "call_id": Schema.String.annotate({ "description": "The stable call ID of the program item." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "code": Schema.String.annotate({ "description": "The JavaScript source executed by programmatic tool calling." }) + .check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })), + "fingerprint": Schema.String.annotate({ + "description": "Opaque program replay fingerprint that must be round-tripped." + }).check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })) +}).annotate({ "identifier": "ProgramItemParam" }) +export type ProgramOutputItemStatus = "completed" | "incomplete" +export const ProgramOutputItemStatus = Schema.Literals(["completed", "incomplete"]).annotate({ + "identifier": "ProgramOutputItemStatus" +}) +export type ConversationResource = { + readonly "id": string + readonly "object": "conversation" + readonly "metadata": { readonly [x: string]: string } + readonly "created_at": number +} +export const ConversationResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the conversation." }), + "object": Schema.Literal("conversation").annotate({ + "description": "The object type, which is always `conversation`." + }), + "metadata": Schema.Record(Schema.String, Schema.String).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.\n Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters." + }), + "created_at": Schema.Number.annotate({ + "description": "The time at which the conversation was created, measured in seconds since the Unix epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "identifier": "ConversationResource" }) +export type CreateEmbeddingRequest = { + readonly "input": string | ReadonlyArray | ReadonlyArray | ReadonlyArray> + readonly "model": string | "text-embedding-ada-002" | "text-embedding-3-small" | "text-embedding-3-large" + readonly "encoding_format"?: "float" | "base64" + readonly "dimensions"?: number + readonly "user"?: string +} +export const CreateEmbeddingRequest = Schema.Struct({ + "input": Schema.Union([ + Schema.String.annotate({ "title": "string", "description": "The string that will be turned into an embedding." }), + Schema.Array(Schema.String).annotate({ + "title": "array", + "description": "The array of strings that will be turned into an embedding." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2048).annotate({ "expected": "a value with a length of at most 2048" }) + ), + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).annotate({ + "title": "array", + "description": "The array of integers that will be turned into an embedding." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2048).annotate({ "expected": "a value with a length of at most 2048" }) + ), + Schema.Array( + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) + ).annotate({ + "title": "array", + "description": "The array of arrays containing integers that will be turned into an embedding." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2048).annotate({ "expected": "a value with a length of at most 2048" }) + ) + ], { mode: "oneOf" }).annotate({ + "description": + "Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. In addition to the per-input token limit, all embedding models enforce a maximum of 300,000 tokens summed across all inputs in a single request.\n" + }), + "model": Schema.Union([ + Schema.String, + Schema.Literals(["text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large"]) + ]).annotate({ + "description": + "ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.\n" + }), + "encoding_format": Schema.optionalKey( + Schema.Literals(["float", "base64"]).annotate({ + "description": + "The format to return the embeddings in. Can be either `float` or [`base64`](https://pypi.org/project/pybase64/)." + }) + ), + "dimensions": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ) +}).annotate({ "identifier": "CreateEmbeddingRequest" }) +export type Embedding = { + readonly "index": number + readonly "embedding": ReadonlyArray | string + readonly "object": "embedding" +} +export const Embedding = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the embedding in the list of embeddings." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "embedding": Schema.Union([ + Schema.Array( + Schema.Number.annotate({ "format": "float" }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + Schema.String.annotate({ "contentEncoding": "base64" }) + ]).annotate({ + "description": + "The embedding vector, returned as a list of floats when `encoding_format` is `float` (the default), or as a base64-encoded string when `encoding_format` is `base64`. The length of the vector depends on the model as listed in the [embedding guide](https://developers.openai.com/api/docs/guides/embeddings).\n" + }), + "object": Schema.Literal("embedding").annotate({ "description": "The object type, which is always \"embedding\"." }) +}).annotate({ + "description": "Represents an embedding vector returned by embedding endpoint.\n", + "identifier": "Embedding" +}) +export type EvalCustomDataSourceConfig = { + readonly "type": "custom" + readonly "schema": { readonly [x: string]: Schema.Json } +} +export const EvalCustomDataSourceConfig = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "The type of data source. Always `custom`." }), + "schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n" + }) +}).annotate({ + "title": "CustomDataSourceConfig", + "description": + "A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces.\nThe response schema defines the shape of the data that will be:\n- Used to define your testing criteria and\n- What data is required when creating a run\n", + "identifier": "EvalCustomDataSourceConfig" +}) +export type EvalItemContentText = string +export const EvalItemContentText = Schema.String.annotate({ + "title": "Text input", + "description": "A text input to the model.\n", + "identifier": "EvalItemContentText" +}) +export type EvalItemContentOutputText = { readonly "type": "output_text"; readonly "text": string } +export const EvalItemContentOutputText = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ + "description": "The type of the output text. Always `output_text`.\n" + }), + "text": Schema.String.annotate({ "description": "The text output from the model.\n" }) +}).annotate({ + "title": "Output text", + "description": "A text output from the model.\n", + "identifier": "EvalItemContentOutputText" +}) +export type EvalItemInputImage = { + readonly "type": "input_image" + readonly "image_url": string + readonly "detail"?: string +} +export const EvalItemInputImage = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ + "description": "The type of the image input. Always `input_image`.\n" + }), + "image_url": Schema.String.annotate({ "description": "The URL of the image input.\n", "format": "uri" }), + "detail": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.\n" + }) + ) +}).annotate({ + "title": "Input image", + "description": "An image input block used within EvalItem content arrays.", + "identifier": "EvalItemInputImage" +}) +export type InputAudio = { + readonly "type": "input_audio" + readonly "input_audio": { readonly "data": string; readonly "format": "mp3" | "wav" } +} +export const InputAudio = Schema.Struct({ + "type": Schema.Literal("input_audio").annotate({ + "description": "The type of the input item. Always `input_audio`.\n" + }), + "input_audio": Schema.Struct({ + "data": Schema.String.annotate({ "description": "Base64-encoded audio data.\n" }), + "format": Schema.Literals(["mp3", "wav"]).annotate({ + "description": "The format of the audio data. Currently supported formats are `mp3` and\n`wav`.\n" + }) + }) +}).annotate({ "title": "Input audio", "description": "An audio input to the model.\n", "identifier": "InputAudio" }) +export type EvalGraderStringCheck = { + readonly "type": "string_check" + readonly "name": string + readonly "input": string + readonly "reference": string + readonly "operation": "eq" | "ne" | "like" | "ilike" +} +export const EvalGraderStringCheck = Schema.Struct({ + "type": Schema.Literal("string_check").annotate({ + "description": "The object type, which is always `string_check`." + }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "input": Schema.String.annotate({ "description": "The input text. This may include template strings." }), + "reference": Schema.String.annotate({ "description": "The reference text. This may include template strings." }), + "operation": Schema.Literals(["eq", "ne", "like", "ilike"]).annotate({ + "description": "The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`." + }) +}).annotate({ + "title": "StringCheckGrader", + "description": + "A StringCheckGrader object that performs a string comparison between input and reference using a specified operation.\n", + "identifier": "EvalGraderStringCheck" +}) +export type EvalGraderTextSimilarity = { + readonly "type": "text_similarity" + readonly "name": string + readonly "input": string + readonly "reference": string + readonly "evaluation_metric": + | "cosine" + | "fuzzy_match" + | "bleu" + | "gleu" + | "meteor" + | "rouge_1" + | "rouge_2" + | "rouge_3" + | "rouge_4" + | "rouge_5" + | "rouge_l" + readonly "pass_threshold": number +} +export const EvalGraderTextSimilarity = Schema.Struct({ + "type": Schema.Literal("text_similarity").annotate({ "description": "The type of grader." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "input": Schema.String.annotate({ "description": "The text being graded." }), + "reference": Schema.String.annotate({ "description": "The text being graded against." }), + "evaluation_metric": Schema.Literals([ + "cosine", + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l" + ]).annotate({ + "description": + "The evaluation metric to use. One of `cosine`, `fuzzy_match`, `bleu`, \n`gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, \nor `rouge_l`.\n" + }), + "pass_threshold": Schema.Number.annotate({ "description": "The threshold for the score." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) +}).annotate({ "title": "TextSimilarityGrader", "identifier": "EvalGraderTextSimilarity" }) +export type EvalGraderPython = { + readonly "type": "python" + readonly "name": string + readonly "source": string + readonly "image_tag"?: string + readonly "pass_threshold"?: number +} +export const EvalGraderPython = Schema.Struct({ + "type": Schema.Literal("python").annotate({ "description": "The object type, which is always `python`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "source": Schema.String.annotate({ "description": "The source code of the python script." }), + "image_tag": Schema.optionalKey( + Schema.String.annotate({ "description": "The image tag to use for the python script." }) + ), + "pass_threshold": Schema.optionalKey( + Schema.Number.annotate({ "description": "The threshold for the score." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ) +}).annotate({ "title": "PythonGrader", "identifier": "EvalGraderPython" }) +export type CreateEvalCustomDataSourceConfig = { + readonly "type": "custom" + readonly "item_schema": { readonly [x: string]: Schema.Json } + readonly "include_sample_schema"?: boolean +} +export const CreateEvalCustomDataSourceConfig = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "The type of data source. Always `custom`." }), + "item_schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The json schema for each row in the data source." + }), + "include_sample_schema": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the eval should expect you to populate the sample namespace (ie, by generating responses off of your data source)" + }) + ) +}).annotate({ + "title": "CustomDataSourceConfig", + "description": + "A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs.\nThis schema is used to define the shape of the data that will be:\n- Used to define your testing criteria and\n- What data is required when creating a run\n", + "identifier": "CreateEvalCustomDataSourceConfig" +}) +export type CreateEvalLogsDataSourceConfig = { + readonly "type": "logs" + readonly "metadata"?: { readonly [x: string]: Schema.Json } +} +export const CreateEvalLogsDataSourceConfig = Schema.Struct({ + "type": Schema.Literal("logs").annotate({ "description": "The type of data source. Always `logs`." }), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Metadata filters for the logs data source." + }) + ) +}).annotate({ + "title": "LogsDataSourceConfig", + "description": + "A data source config which specifies the metadata property of your logs query.\nThis is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.\n", + "identifier": "CreateEvalLogsDataSourceConfig" +}) +export type CreateEvalStoredCompletionsDataSourceConfig = { + readonly "type": "stored_completions" + readonly "metadata"?: { readonly [x: string]: Schema.Json } +} +export const CreateEvalStoredCompletionsDataSourceConfig = Schema.Struct({ + "type": Schema.Literal("stored_completions").annotate({ + "description": "The type of data source. Always `stored_completions`." + }), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Metadata filters for the stored completions data source." + }) + ) +}).annotate({ + "title": "StoredCompletionsDataSourceConfig", + "description": "Deprecated in favor of LogsDataSourceConfig.\n", + "identifier": "CreateEvalStoredCompletionsDataSourceConfig" +}) +export type EvalJsonlFileContentSource = { + readonly "type": "file_content" + readonly "content": ReadonlyArray< + { + readonly "item": { readonly [x: string]: Schema.Json } + readonly "sample"?: { readonly [x: string]: Schema.Json } + } + > +} +export const EvalJsonlFileContentSource = Schema.Struct({ + "type": Schema.Literal("file_content").annotate({ + "description": "The type of jsonl source. Always `file_content`." + }), + "content": Schema.Array( + Schema.Struct({ + "item": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + "sample": Schema.optionalKey(Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))) + }) + ).annotate({ "description": "The content of the jsonl file." }) +}).annotate({ "title": "EvalJsonlFileContentSource", "identifier": "EvalJsonlFileContentSource" }) +export type EvalJsonlFileIdSource = { readonly "type": "file_id"; readonly "id": string } +export const EvalJsonlFileIdSource = Schema.Struct({ + "type": Schema.Literal("file_id").annotate({ "description": "The type of jsonl source. Always `file_id`." }), + "id": Schema.String.annotate({ "description": "The identifier of the file." }) +}).annotate({ "title": "EvalJsonlFileIdSource", "identifier": "EvalJsonlFileIdSource" }) +export type EvalApiError = { readonly "code": string; readonly "message": string } +export const EvalApiError = Schema.Struct({ + "code": Schema.String.annotate({ "description": "The error code." }), + "message": Schema.String.annotate({ "description": "The error message." }) +}).annotate({ + "title": "EvalApiError", + "description": "An object representing an error response from the Eval API.\n", + "identifier": "EvalApiError" +}) +export type EvalRunOutputItemResult = { + readonly "name": string + readonly "type"?: string + readonly "score": number + readonly "passed": boolean + readonly "sample"?: { readonly [x: string]: Schema.Json } | null +} & { readonly [x: string]: Schema.Json } +export const EvalRunOutputItemResult = Schema.StructWithRest( + Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "type": Schema.optionalKey( + Schema.String.annotate({ "description": "The grader type (for example, \"string-check-grader\")." }) + ), + "score": Schema.Number.annotate({ "description": "The numeric score produced by the grader." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "passed": Schema.Boolean.annotate({ "description": "Whether the grader considered the output a pass." }), + "sample": Schema.optionalKey( + Schema.Union([Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), Schema.Null]) + .annotate({ "description": "Optional sample or intermediate data produced by the grader." }) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] +).annotate({ + "title": "EvalRunOutputItemResult", + "description": "A single grader result for an evaluation run output item.\n", + "identifier": "EvalRunOutputItemResult" +}) +export type OpenAIFile = + | null + | string + | number + | boolean + | { + readonly "id": string + readonly "bytes": number | null + readonly "created_at": number + readonly "expires_at"?: number | null + readonly "filename": string + readonly "object": "file" + readonly "purpose": + | "assistants" + | "assistants_output" + | "batch" + | "batch_output" + | "fine-tune" + | "fine-tune-results" + | "vision" + | "user_data" + readonly "status": "uploaded" | "processed" | "error" + readonly "status_details"?: string | null + } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const OpenAIFile = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The file identifier, which can be referenced in the API endpoints." + }), + "bytes": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ + "description": + "The size of the file, in bytes. In a completed file upload response, this can\nbe null when the file size is not yet available.\n" + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the file was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the file will expire.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "filename": Schema.String.annotate({ "description": "The name of the file." }), + "object": Schema.Literal("file").annotate({ "description": "The object type, which is always `file`." }), + "purpose": Schema.Literals([ + "assistants", + "assistants_output", + "batch", + "batch_output", + "fine-tune", + "fine-tune-results", + "vision", + "user_data" + ]).annotate({ + "description": + "The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, `fine-tune`, `fine-tune-results`, `vision`, and `user_data`." + }), + "status": Schema.Literals(["uploaded", "processed", "error"]).annotate({ + "description": + "Deprecated. The current status of the file, which can be either `uploaded`, `processed`, or `error`." + }), + "status_details": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "Deprecated. For details on why a fine-tuning training file failed validation, see the `error` field on `fine_tuning.job`. Completed file upload responses can return null when these details are unset." + }) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]).annotate({ + "title": "OpenAIFile", + "description": "The `File` object represents a document that has been uploaded to OpenAI.", + "identifier": "OpenAIFile" +}) +export type FileExpirationAfter = { readonly "anchor": "created_at"; readonly "seconds": number } +export const FileExpirationAfter = Schema.Struct({ + "anchor": Schema.Literal("created_at").annotate({ + "description": "Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`." + }), + "seconds": Schema.Number.annotate({ + "description": + "The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(3600).annotate({ "expected": "a value greater than or equal to 3600" }) + ).check(Schema.isLessThanOrEqualTo(2592000).annotate({ "expected": "a value less than or equal to 2592000" })) +}).annotate({ + "title": "File expiration policy", + "description": + "The expiration policy for a file. By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted.", + "identifier": "FileExpirationAfter" +}) +export type DeleteFileResponse = { readonly "id": string; readonly "object": "file"; readonly "deleted": boolean } +export const DeleteFileResponse = Schema.Struct({ + "id": Schema.String, + "object": Schema.Literal("file"), + "deleted": Schema.Boolean +}).annotate({ "identifier": "DeleteFileResponse" }) +export type GraderStringCheck = { + readonly "type": "string_check" + readonly "name": string + readonly "input": string + readonly "reference": string + readonly "operation": "eq" | "ne" | "like" | "ilike" +} +export const GraderStringCheck = Schema.Struct({ + "type": Schema.Literal("string_check").annotate({ + "description": "The object type, which is always `string_check`." + }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "input": Schema.String.annotate({ "description": "The input text. This may include template strings." }), + "reference": Schema.String.annotate({ "description": "The reference text. This may include template strings." }), + "operation": Schema.Literals(["eq", "ne", "like", "ilike"]).annotate({ + "description": "The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`." + }) +}).annotate({ + "title": "StringCheckGrader", + "description": + "A StringCheckGrader object that performs a string comparison between input and reference using a specified operation.\n", + "identifier": "GraderStringCheck" +}) +export type GraderTextSimilarity = { + readonly "type": "text_similarity" + readonly "name": string + readonly "input": string + readonly "reference": string + readonly "evaluation_metric": + | "cosine" + | "fuzzy_match" + | "bleu" + | "gleu" + | "meteor" + | "rouge_1" + | "rouge_2" + | "rouge_3" + | "rouge_4" + | "rouge_5" + | "rouge_l" +} +export const GraderTextSimilarity = Schema.Struct({ + "type": Schema.Literal("text_similarity").annotate({ "description": "The type of grader." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "input": Schema.String.annotate({ "description": "The text being graded." }), + "reference": Schema.String.annotate({ "description": "The text being graded against." }), + "evaluation_metric": Schema.Literals([ + "cosine", + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l" + ]).annotate({ + "description": + "The evaluation metric to use. One of `cosine`, `fuzzy_match`, `bleu`, \n`gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, \nor `rouge_l`.\n" + }) +}).annotate({ + "title": "TextSimilarityGrader", + "description": "A TextSimilarityGrader object which grades text based on similarity metrics.\n", + "identifier": "GraderTextSimilarity" +}) +export type GraderPython = { + readonly "type": "python" + readonly "name": string + readonly "source": string + readonly "image_tag"?: string +} +export const GraderPython = Schema.Struct({ + "type": Schema.Literal("python").annotate({ "description": "The object type, which is always `python`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "source": Schema.String.annotate({ "description": "The source code of the python script." }), + "image_tag": Schema.optionalKey( + Schema.String.annotate({ "description": "The image tag to use for the python script." }) + ) +}).annotate({ + "title": "PythonGrader", + "description": "A PythonGrader object that runs a python script on the input.\n", + "identifier": "GraderPython" +}) +export type RunGraderResponse = { + readonly "reward": number + readonly "metadata": { + readonly "name": string + readonly "type": string + readonly "errors": { + readonly "formula_parse_error": boolean + readonly "sample_parse_error": boolean + readonly "truncated_observation_error": boolean + readonly "unresponsive_reward_error": boolean + readonly "invalid_variable_error": boolean + readonly "other_error": boolean + readonly "python_grader_server_error": boolean + readonly "python_grader_server_error_type": string | null + readonly "python_grader_runtime_error": boolean + readonly "python_grader_runtime_error_details": string | null + readonly "model_grader_server_error": boolean + readonly "model_grader_refusal_error": boolean + readonly "model_grader_parse_error": boolean + readonly "model_grader_server_error_details": string | null + } + readonly "execution_time": number + readonly "scores": { readonly [x: string]: Schema.Json } + readonly "token_usage": { + readonly "prompt_tokens": number + readonly "total_tokens": number + readonly "completion_tokens"?: number | null + readonly "cached_tokens"?: number | null + } | null + readonly "sampled_model_name": string | null + } + readonly "sub_rewards": { readonly [x: string]: Schema.Json } + readonly "model_grader_token_usage_per_model": { readonly [x: string]: Schema.Json } +} +export const RunGraderResponse = Schema.Struct({ + "reward": Schema.Finite, + "metadata": Schema.Struct({ + "name": Schema.String, + "type": Schema.String, + "errors": Schema.Struct({ + "formula_parse_error": Schema.Boolean, + "sample_parse_error": Schema.Boolean, + "truncated_observation_error": Schema.Boolean, + "unresponsive_reward_error": Schema.Boolean, + "invalid_variable_error": Schema.Boolean, + "other_error": Schema.Boolean, + "python_grader_server_error": Schema.Boolean, + "python_grader_server_error_type": Schema.Union([Schema.String, Schema.Null]), + "python_grader_runtime_error": Schema.Boolean, + "python_grader_runtime_error_details": Schema.Union([Schema.String, Schema.Null]), + "model_grader_server_error": Schema.Boolean, + "model_grader_refusal_error": Schema.Boolean, + "model_grader_parse_error": Schema.Boolean, + "model_grader_server_error_details": Schema.Union([Schema.String, Schema.Null]) + }), + "execution_time": Schema.Finite, + "scores": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + "token_usage": Schema.Union([ + Schema.Struct({ + "prompt_tokens": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "total_tokens": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "completion_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + ), + "cached_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + ) + }), + Schema.Null + ]), + "sampled_model_name": Schema.Union([Schema.String, Schema.Null]) + }), + "sub_rewards": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + "model_grader_token_usage_per_model": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) +}).annotate({ "identifier": "RunGraderResponse" }) +export type FineTuningCheckpointPermission = { + readonly "id": string + readonly "created_at": number + readonly "project_id": string + readonly "object": "checkpoint.permission" +} +export const FineTuningCheckpointPermission = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The permission identifier, which can be referenced in the API endpoints." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the permission was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "project_id": Schema.String.annotate({ "description": "The project identifier that the permission is for." }), + "object": Schema.Literal("checkpoint.permission").annotate({ + "description": "The object type, which is always \"checkpoint.permission\"." + }) +}).annotate({ + "title": "FineTuningCheckpointPermission", + "description": "The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint.\n", + "identifier": "FineTuningCheckpointPermission" +}) +export type CreateFineTuningCheckpointPermissionRequest = { readonly "project_ids": ReadonlyArray } +export const CreateFineTuningCheckpointPermissionRequest = Schema.Struct({ + "project_ids": Schema.Array(Schema.String).annotate({ "description": "The project identifiers to grant access to." }) +}).annotate({ "identifier": "CreateFineTuningCheckpointPermissionRequest" }) +export type DeleteFineTuningCheckpointPermissionResponse = { + readonly "id": string + readonly "object": "checkpoint.permission" + readonly "deleted": boolean +} +export const DeleteFineTuningCheckpointPermissionResponse = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The ID of the fine-tuned model checkpoint permission that was deleted." + }), + "object": Schema.Literal("checkpoint.permission").annotate({ + "description": "The object type, which is always \"checkpoint.permission\"." + }), + "deleted": Schema.Boolean.annotate({ + "description": "Whether the fine-tuned model checkpoint permission was successfully deleted." + }) +}).annotate({ "identifier": "DeleteFineTuningCheckpointPermissionResponse" }) +export type FineTuningIntegration = { + readonly "type": "wandb" + readonly "wandb": { + readonly "project": string + readonly "name"?: string | null + readonly "entity"?: string | null + readonly "tags"?: ReadonlyArray + } +} +export const FineTuningIntegration = Schema.Struct({ + "type": Schema.Literal("wandb").annotate({ + "description": "The type of the integration being enabled for the fine-tuning job" + }), + "wandb": Schema.Struct({ + "project": Schema.String.annotate({ + "description": "The name of the project that the new run will be created under.\n" + }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "A display name to set for the run. If not set, we will use the Job ID as the name.\n" + }), + Schema.Null + ]) + ), + "entity": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The entity to use for the run. This allows you to set the team or username of the WandB user that you would\nlike associated with the run. If not set, the default entity for the registered WandB API key is used.\n" + }), + Schema.Null + ]) + ), + "tags": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some\ndefault tags are generated by OpenAI: \"openai/finetune\", \"openai/{base-model}\", \"openai/{ftjob-abcdef}\".\n" + }) + ) + }).annotate({ + "description": + "The settings for your integration with Weights and Biases. This payload specifies the project that\nmetrics will be sent to. Optionally, you can set an explicit display name for your run, add tags\nto your run, and set a default entity (team, username, etc) to be associated with your run.\n" + }) +}).annotate({ "title": "Fine-Tuning Job Integration", "identifier": "FineTuningIntegration" }) +export type FineTuneSupervisedHyperparameters = { + readonly "batch_size"?: "auto" | number + readonly "learning_rate_multiplier"?: "auto" | number + readonly "n_epochs"?: "auto" | number +} +export const FineTuneSupervisedHyperparameters = Schema.Struct({ + "batch_size": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(256).annotate({ "expected": "a value less than or equal to 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.\n" + }) + ), + "learning_rate_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.\n" + }) + ), + "n_epochs": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.\n" + }) + ) +}).annotate({ + "description": "The hyperparameters used for the fine-tuning job.", + "identifier": "FineTuneSupervisedHyperparameters" +}) +export type FineTuneDPOHyperparameters = { + readonly "beta"?: "auto" | number + readonly "batch_size"?: "auto" | number + readonly "learning_rate_multiplier"?: "auto" | number + readonly "n_epochs"?: "auto" | number +} +export const FineTuneDPOHyperparameters = Schema.Struct({ + "beta": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })) + .check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The beta value for the DPO method. A higher beta value will increase the weight of the penalty between the policy and reference model.\n" + }) + ), + "batch_size": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(256).annotate({ "expected": "a value less than or equal to 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.\n" + }) + ), + "learning_rate_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.\n" + }) + ), + "n_epochs": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.\n" + }) + ) +}).annotate({ + "description": "The hyperparameters used for the DPO fine-tuning job.", + "identifier": "FineTuneDPOHyperparameters" +}) +export type FineTuneReinforcementHyperparameters = { + readonly "batch_size"?: "auto" | number + readonly "learning_rate_multiplier"?: "auto" | number + readonly "n_epochs"?: "auto" | number + readonly "reasoning_effort"?: "default" | "low" | "medium" | "high" + readonly "compute_multiplier"?: "auto" | number + readonly "eval_interval"?: "auto" | number + readonly "eval_samples"?: "auto" | number +} +export const FineTuneReinforcementHyperparameters = Schema.Struct({ + "batch_size": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(256).annotate({ "expected": "a value less than or equal to 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.\n" + }) + ), + "learning_rate_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.\n" + }) + ), + "n_epochs": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.\n" + }) + ), + "reasoning_effort": Schema.optionalKey( + Schema.Literals(["default", "low", "medium", "high"]).annotate({ "description": "Level of reasoning effort.\n" }) + ), + "compute_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isLessThanOrEqualTo(10).annotate({ "expected": "a value less than or equal to 10" })) + .check(Schema.isGreaterThan(0.00001).annotate({ "expected": "a value greater than 0.00001" })) + ], { mode: "oneOf" }).annotate({ + "description": "Multiplier on amount of compute used for exploring search space during training.\n" + }) + ), + "eval_interval": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ], { mode: "oneOf" }).annotate({ "description": "The number of training steps between evaluation runs.\n" }) + ), + "eval_samples": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ], { mode: "oneOf" }).annotate({ "description": "Number of evaluation samples to generate per training step.\n" }) + ) +}).annotate({ + "description": "The hyperparameters used for the reinforcement fine-tuning job.", + "identifier": "FineTuneReinforcementHyperparameters" +}) +export type FineTuningJobCheckpoint = { + readonly "id": string + readonly "created_at": number + readonly "fine_tuned_model_checkpoint": string + readonly "step_number": number + readonly "metrics": { + readonly "step"?: number + readonly "train_loss"?: number + readonly "train_mean_token_accuracy"?: number + readonly "valid_loss"?: number + readonly "valid_mean_token_accuracy"?: number + readonly "full_valid_loss"?: number + readonly "full_valid_mean_token_accuracy"?: number + } + readonly "fine_tuning_job_id": string + readonly "object": "fine_tuning.job.checkpoint" +} +export const FineTuningJobCheckpoint = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The checkpoint identifier, which can be referenced in the API endpoints." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the checkpoint was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "fine_tuned_model_checkpoint": Schema.String.annotate({ + "description": "The name of the fine-tuned checkpoint model that is created." + }), + "step_number": Schema.Number.annotate({ "description": "The step number that the checkpoint was created at." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "metrics": Schema.Struct({ + "step": Schema.optionalKey(Schema.Finite), + "train_loss": Schema.optionalKey(Schema.Finite), + "train_mean_token_accuracy": Schema.optionalKey(Schema.Finite), + "valid_loss": Schema.optionalKey(Schema.Finite), + "valid_mean_token_accuracy": Schema.optionalKey(Schema.Finite), + "full_valid_loss": Schema.optionalKey(Schema.Finite), + "full_valid_mean_token_accuracy": Schema.optionalKey(Schema.Finite) + }).annotate({ "description": "Metrics at the step number during the fine-tuning job." }), + "fine_tuning_job_id": Schema.String.annotate({ + "description": "The name of the fine-tuning job that this checkpoint was created from." + }), + "object": Schema.Literal("fine_tuning.job.checkpoint").annotate({ + "description": "The object type, which is always \"fine_tuning.job.checkpoint\"." + }) +}).annotate({ + "title": "FineTuningJobCheckpoint", + "description": + "The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use.\n", + "identifier": "FineTuningJobCheckpoint" +}) +export type FineTuningJobEvent = { + readonly "object": "fine_tuning.job.event" + readonly "id": string + readonly "created_at": number + readonly "level": "info" | "warn" | "error" + readonly "message": string + readonly "type"?: "message" | "metrics" + readonly "data"?: { readonly [x: string]: Schema.Json } | null +} +export const FineTuningJobEvent = Schema.Struct({ + "object": Schema.Literal("fine_tuning.job.event").annotate({ + "description": "The object type, which is always \"fine_tuning.job.event\"." + }), + "id": Schema.String.annotate({ "description": "The object identifier." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the fine-tuning job was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "level": Schema.Literals(["info", "warn", "error"]).annotate({ "description": "The log level of the event." }), + "message": Schema.String.annotate({ "description": "The message of the event." }), + "type": Schema.optionalKey(Schema.Literals(["message", "metrics"]).annotate({ "description": "The type of event." })), + "data": Schema.optionalKey( + Schema.Union([Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), Schema.Null]) + .annotate({ "description": "The data associated with the event." }) + ) +}).annotate({ "description": "Fine-tuning job event object", "identifier": "FineTuningJobEvent" }) +export type ImageRefParam = { readonly "image_url": string; readonly "file_id"?: string } | { + readonly "image_url"?: string + readonly "file_id": string +} +export const ImageRefParam = Schema.Union([ + Schema.Struct({ + "image_url": Schema.String.annotate({ + "description": "A fully qualified URL or base64-encoded data URL.", + "format": "uri" + }).check(Schema.isMaxCodePoints(20971520).annotate({ "expected": "a string with at most 20971520 code points" })), + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The File API ID of an uploaded image to use as input." }) + ) + }), + Schema.Struct({ + "image_url": Schema.optionalKey( + Schema.String.annotate({ "description": "A fully qualified URL or base64-encoded data URL.", "format": "uri" }) + .check(Schema.isMaxCodePoints(20971520).annotate({ "expected": "a string with at most 20971520 code points" })) + ), + "file_id": Schema.String.annotate({ "description": "The File API ID of an uploaded image to use as input." }) + }) +], { mode: "oneOf" }).annotate({ + "description": + "Reference an input image by either URL or uploaded file ID.\nProvide exactly one of `image_url` or `file_id`.\n", + "identifier": "ImageRefParam" +}) +export type PartialImages = number | null +export const PartialImages = Schema.Union([ + Schema.Number.annotate({ + "description": + "The number of partial images to generate. This parameter is used for\nstreaming responses that return partial images. Value must be between 0 and 3.\nWhen set to 0, the response will be a single image sent in one streaming event.\n\nNote that the final image may be sent before the full number of partial images\nare generated if the full image is generated more quickly.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(3).annotate({ "expected": "a value less than or equal to 3" })), + Schema.Null +]).annotate({ "identifier": "PartialImages" }) +export type Image = { readonly "b64_json"?: string; readonly "url"?: string; readonly "revised_prompt"?: string } +export const Image = Schema.Struct({ + "b64_json": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The base64-encoded JSON of the generated image. Returned by default for GPT image models, or when `response_format` is set to `b64_json` for models that support that parameter." + }) + ), + "url": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The URL of the generated image when `response_format` is set to `url` for models that support that parameter. Unsupported for GPT image models.", + "format": "uri" + }) + ), + "revised_prompt": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The revised prompt used to generate the image, for models that support prompt revision. Not returned by GPT image models." + }) + ) +}).annotate({ + "description": "Represents the content or the URL of an image generated by the OpenAI API.", + "identifier": "Image" +}) +export type ImageGenOutputTokensDetails = { readonly "image_tokens": number; readonly "text_tokens": number } +export const ImageGenOutputTokensDetails = Schema.Struct({ + "image_tokens": Schema.Number.annotate({ "description": "The number of image output tokens generated by the model." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "text_tokens": Schema.Number.annotate({ "description": "The number of text output tokens generated by the model." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "Image generation output token details", + "description": "The output token details for the image generation.", + "identifier": "ImageGenOutputTokensDetails" +}) +export type ImageGenInputUsageDetails = { readonly "text_tokens": number; readonly "image_tokens": number } +export const ImageGenInputUsageDetails = Schema.Struct({ + "text_tokens": Schema.Number.annotate({ "description": "The number of text tokens in the input prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "image_tokens": Schema.Number.annotate({ "description": "The number of image tokens in the input prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Input usage details", + "description": "The input tokens detailed information for the image generation.", + "identifier": "ImageGenInputUsageDetails" +}) +export type ImageEditPartialImageEvent = { + readonly "type": "image_edit.partial_image" + readonly "b64_json": string + readonly "created_at": number + readonly "size": string | "1024x1024" | "1024x1536" | "1536x1024" | "auto" + readonly "quality": "low" | "medium" | "high" | "xhigh" | "max" | "auto" + readonly "background": "transparent" | "opaque" | "auto" + readonly "output_format": "png" | "webp" | "jpeg" + readonly "partial_image_index": number +} +export const ImageEditPartialImageEvent = Schema.Struct({ + "type": Schema.Literal("image_edit.partial_image").annotate({ + "description": "The type of the event. Always `image_edit.partial_image`.\n" + }), + "b64_json": Schema.String.annotate({ + "description": "Base64-encoded partial image data, suitable for rendering as an image.\n" + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp when the event was created.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "size": Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024", "auto"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + "quality": Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": "The quality setting for the requested edited image.\n" + }), + "background": Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "description": "The background setting for the requested edited image.\n" + }), + "output_format": Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format for the requested edited image.\n" + }), + "partial_image_index": Schema.Number.annotate({ "description": "0-based index for the partial image (streaming).\n" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a partial image is available during image editing streaming.\n", + "identifier": "ImageEditPartialImageEvent" +}) +export type ImagesUsage = { + readonly "total_tokens": number + readonly "input_tokens": number + readonly "output_tokens": number + readonly "input_tokens_details": { readonly "text_tokens": number; readonly "image_tokens": number } +} +export const ImagesUsage = Schema.Struct({ + "total_tokens": Schema.Number.annotate({ + "description": "The total number of tokens (images and text) used for the image generation.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "input_tokens": Schema.Number.annotate({ + "description": "The number of tokens (images and text) in the input prompt." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_tokens": Schema.Number.annotate({ "description": "The number of image tokens in the output image." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "input_tokens_details": Schema.Struct({ + "text_tokens": Schema.Number.annotate({ "description": "The number of text tokens in the input prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "image_tokens": Schema.Number.annotate({ "description": "The number of image tokens in the input prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "The input tokens detailed information for the image generation." }) +}).annotate({ + "description": "For the GPT image models only, the token usage information for the image generation.\n", + "identifier": "ImagesUsage" +}) +export type ImageGenPartialImageEvent = { + readonly "type": "image_generation.partial_image" + readonly "b64_json": string + readonly "created_at": number + readonly "size": string | "1024x1024" | "1024x1536" | "1536x1024" | "auto" + readonly "quality": "low" | "medium" | "high" | "xhigh" | "max" | "auto" + readonly "background": "transparent" | "opaque" | "auto" + readonly "output_format": "png" | "webp" | "jpeg" + readonly "partial_image_index": number +} +export const ImageGenPartialImageEvent = Schema.Struct({ + "type": Schema.Literal("image_generation.partial_image").annotate({ + "description": "The type of the event. Always `image_generation.partial_image`.\n" + }), + "b64_json": Schema.String.annotate({ + "description": "Base64-encoded partial image data, suitable for rendering as an image.\n" + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp when the event was created.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "size": Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024", "auto"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + "quality": Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": "The quality setting for the requested image.\n" + }), + "background": Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "description": "The background setting for the requested image.\n" + }), + "output_format": Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format for the requested image.\n" + }), + "partial_image_index": Schema.Number.annotate({ "description": "0-based index for the partial image (streaming).\n" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a partial image is available during image generation streaming.\n", + "identifier": "ImageGenPartialImageEvent" +}) +export type ModelIdsLive = string | "gpt-live-1" +export const ModelIdsLive = Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + Schema.Literal("gpt-live-1") +]).annotate({ + "description": + "The Live model. Required in the session configuration for every transport; do not pass it as a URL query parameter.", + "identifier": "ModelIdsLive" +}) +export type Instructions = string | null +export const Instructions = Schema.Union([ + Schema.String.annotate({ + "description": + "Frontend instructions for voice, conversation, interruptions, and when to delegate. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); put business rules and tool workflows in a separate [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). Limited to 16,384 client-supplied tokens. Omitted or blank instructions use server defaults. Immutable after startup." + }), + Schema.Null +]).annotate({ "identifier": "instructions" }) +export type LiveCustomVoiceParam = { readonly "id": string } +export const LiveCustomVoiceParam = Schema.Struct({ + "id": Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })) +}).annotate({ "identifier": "LiveCustomVoiceParam" }) +export type LiveClientDelegationParam = { readonly "type": "client" } +export const LiveClientDelegationParam = Schema.Struct({ + "type": Schema.Literal("client").annotate({ + "description": "The delegation owner. Always `client` for tasks handled by your application." + }) +}).annotate({ + "description": + "Delegate tasks to your application. The Live session emits delegation events that your backend handles.", + "identifier": "LiveClientDelegationParam" +}) +export type LiveResponsesServiceTier = "auto" | "default" | "fast_tier_temp_pilot" | "flex" | "priority" | "ultrafast" +export const LiveResponsesServiceTier = Schema.Literals([ + "auto", + "default", + "fast_tier_temp_pilot", + "flex", + "priority", + "ultrafast" +]).annotate({ "identifier": "LiveResponsesServiceTier" }) +export type LiveReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" +export const LiveReasoningEffort = Schema.Literals(["none", "minimal", "low", "medium", "high", "xhigh"]).annotate({ + "identifier": "LiveReasoningEffort" +}) +export type LiveReasoningSummary = "concise" | "detailed" | "auto" +export const LiveReasoningSummary = Schema.Literals(["concise", "detailed", "auto"]).annotate({ + "identifier": "LiveReasoningSummary" +}) +export type LiveTextVerbosity = "low" | "medium" | "high" +export const LiveTextVerbosity = Schema.Literals(["low", "medium", "high"]).annotate({ + "identifier": "LiveTextVerbosity" +}) +export type LiveFunctionToolInputParam = { + readonly "type": "function" + readonly "name": string + readonly "description"?: string | null + readonly "parameters"?: { readonly [x: string]: Schema.Json } | null + readonly "strict"?: boolean | null +} +export const LiveFunctionToolInputParam = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The tool type. Always `function`." }), + "name": Schema.String.annotate({ + "description": "The name the delegated Responses model uses when calling this function." + }), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "What the function does and when the delegated Responses model should call it." + }), + Schema.Null + ]) + ), + "parameters": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON Schema object describing the arguments accepted by the function." + }), + Schema.Null + ]) + ), + "strict": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": "Whether the delegated Responses model must follow the function’s parameter schema exactly." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "A function tool available to the Responses backend when the Live model delegates a task.", + "identifier": "LiveFunctionToolInputParam" +}) +export type LiveWebSearchToolInputParam = { readonly "type": "web_search" } +export const LiveWebSearchToolInputParam = Schema.Struct({ + "type": Schema.Literal("web_search").annotate({ "description": "The tool type. Always `web_search`." }) +}).annotate({ + "description": "A web search tool available to the Live session’s Responses backend.", + "identifier": "LiveWebSearchToolInputParam" +}) +export type LiveFileSearchToolInputParam = { readonly "type": "file_search" } +export const LiveFileSearchToolInputParam = Schema.Struct({ "type": Schema.Literal("file_search") }).annotate({ + "identifier": "LiveFileSearchToolInputParam" +}) +export type LiveCodeInterpreterToolInputParam = { readonly "type": "code_interpreter" } +export const LiveCodeInterpreterToolInputParam = Schema.Struct({ "type": Schema.Literal("code_interpreter") }).annotate( + { "identifier": "LiveCodeInterpreterToolInputParam" } +) +export type LiveContainerMemoryLimit = "1g" | "4g" | "16g" | "64g" +export const LiveContainerMemoryLimit = Schema.Literals(["1g", "4g", "16g", "64g"]).annotate({ + "identifier": "LiveContainerMemoryLimit" +}) +export type LiveContainerNetworkPolicyDisabledParam = { readonly "type": "disabled" } +export const LiveContainerNetworkPolicyDisabledParam = Schema.Struct({ + "type": Schema.Literal("disabled").annotate({ "description": "Disable outbound network access. Always `disabled`." }) +}).annotate({ "identifier": "LiveContainerNetworkPolicyDisabledParam" }) +export type LiveHostedShellNetworkPolicyAllowlistParam = { + readonly "type": "allowlist" + readonly "allowed_domains": ReadonlyArray +} +export const LiveHostedShellNetworkPolicyAllowlistParam = Schema.Struct({ + "type": Schema.Literal("allowlist").annotate({ + "description": "Allow outbound network access only to specified domains. Always `allowlist`." + }), + "allowed_domains": Schema.Array(Schema.String).annotate({ + "description": "A list of allowed domains when type is `allowlist`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) +}).annotate({ "identifier": "LiveHostedShellNetworkPolicyAllowlistParam" }) +export type LiveSkillReferenceParam = { + readonly "type": "skill_reference" + readonly "skill_id": string + readonly "version"?: string | null +} +export const LiveSkillReferenceParam = Schema.Struct({ + "type": Schema.Literal("skill_reference").annotate({ + "description": "References a skill created with the /v1/skills endpoint." + }), + "skill_id": Schema.String.annotate({ "description": "The ID of the referenced skill." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "version": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "Optional skill version. Use a positive integer or 'latest'. Omit for default." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "LiveSkillReferenceParam" }) +export type LiveInlineSkillSourceParam = { + readonly "type": "base64" + readonly "media_type": "application/zip" + readonly "data": string +} +export const LiveInlineSkillSourceParam = Schema.Struct({ + "type": Schema.Literal("base64").annotate({ + "description": "The type of the inline skill source. Must be `base64`." + }), + "media_type": Schema.Literal("application/zip").annotate({ + "description": "The media type of the inline skill payload. Must be `application/zip`." + }), + "data": Schema.String.annotate({ "description": "Base64-encoded skill zip bundle." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(70254592).annotate({ "expected": "a string with at most 70254592 code points" })) +}).annotate({ "description": "Inline skill payload", "identifier": "LiveInlineSkillSourceParam" }) +export type LiveContainerReferenceParam = { readonly "type": "container_reference"; readonly "container_id": string } +export const LiveContainerReferenceParam = Schema.Struct({ + "type": Schema.Literal("container_reference").annotate({ + "description": "References a container created with the /v1/containers endpoint" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the referenced container." }) +}).annotate({ "identifier": "LiveContainerReferenceParam" }) +export type LiveLocalSkillParam = { readonly "name": string; readonly "description": string; readonly "path": string } +export const LiveLocalSkillParam = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the skill." }), + "description": Schema.String.annotate({ "description": "The description of the skill." }), + "path": Schema.String.annotate({ "description": "The path to the directory containing the skill." }) +}).annotate({ "identifier": "LiveLocalSkillParam" }) +export type LiveImageGenerationToolInputParam = { readonly "type": "image_generation" } +export const LiveImageGenerationToolInputParam = Schema.Struct({ "type": Schema.Literal("image_generation") }).annotate( + { "identifier": "LiveImageGenerationToolInputParam" } +) +export type LiveMCPToolInputParam = { readonly "type": "mcp" } +export const LiveMCPToolInputParam = Schema.Struct({ "type": Schema.Literal("mcp") }).annotate({ + "identifier": "LiveMCPToolInputParam" +}) +export type LiveCustomToolInputParam = { readonly "type": "custom" } +export const LiveCustomToolInputParam = Schema.Struct({ "type": Schema.Literal("custom") }).annotate({ + "identifier": "LiveCustomToolInputParam" +}) +export type LiveNamespaceToolInputParam = { readonly "type": "namespace" } +export const LiveNamespaceToolInputParam = Schema.Struct({ "type": Schema.Literal("namespace") }).annotate({ + "identifier": "LiveNamespaceToolInputParam" +}) +export type LiveToolSearchToolInputParam = { readonly "type": "tool_search" } +export const LiveToolSearchToolInputParam = Schema.Struct({ "type": Schema.Literal("tool_search") }).annotate({ + "identifier": "LiveToolSearchToolInputParam" +}) +export type LiveProgrammaticToolInputParam = { readonly "type": "programmatic_tool_calling" } +export const LiveProgrammaticToolInputParam = Schema.Struct({ "type": Schema.Literal("programmatic_tool_calling") }) + .annotate({ "identifier": "LiveProgrammaticToolInputParam" }) +export type LiveComputerToolInputParam = { readonly "type": "computer" } +export const LiveComputerToolInputParam = Schema.Struct({ "type": Schema.Literal("computer") }).annotate({ + "identifier": "LiveComputerToolInputParam" +}) +export type LiveApplyPatchToolInputParam = { readonly "type": "apply_patch" } +export const LiveApplyPatchToolInputParam = Schema.Struct({ "type": Schema.Literal("apply_patch") }).annotate({ + "identifier": "LiveApplyPatchToolInputParam" +}) +export type LiveToolChoiceEnum = "auto" | "none" | "required" +export const LiveToolChoiceEnum = Schema.Literals(["auto", "none", "required"]).annotate({ + "identifier": "LiveToolChoiceEnum" +}) +export type LiveFunctionToolChoiceParam = { readonly "type": "function"; readonly "name": string } +export const LiveFunctionToolChoiceParam = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The tool to call. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function tool to call." }) +}).annotate({ "identifier": "LiveFunctionToolChoiceParam" }) +export type LiveMCPToolChoiceParam = { + readonly "type": "mcp" + readonly "server_label": string + readonly "name"?: string | null +} +export const LiveMCPToolChoiceParam = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The tool to call. Always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server to call." }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The name of the MCP tool to call. If omitted, the server may choose a default." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "LiveMCPToolChoiceParam" }) +export type LiveSpecificFileSearchParam = { readonly "type": "file_search" } +export const LiveSpecificFileSearchParam = Schema.Struct({ + "type": Schema.Literal("file_search").annotate({ "description": "The tool to call. Always `file_search`." }) +}).annotate({ "identifier": "LiveSpecificFileSearchParam" }) +export type LiveSpecificWebSearchParam = { readonly "type": "web_search" } +export const LiveSpecificWebSearchParam = Schema.Struct({ + "type": Schema.Literal("web_search").annotate({ "description": "The tool to call. Always `web_search`." }) +}).annotate({ "identifier": "LiveSpecificWebSearchParam" }) +export type LiveSpecificWebSearchPreviewParam = { readonly "type": "web_search_preview" } +export const LiveSpecificWebSearchPreviewParam = Schema.Struct({ + "type": Schema.Literal("web_search_preview").annotate({ + "description": "The tool to call. Always `web_search_preview`." + }) +}).annotate({ "identifier": "LiveSpecificWebSearchPreviewParam" }) +export type LiveSpecificImageGenParam = { readonly "type": "image_generation" } +export const LiveSpecificImageGenParam = Schema.Struct({ + "type": Schema.Literal("image_generation").annotate({ "description": "The tool to call. Always `image_generation`." }) +}).annotate({ "identifier": "LiveSpecificImageGenParam" }) +export type LiveSpecificComputerParam = { readonly "type": "computer" } +export const LiveSpecificComputerParam = Schema.Struct({ + "type": Schema.Literal("computer").annotate({ "description": "The tool to call. Always `computer`." }) +}).annotate({ "identifier": "LiveSpecificComputerParam" }) +export type LiveSpecificCodeInterpreterParam = { readonly "type": "code_interpreter" } +export const LiveSpecificCodeInterpreterParam = Schema.Struct({ + "type": Schema.Literal("code_interpreter").annotate({ "description": "The tool to call. Always `code_interpreter`." }) +}).annotate({ "identifier": "LiveSpecificCodeInterpreterParam" }) +export type LiveSpecificProgrammaticToolCallingParam = { readonly "type": "programmatic_tool_calling" } +export const LiveSpecificProgrammaticToolCallingParam = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The tool to call. Always `programmatic_tool_calling`." + }) +}).annotate({ "identifier": "LiveSpecificProgrammaticToolCallingParam" }) +export type LiveSpecificFunctionShellParam = { readonly "type": "shell" } +export const LiveSpecificFunctionShellParam = Schema.Struct({ + "type": Schema.Literal("shell").annotate({ "description": "The tool to call. Always `shell`." }) +}).annotate({ + "title": "Specific shell tool choice", + "description": "Forces the model to call the shell tool when a tool call is required.", + "identifier": "LiveSpecificFunctionShellParam" +}) +export type LiveSpecificCustomToolParam = { readonly "type": "custom"; readonly "name": string } +export const LiveSpecificCustomToolParam = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "The tool to call. Always `custom`." }), + "name": Schema.String.annotate({ "description": "The name of the custom tool to call." }) +}).annotate({ "identifier": "LiveSpecificCustomToolParam" }) +export type LiveSpecificApplyPatchParam = { readonly "type": "apply_patch" } +export const LiveSpecificApplyPatchParam = Schema.Struct({ + "type": Schema.Literal("apply_patch").annotate({ "description": "The tool to call. Always `apply_patch`." }) +}).annotate({ + "title": "Specific apply patch tool choice", + "description": "Forces the model to call the apply_patch tool when executing a tool call.", + "identifier": "LiveSpecificApplyPatchParam" +}) +export type LiveToolChoiceValueEnum = "none" | "auto" | "required" +export const LiveToolChoiceValueEnum = Schema.Literals(["none", "auto", "required"]).annotate({ + "identifier": "LiveToolChoiceValueEnum" +}) +export type Store = boolean +export const Store = Schema.Boolean.annotate({ + "description": + "Whether to store the session for later forking and recording download. Defaults to false for new sessions.", + "identifier": "store" +}) +export type LiveInitialMessageStatus = "incomplete" | "completed" +export const LiveInitialMessageStatus = Schema.Literals(["incomplete", "completed"]).annotate({ + "identifier": "LiveInitialMessageStatus" +}) +export type LiveInitialInputTextContentPartParam = { readonly "type"?: "input_text"; readonly "text": string } +export const LiveInitialInputTextContentPartParam = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("input_text").annotate({ "description": "The text content type. Always `input_text`." }) + ), + "text": Schema.String.annotate({ + "description": "The message text to include in the Live session’s initial conversation history." + }) +}).annotate({ + "description": "Text supplied in a developer or user message when starting a Live session.", + "identifier": "LiveInitialInputTextContentPartParam" +}) +export type LiveInitialTextContentPartParam = { readonly "type"?: "text"; readonly "text": string } +export const LiveInitialTextContentPartParam = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("text").annotate({ "description": "The text content type. Always `text`." }) + ), + "text": Schema.String.annotate({ + "description": "The message text to include in the Live session’s initial conversation history." + }) +}).annotate({ + "description": "Assistant text supplied as conversation history when starting a Live session.", + "identifier": "LiveInitialTextContentPartParam" +}) +export type LiveInitialOutputTextContentPartParam = { readonly "type": "output_text"; readonly "text": string } +export const LiveInitialOutputTextContentPartParam = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ "description": "The text content type. Always `output_text`." }), + "text": Schema.String.annotate({ + "description": "The message text to include in the Live session’s initial conversation history." + }) +}).annotate({ + "description": "Assistant output text supplied as conversation history when starting a Live session.", + "identifier": "LiveInitialOutputTextContentPartParam" +}) +export type LiveAllowedServerEventParam = { readonly "type": string; readonly "response_event"?: string } +export const LiveAllowedServerEventParam = Schema.Struct({ + "type": Schema.String.annotate({ + "description": "The outer Live server event type. Use 'response.event' for Responses events." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ), + "response_event": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The nested Responses event type. Required when type is 'response.event'; forbidden for other event types." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ) +}).annotate({ + "description": "A Live server event selector for the WebRTC frontend data channel.", + "identifier": "LiveAllowedServerEventParam" +}) +export type LiveWebRTCTransport = { readonly "type": "webrtc"; readonly "sdp": string } +export const LiveWebRTCTransport = Schema.Struct({ + "type": Schema.Literal("webrtc").annotate({ + "description": "The transport used for the Live session. Always `webrtc`." + }), + "sdp": Schema.String.annotate({ "description": "Session Description Protocol message for the WebRTC connection." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) +}).annotate({ + "description": "WebRTC transport carrying the offer SDP in a creation request or answer SDP in its response.", + "identifier": "LiveWebRTCTransport" +}) +export type LiveSIPTrunkAuth = { readonly "type": "digest"; readonly "username": string; readonly "password": string } +export const LiveSIPTrunkAuth = Schema.Struct({ + "type": Schema.Literal("digest").annotate({ + "description": "Use `digest` for SIP Digest authentication. Handles 401 and 407 challenges." + }), + "username": Schema.String.annotate({ + "description": + "Provider username. Must contain a non-whitespace character, be at most 256 bytes, and contain no CR, LF, or NUL." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "password": Schema.String.annotate({ + "description": "Provider password. Must be at most 4096 bytes and contain no CR, LF, or NUL.", + "writeOnly": true + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) +}).annotate({ + "description": "Credentials for SIP Digest authentication with your trunk provider.", + "identifier": "LiveSIPTrunkAuth" +}) +export type Session = { readonly "id": string } +export const Session = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "Opaque session identifier. Preserve the returned value unchanged, including its prefix." + }) +}).annotate({ + "description": "The newly created Live session. Use its ID for session controls and sideband connections.", + "identifier": "session" +}) +export type Properties_store = boolean +export const Properties_store = Schema.Boolean.annotate({ + "description": "Whether to store the forked session. Omission inherits the stored session's setting.", + "identifier": "properties-store" +}) +export type LiveCallReferRequest = { readonly "target_uri": string } +export const LiveCallReferRequest = Schema.Struct({ + "target_uri": Schema.String.annotate({ + "description": "Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or sip:agent@example.com." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isPattern(new RegExp("\\S", "u")).annotate({ "expected": "a string matching the RegExp \\S" }) + ) +}).annotate({ + "description": "Parameters used to transfer a Live SIP call to another destination.", + "identifier": "LiveCallReferRequest" +}) +export type LiveCallRejectRequest = { readonly "status_code": number } +export const LiveCallRejectRequest = Schema.Struct({ + "status_code": Schema.Number.annotate({ + "description": "SIP rejection status sent to the caller. This field is required." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(300).annotate({ "expected": "a value greater than or equal to 300" }) + ).check(Schema.isLessThanOrEqualTo(699).annotate({ "expected": "a value less than or equal to 699" })) +}).annotate({ + "description": "Parameters used to reject an incoming Live SIP call.", + "identifier": "LiveCallRejectRequest" +}) +export type Model = + | null + | string + | number + | boolean + | { + readonly "id": string + readonly "created": number + readonly "object": "model" + readonly "owned_by": string + readonly "shutdown_date"?: string | null + } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const Model = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The model identifier, which can be referenced in the API endpoints." + }), + "created": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) when the model was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "object": Schema.Literal("model").annotate({ "description": "The object type, which is always \"model\"." }), + "owned_by": Schema.String.annotate({ "description": "The organization that owns the model." }), + "shutdown_date": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "format": "date" }), Schema.Null]).annotate({ + "description": "The date when the model will shut down, or null if not announced." + }) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]).annotate({ + "title": "Model", + "description": "Describes an OpenAI model offering that can be used with the API.", + "identifier": "Model" +}) +export type DeleteModelResponse = { readonly "id": string; readonly "deleted": boolean; readonly "object": string } +export const DeleteModelResponse = Schema.Struct({ + "id": Schema.String, + "deleted": Schema.Boolean, + "object": Schema.String +}).annotate({ "identifier": "DeleteModelResponse" }) +export type CreateModerationRequest = { + readonly "input": + | string + | ReadonlyArray + | ReadonlyArray< + { readonly "type": "image_url"; readonly "image_url": { readonly "url": string } } | { + readonly "type": "text" + readonly "text": string + } + > + readonly "model"?: + | string + | "omni-moderation-latest" + | "omni-moderation-2024-09-26" + | "text-moderation-latest" + | "text-moderation-stable" +} +export const CreateModerationRequest = Schema.Struct({ + "input": Schema.Union([ + Schema.String.annotate({ "description": "A string of text to classify for moderation." }), + Schema.Array(Schema.String).annotate({ "description": "An array of strings to classify for moderation." }), + Schema.Array(Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("image_url").annotate({ "description": "Always `image_url`." }), + "image_url": Schema.Struct({ + "url": Schema.String.annotate({ + "description": "Either a URL of the image or the base64 encoded image data.", + "format": "uri" + }) + }).annotate({ "description": "Contains either an image URL or a data URL for a base64 encoded image." }) + }).annotate({ "description": "An object describing an image to classify." }), + Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "Always `text`." }), + "text": Schema.String.annotate({ "description": "A string of text to classify." }) + }).annotate({ "description": "An object describing text to classify." }) + ], { mode: "oneOf" })).annotate({ "description": "An array of multi-modal inputs to the moderation model." }) + ], { mode: "oneOf" }).annotate({ + "description": + "Input (or inputs) to classify. Can be a single string, an array of strings, or\nan array of multi-modal input objects similar to other models.\n" + }), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "omni-moderation-latest", + "omni-moderation-2024-09-26", + "text-moderation-latest", + "text-moderation-stable" + ]) + ]).annotate({ + "description": + "The content moderation model you would like to use. Learn more in\n[the moderation guide](https://developers.openai.com/api/docs/guides/moderation), and learn about\navailable models [here](https://developers.openai.com/api/docs/guides/moderation).\n" + }) + ) +}).annotate({ "identifier": "CreateModerationRequest" }) +export type CreateModerationResponse = { + readonly "id": string + readonly "model": string + readonly "results": ReadonlyArray< + { + readonly "flagged": boolean + readonly "categories": { + readonly "hate": boolean + readonly "hate/threatening": boolean + readonly "harassment": boolean + readonly "harassment/threatening": boolean + readonly "illicit": boolean | null + readonly "illicit/violent": boolean | null + readonly "self-harm": boolean + readonly "self-harm/intent": boolean + readonly "self-harm/instructions": boolean + readonly "sexual": boolean + readonly "sexual/minors": boolean + readonly "violence": boolean + readonly "violence/graphic": boolean + } + readonly "category_scores": { + readonly "hate": number + readonly "hate/threatening": number + readonly "harassment": number + readonly "harassment/threatening": number + readonly "illicit": number + readonly "illicit/violent": number + readonly "self-harm": number + readonly "self-harm/intent": number + readonly "self-harm/instructions": number + readonly "sexual": number + readonly "sexual/minors": number + readonly "violence": number + readonly "violence/graphic": number + } + readonly "category_applied_input_types": { + readonly "hate": ReadonlyArray<"text"> + readonly "hate/threatening": ReadonlyArray<"text"> + readonly "harassment": ReadonlyArray<"text"> + readonly "harassment/threatening": ReadonlyArray<"text"> + readonly "illicit": ReadonlyArray<"text"> + readonly "illicit/violent": ReadonlyArray<"text"> + readonly "self-harm": ReadonlyArray<"text" | "image"> + readonly "self-harm/intent": ReadonlyArray<"text" | "image"> + readonly "self-harm/instructions": ReadonlyArray<"text" | "image"> + readonly "sexual": ReadonlyArray<"text" | "image"> + readonly "sexual/minors": ReadonlyArray<"text"> + readonly "violence": ReadonlyArray<"text" | "image"> + readonly "violence/graphic": ReadonlyArray<"text" | "image"> + } + } + > +} +export const CreateModerationResponse = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique identifier for the moderation request." }), + "model": Schema.String.annotate({ "description": "The model used to generate the moderation results." }), + "results": Schema.Array(Schema.Struct({ + "flagged": Schema.Boolean.annotate({ "description": "Whether any of the below categories are flagged." }), + "categories": Schema.Struct({ + "hate": Schema.Boolean.annotate({ + "description": + "Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) is harassment." + }), + "hate/threatening": Schema.Boolean.annotate({ + "description": + "Hateful content that also includes violence or serious harm towards the targeted group based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste." + }), + "harassment": Schema.Boolean.annotate({ + "description": "Content that expresses, incites, or promotes harassing language towards any target." + }), + "harassment/threatening": Schema.Boolean.annotate({ + "description": "Harassment content that also includes violence or serious harm towards any target." + }), + "illicit": Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Content that includes instructions or advice that facilitate the planning or execution of wrongdoing, or that gives advice or instruction on how to commit illicit acts. For example, \"how to shoplift\" would fit this category." + }), + Schema.Null + ]), + "illicit/violent": Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Content that includes instructions or advice that facilitate the planning or execution of wrongdoing that also includes violence, or that gives advice or instruction on the procurement of any weapon." + }), + Schema.Null + ]), + "self-harm": Schema.Boolean.annotate({ + "description": + "Content that promotes, encourages, or depicts acts of self-harm, such as suicide, cutting, and eating disorders." + }), + "self-harm/intent": Schema.Boolean.annotate({ + "description": + "Content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders." + }), + "self-harm/instructions": Schema.Boolean.annotate({ + "description": + "Content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts." + }), + "sexual": Schema.Boolean.annotate({ + "description": + "Content meant to arouse sexual excitement, such as the description of sexual activity, or that promotes sexual services (excluding sex education and wellness)." + }), + "sexual/minors": Schema.Boolean.annotate({ + "description": "Sexual content that includes an individual who is under 18 years old." + }), + "violence": Schema.Boolean.annotate({ + "description": "Content that depicts death, violence, or physical injury." + }), + "violence/graphic": Schema.Boolean.annotate({ + "description": "Content that depicts death, violence, or physical injury in graphic detail." + }) + }).annotate({ "description": "A list of the categories, and whether they are flagged or not." }), + "category_scores": Schema.Struct({ + "hate": Schema.Number.annotate({ "description": "The score for the category 'hate'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "hate/threatening": Schema.Number.annotate({ "description": "The score for the category 'hate/threatening'." }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "harassment": Schema.Number.annotate({ "description": "The score for the category 'harassment'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "harassment/threatening": Schema.Number.annotate({ + "description": "The score for the category 'harassment/threatening'." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "illicit": Schema.Number.annotate({ "description": "The score for the category 'illicit'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "illicit/violent": Schema.Number.annotate({ "description": "The score for the category 'illicit/violent'." }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "self-harm": Schema.Number.annotate({ "description": "The score for the category 'self-harm'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "self-harm/intent": Schema.Number.annotate({ "description": "The score for the category 'self-harm/intent'." }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "self-harm/instructions": Schema.Number.annotate({ + "description": "The score for the category 'self-harm/instructions'." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "sexual": Schema.Number.annotate({ "description": "The score for the category 'sexual'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "sexual/minors": Schema.Number.annotate({ "description": "The score for the category 'sexual/minors'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "violence": Schema.Number.annotate({ "description": "The score for the category 'violence'." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "violence/graphic": Schema.Number.annotate({ "description": "The score for the category 'violence/graphic'." }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })) + }).annotate({ "description": "A list of the categories along with their scores as predicted by model." }), + "category_applied_input_types": Schema.Struct({ + "hate": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'hate'." + }), + "hate/threatening": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'hate/threatening'." + }), + "harassment": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'harassment'." + }), + "harassment/threatening": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'harassment/threatening'." + }), + "illicit": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'illicit'." + }), + "illicit/violent": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'illicit/violent'." + }), + "self-harm": Schema.Array(Schema.Literals(["text", "image"])).annotate({ + "description": "The applied input type(s) for the category 'self-harm'." + }), + "self-harm/intent": Schema.Array(Schema.Literals(["text", "image"])).annotate({ + "description": "The applied input type(s) for the category 'self-harm/intent'." + }), + "self-harm/instructions": Schema.Array(Schema.Literals(["text", "image"])).annotate({ + "description": "The applied input type(s) for the category 'self-harm/instructions'." + }), + "sexual": Schema.Array(Schema.Literals(["text", "image"])).annotate({ + "description": "The applied input type(s) for the category 'sexual'." + }), + "sexual/minors": Schema.Array(Schema.Literal("text")).annotate({ + "description": "The applied input type(s) for the category 'sexual/minors'." + }), + "violence": Schema.Array(Schema.Literals(["text", "image"])).annotate({ + "description": "The applied input type(s) for the category 'violence'." + }), + "violence/graphic": Schema.Array(Schema.Literals(["text", "image"])).annotate({ + "description": "The applied input type(s) for the category 'violence/graphic'." + }) + }).annotate({ "description": "A list of the categories along with the input type(s) that the score applies to." }) + })).annotate({ "description": "A list of moderation objects." }) +}).annotate({ + "description": "Represents if a given text input is potentially harmful.", + "identifier": "CreateModerationResponse" +}) +export type AdminApiKey = { + readonly "object": "organization.admin_api_key" + readonly "id": string + readonly "name"?: string | null + readonly "redacted_value": string + readonly "created_at": number + readonly "expires_at": number | null + readonly "last_used_at"?: number | null + readonly "owner": { + readonly "type"?: string + readonly "object"?: string + readonly "id"?: string + readonly "name"?: string + readonly "created_at"?: number + readonly "role"?: string + } +} +export const AdminApiKey = Schema.Struct({ + "object": Schema.Literal("organization.admin_api_key").annotate({ + "description": "The object type, which is always `organization.admin_api_key`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the API key" }) + ), + "redacted_value": Schema.String.annotate({ "description": "The redacted value of the API key" }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key was created", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "expires_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key expires", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "last_used_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key was last used", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "owner": Schema.Struct({ + "type": Schema.optionalKey(Schema.String.annotate({ "description": "Always `user`" })), + "object": Schema.optionalKey( + Schema.String.annotate({ "description": "The object type, which is always organization.user" }) + ), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }) + ), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the user" })), + "created_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the user was created", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "role": Schema.optionalKey(Schema.String.annotate({ "description": "Always `owner`" })) + }) +}).annotate({ "description": "Represents an individual Admin API key in an org.", "identifier": "AdminApiKey" }) +export type AdminApiKeyCreateResponse = { + readonly "object": "organization.admin_api_key" + readonly "id": string + readonly "name"?: string | null + readonly "redacted_value": string + readonly "created_at": number + readonly "expires_at": number | null + readonly "last_used_at"?: number | null + readonly "owner": { + readonly "type"?: string + readonly "object"?: string + readonly "id"?: string + readonly "name"?: string + readonly "created_at"?: number + readonly "role"?: string + } + readonly "value": string +} +export const AdminApiKeyCreateResponse = Schema.Struct({ + "object": Schema.Literal("organization.admin_api_key").annotate({ + "description": "The object type, which is always `organization.admin_api_key`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the API key" }) + ), + "redacted_value": Schema.String.annotate({ "description": "The redacted value of the API key" }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key was created", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "expires_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key expires", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "last_used_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key was last used", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "owner": Schema.Struct({ + "type": Schema.optionalKey(Schema.String.annotate({ "description": "Always `user`" })), + "object": Schema.optionalKey( + Schema.String.annotate({ "description": "The object type, which is always organization.user" }) + ), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }) + ), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the user" })), + "created_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the user was created", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "role": Schema.optionalKey(Schema.String.annotate({ "description": "Always `owner`" })) + }), + "value": Schema.String.annotate({ "description": "The value of the API key. Only shown on create." }) +}).annotate({ "identifier": "AdminApiKeyCreateResponse" }) +export type AuditLogEventType = + | "api_key.created" + | "api_key.updated" + | "api_key.deleted" + | "certificate.created" + | "certificate.updated" + | "certificate.deleted" + | "certificates.activated" + | "certificates.deactivated" + | "checkpoint.permission.created" + | "checkpoint.permission.deleted" + | "external_key.registered" + | "external_key.removed" + | "external_storage.registered" + | "external_storage.removed" + | "group.created" + | "group.updated" + | "group.deleted" + | "invite.sent" + | "invite.accepted" + | "invite.deleted" + | "ip_allowlist.created" + | "ip_allowlist.updated" + | "ip_allowlist.deleted" + | "ip_allowlist.config.activated" + | "ip_allowlist.config.deactivated" + | "login.succeeded" + | "login.failed" + | "logout.succeeded" + | "logout.failed" + | "organization.updated" + | "project.created" + | "project.updated" + | "project.archived" + | "project.deleted" + | "rate_limit.updated" + | "rate_limit.deleted" + | "resource.deleted" + | "tunnel.created" + | "tunnel.updated" + | "tunnel.deleted" + | "workload_identity_provider.created" + | "workload_identity_provider.updated" + | "workload_identity_provider.deleted" + | "workload_identity_provider_mapping.created" + | "workload_identity_provider_mapping.updated" + | "workload_identity_provider_mapping.deleted" + | "role.created" + | "role.updated" + | "role.deleted" + | "role.assignment.created" + | "role.assignment.deleted" + | "role.bound_to_resource" + | "role.unbound_from_resource" + | "scim.enabled" + | "scim.disabled" + | "service_account.created" + | "service_account.updated" + | "service_account.deleted" + | "user.added" + | "user.updated" + | "user.deleted" + | "tenant.metadata.updated" + | "tenant.microsoft_entra_mapping.upserted" + | "tenant.microsoft_entra_mapping.deleted" + | "tenant.workload_identity.provider.created" + | "tenant.workload_identity.provider.updated" + | "tenant.workload_identity.provider.archived" + | "tenant.workload_identity.mapping.created" + | "tenant.workload_identity.mapping.updated" + | "tenant.workload_identity.mapping.archived" + | "tenant.workload_identity.binding.created" + | "tenant.workload_identity.principal.provisioned" + | "tenant.workload_identity.access_token.issued" + | "tenant.admin_api_key.created" + | "tenant.admin_api_key.updated" + | "tenant.admin_api_key.deleted" + | "tenant.project_api_key.created" + | "tenant.trusted_access.business_verification.started" + | "tenant.trusted_access.application.submitted" + | "tenant.chatgpt_access_token.revoked" + | "tenant.migration.completed" + | "tenant.sso.migrated" + | "tenant.domains.migrated" + | "tenant.sso_connection.created" + | "tenant.sso_connection.updated" + | "tenant.sso_connection.deleted" + | "tenant.sso_connection.setup.started" + | "tenant.policy.created" + | "tenant.policy.updated" + | "tenant.policy.deleted" + | "tenant.policy.attached" + | "tenant.policy.detached" + | "tenant.principal_authentication_policy.resolved" + | "tenant.scim.setup.started" + | "tenant.scim.deletion.requested" + | "tenant.scim.directory.created" + | "tenant.product_access_policy.updated" + | "tenant.resource_share_grant.created" + | "tenant.resource_share_grant.updated" + | "tenant.resource_share_grant.accepted" + | "tenant.resource_share_grant.declined" + | "tenant.resource_share_grant.revoked" + | "tenant.resource_share_grant.deleted" + | "tenant.service_account.updated" + | "tenant.service_account.deleted" + | "tenant.service_account.token.revoked" + | "tenant.billing.overage_limit.updated" + | "tenant.billing.alerts.updated" + | "tenant.billing.info.updated" + | "tenant.usage_limit.workspace.updated" + | "tenant.usage_limit.group.updated" + | "tenant.usage_limit.user.updated" + | "tenant.usage_limit.increase_request.updated" + | "tenant.usage_limit.increase_request.resolved" + | "tenant.group.created" + | "tenant.group.updated" + | "tenant.group.deleted" + | "tenant.group.member.added" + | "tenant.group.member.removed" + | "tenant.migration_rollout.status.updated" + | "tenant.migration_rollout.tier.updated" + | "tenant.role.metadata.updated" + | "tenant.custom_role.created" + | "tenant.custom_role.updated" + | "tenant.custom_role.deleted" + | "tenant.role_assignment.created" + | "tenant.role_assignment.deleted" + | "tenant.resource_role_assignment.created" + | "tenant.resource_role_assignment.deleted" + | "tenant.resource_access.updated" + | "tenant.resource_access.deleted" + | "tenant.ads_account.onboarding.redemption" + | "tenant.session_policy.created" + | "tenant.session_policy.updated" + | "tenant.session_policy.deleted" + | "tenant.session_revocation.started" + | "tenant.third_party_app_policy.updated" + | "tenant.user.added" + | "tenant.user.updated" + | "tenant.user.removed" + | "tenant.user.looked_up" + | "tenant.user.invited" + | "tenant.membership.revoked" + | "tenant.api_organization_invite.upserted" + | "tenant.api_organization_invite.deleted" + | "tenant.chatgpt_workspace_invite.upserted" + | "tenant.membership.accepted" + | "tenant.membership.declined" + | "tenant.workspace_invite_email_settings.updated" +export const AuditLogEventType = Schema.Literals([ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "external_storage.registered", + "external_storage.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.workload_identity.access_token.issued", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.trusted_access.business_verification.started", + "tenant.trusted_access.application.submitted", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.ads_account.onboarding.redemption", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated" +]).annotate({ "description": "The event type.", "identifier": "AuditLogEventType" }) +export type AuditLogActorUser = { readonly "id"?: string; readonly "email"?: string } +export const AuditLogActorUser = Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The user id." })), + "email": Schema.optionalKey(Schema.String.annotate({ "description": "The user email." })) +}).annotate({ "description": "The user who performed the audit logged action.", "identifier": "AuditLogActorUser" }) +export type AuditLogActorServiceAccount = { readonly "id"?: string } +export const AuditLogActorServiceAccount = Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The service account id." })) +}).annotate({ + "description": "The service account that performed the audit logged action.", + "identifier": "AuditLogActorServiceAccount" +}) +export type AwsExternalStorageProviderResponse = { + readonly "type": "aws" + readonly "account_id": string + readonly "region": string + readonly "bucket": string + readonly "role_arn": string + readonly "external_id": string +} +export const AwsExternalStorageProviderResponse = Schema.Struct({ + "type": Schema.Literal("aws"), + "account_id": Schema.String, + "region": Schema.String, + "bucket": Schema.String, + "role_arn": Schema.String, + "external_id": Schema.String +}).annotate({ "identifier": "AwsExternalStorageProviderResponse" }) +export type AzureExternalStorageProviderResponse = { + readonly "type": "azure" + readonly "tenant_id": string + readonly "subscription_id": string + readonly "resource_group": string + readonly "account_name": string + readonly "container": string + readonly "region": string +} +export const AzureExternalStorageProviderResponse = Schema.Struct({ + "type": Schema.Literal("azure"), + "tenant_id": Schema.String, + "subscription_id": Schema.String, + "resource_group": Schema.String, + "account_name": Schema.String, + "container": Schema.String, + "region": Schema.String +}).annotate({ "identifier": "AzureExternalStorageProviderResponse" }) +export type GcpExternalStorageProviderResponse = { + readonly "type": "gcp" + readonly "bucket": string + readonly "workload_identity_project_number": string + readonly "workload_identity_pool_id": string + readonly "workload_identity_provider_id": string + readonly "region": string + readonly "audience": string +} +export const GcpExternalStorageProviderResponse = Schema.Struct({ + "type": Schema.Literal("gcp"), + "bucket": Schema.String, + "workload_identity_project_number": Schema.String, + "workload_identity_pool_id": Schema.String, + "workload_identity_provider_id": Schema.String, + "region": Schema.String, + "audience": Schema.String +}).annotate({ "identifier": "GcpExternalStorageProviderResponse" }) +export type OciExternalStorageProviderResponse = { + readonly "type": "oci" + readonly "tenancy_ocid": string + readonly "region": string + readonly "bucket": string +} +export const OciExternalStorageProviderResponse = Schema.Struct({ + "type": Schema.Literal("oci"), + "tenancy_ocid": Schema.String, + "region": Schema.String, + "bucket": Schema.String +}).annotate({ "identifier": "OciExternalStorageProviderResponse" }) +export type OrganizationCertificate = { + readonly "object": "organization.certificate" + readonly "id": string + readonly "name": string | null + readonly "created_at": number + readonly "certificate_details": { readonly "valid_at"?: number; readonly "expires_at"?: number } + readonly "active": boolean +} +export const OrganizationCertificate = Schema.Struct({ + "object": Schema.Literal("organization.certificate").annotate({ + "description": "The object type, which is always `organization.certificate`." + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the certificate." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate was uploaded.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "certificate_details": Schema.Struct({ + "valid_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate becomes valid.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate expires.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }), + "active": Schema.Boolean.annotate({ + "description": "Whether the certificate is currently active at the organization level." + }) +}).annotate({ + "description": "Represents an individual certificate configured at the organization level.", + "identifier": "OrganizationCertificate" +}) +export type UploadCertificateRequest = { readonly "name"?: string; readonly "certificate": string } +export const UploadCertificateRequest = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "An optional name for the certificate" })), + "certificate": Schema.String.annotate({ "description": "The certificate content in PEM format" }) +}).annotate({ "identifier": "UploadCertificateRequest" }) +export type Certificate = { + readonly "object": "certificate" | "organization.certificate" | "organization.project.certificate" + readonly "id": string + readonly "name": string | null + readonly "created_at": number + readonly "certificate_details": { + readonly "valid_at"?: number + readonly "expires_at"?: number + readonly "content"?: string + } + readonly "active"?: boolean +} +export const Certificate = Schema.Struct({ + "object": Schema.Literals(["certificate", "organization.certificate", "organization.project.certificate"]).annotate({ + "description": + "The object type.\n\n- If creating, updating, or getting a specific certificate, the object type is `certificate`.\n- If listing, activating, or deactivating certificates for the organization, the object type is `organization.certificate`.\n- If listing, activating, or deactivating certificates for a project, the object type is `organization.project.certificate`.\n" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the certificate." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate was uploaded.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "certificate_details": Schema.Struct({ + "valid_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate becomes valid.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate expires.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "content": Schema.optionalKey( + Schema.String.annotate({ "description": "The content of the certificate in PEM format." }) + ) + }), + "active": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the certificate is currently active at the specified scope. Not returned when getting details for a specific certificate." + }) + ) +}).annotate({ + "description": "Represents an individual `certificate` uploaded to the organization.", + "identifier": "Certificate" +}) +export type ToggleCertificatesRequest = { readonly "certificate_ids": ReadonlyArray } +export const ToggleCertificatesRequest = Schema.Struct({ + "certificate_ids": Schema.Array(Schema.String).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxLength(10).annotate({ "expected": "a value with a length of at most 10" })) +}).annotate({ "identifier": "ToggleCertificatesRequest" }) +export type ModifyCertificateRequest = { readonly "name"?: string } +export const ModifyCertificateRequest = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The updated name for the certificate" })) +}).annotate({ "identifier": "ModifyCertificateRequest" }) +export type DeleteCertificateResponse = { readonly "object": "certificate.deleted"; readonly "id": string } +export const DeleteCertificateResponse = Schema.Struct({ + "object": Schema.Literal("certificate.deleted").annotate({ + "description": "The object type, must be `certificate.deleted`." + }), + "id": Schema.String.annotate({ "description": "The ID of the certificate that was deleted." }) +}).annotate({ "identifier": "DeleteCertificateResponse" }) +export type UsageCompletionsResult = { + readonly "api_source"?: "agents_api" | "unlabeled" | null + readonly "object": "organization.usage.completions.result" + readonly "input_tokens": number + readonly "input_cached_tokens"?: number + readonly "input_cache_write_tokens"?: number + readonly "input_cache_write_12h_tokens"?: number + readonly "input_uncached_tokens"?: number + readonly "output_tokens": number + readonly "input_text_tokens"?: number + readonly "output_text_tokens"?: number + readonly "input_cached_text_tokens"?: number + readonly "input_audio_tokens"?: number + readonly "input_cached_audio_tokens"?: number + readonly "output_audio_tokens"?: number + readonly "input_image_tokens"?: number + readonly "input_cached_image_tokens"?: number + readonly "output_image_tokens"?: number + readonly "num_model_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null + readonly "batch"?: boolean | null + readonly "service_tier"?: string | null +} +export const UsageCompletionsResult = Schema.Struct({ + "api_source": Schema.optionalKey( + Schema.Union([Schema.Literals(["agents_api", "unlabeled"]), Schema.Null]).annotate({ + "description": + "When grouped by `api_source`, `agents_api` identifies attributed Agents API activity and `unlabeled` includes all records without published source attribution, including historical and unknown origins. Unlabeled does not imply direct API usage. Without source grouping, this field is null." + }) + ), + "object": Schema.Literal("organization.usage.completions.result"), + "input_tokens": Schema.Number.annotate({ + "description": + "The aggregated number of input tokens used, including cached and cache-write tokens. This includes text, audio, and image tokens. For customers subscribed to Scale Tier, this includes Scale Tier tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "input_cached_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The aggregated number of cached input tokens used across text, audio, and image inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "input_cache_write_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The aggregated number of input tokens written to the cache with a 30-minute retention period." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "input_cache_write_12h_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The aggregated number of input tokens written to the cache with a 12-hour retention period." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "input_uncached_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The aggregated number of uncached input tokens used across text, audio, and image inputs, excluding cache-write tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "output_tokens": Schema.Number.annotate({ + "description": + "The aggregated number of output tokens used across text, audio, and image outputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "input_text_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The aggregated number of uncached text input tokens used, excluding cache-write tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "output_text_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of text output tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "input_cached_text_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of cached text input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "input_audio_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of uncached audio input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "input_cached_audio_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of cached audio input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "output_audio_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of audio output tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "input_image_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of uncached image input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "input_cached_image_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of cached image input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "output_image_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The aggregated number of image output tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ), + "batch": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": "When `group_by=batch`, this field tells whether the grouped usage result is batch or not." + }), + Schema.Null + ]) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=service_tier`, this field provides the service tier of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated completions usage details of the specific time bucket.", + "identifier": "UsageCompletionsResult" +}) +export type UsageEmbeddingsResult = { + readonly "object": "organization.usage.embeddings.result" + readonly "input_tokens": number + readonly "num_model_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null +} +export const UsageEmbeddingsResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.embeddings.result"), + "input_tokens": Schema.Number.annotate({ "description": "The aggregated number of input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated embeddings usage details of the specific time bucket.", + "identifier": "UsageEmbeddingsResult" +}) +export type UsageModerationsResult = { + readonly "object": "organization.usage.moderations.result" + readonly "input_tokens": number + readonly "num_model_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null +} +export const UsageModerationsResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.moderations.result"), + "input_tokens": Schema.Number.annotate({ "description": "The aggregated number of input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated moderations usage details of the specific time bucket.", + "identifier": "UsageModerationsResult" +}) +export type UsageImagesResult = { + readonly "object": "organization.usage.images.result" + readonly "images": number + readonly "num_model_requests": number + readonly "source"?: string | null + readonly "size"?: string | null + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null +} +export const UsageImagesResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.images.result"), + "images": Schema.Number.annotate({ "description": "The number of images processed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "source": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "When `group_by=source`, this field provides the source of the grouped usage result, possible values are `image.generation`, `image.edit`, `image.variation`." + }), + Schema.Null + ]) + ), + "size": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=size`, this field provides the image size of the grouped usage result." + }), + Schema.Null + ]) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated images usage details of the specific time bucket.", + "identifier": "UsageImagesResult" +}) +export type UsageAudioSpeechesResult = { + readonly "object": "organization.usage.audio_speeches.result" + readonly "characters": number + readonly "num_model_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null +} +export const UsageAudioSpeechesResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.audio_speeches.result"), + "characters": Schema.Number.annotate({ "description": "The number of characters processed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated audio speeches usage details of the specific time bucket.", + "identifier": "UsageAudioSpeechesResult" +}) +export type UsageAudioTranscriptionsResult = { + readonly "object": "organization.usage.audio_transcriptions.result" + readonly "seconds": number + readonly "num_model_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null +} +export const UsageAudioTranscriptionsResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.audio_transcriptions.result"), + "seconds": Schema.Number.annotate({ "description": "The number of seconds processed.", "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated audio transcriptions usage details of the specific time bucket.", + "identifier": "UsageAudioTranscriptionsResult" +}) +export type UsageVectorStoresResult = { + readonly "object": "organization.usage.vector_stores.result" + readonly "usage_bytes": number + readonly "project_id"?: string | null +} +export const UsageVectorStoresResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.vector_stores.result"), + "usage_bytes": Schema.Number.annotate({ "description": "The vector stores usage in bytes." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated vector stores usage details of the specific time bucket.", + "identifier": "UsageVectorStoresResult" +}) +export type UsageCodeInterpreterSessionsResult = { + readonly "object": "organization.usage.code_interpreter_sessions.result" + readonly "num_sessions": number + readonly "project_id"?: string | null +} +export const UsageCodeInterpreterSessionsResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.code_interpreter_sessions.result"), + "num_sessions": Schema.Number.annotate({ "description": "The number of code interpreter sessions." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated code interpreter sessions usage details of the specific time bucket.", + "identifier": "UsageCodeInterpreterSessionsResult" +}) +export type UsageFileSearchCallsResult = { + readonly "object": "organization.usage.file_searches.result" + readonly "num_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "vector_store_id"?: string | null +} +export const UsageFileSearchCallsResult = Schema.Struct({ + "object": Schema.Literal("organization.usage.file_searches.result"), + "num_requests": Schema.Number.annotate({ "description": "The count of file search calls." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "vector_store_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "When `group_by=vector_store_id`, this field provides the vector store ID of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated file search calls usage details of the specific time bucket.", + "identifier": "UsageFileSearchCallsResult" +}) +export type UsageWebSearchCallsResult = { + readonly "api_source"?: "agents_api" | "unlabeled" | null + readonly "object": "organization.usage.web_searches.result" + readonly "num_model_requests": number + readonly "num_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null + readonly "context_level"?: string | null +} +export const UsageWebSearchCallsResult = Schema.Struct({ + "api_source": Schema.optionalKey( + Schema.Union([Schema.Literals(["agents_api", "unlabeled"]), Schema.Null]).annotate({ + "description": + "When grouped by `api_source`, `agents_api` identifies attributed Agents API activity and `unlabeled` includes all records without published source attribution, including historical and unknown origins. Unlabeled does not imply direct API usage. Without source grouping, this field is null." + }) + ), + "object": Schema.Literal("organization.usage.web_searches.result"), + "num_model_requests": Schema.Number.annotate({ "description": "The count of model requests." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "num_requests": Schema.Number.annotate({ "description": "The count of web search calls." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ), + "context_level": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "When `group_by=context_level`, this field provides the search context size of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated web search calls usage details of the specific time bucket.", + "identifier": "UsageWebSearchCallsResult" +}) +export type CostsResult = { + readonly "api_source"?: "agents_api" | "unlabeled" | null + readonly "object": "organization.costs.result" + readonly "amount"?: { readonly "value"?: number; readonly "currency"?: string } + readonly "line_item"?: string | null + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "quantity"?: number | null + readonly "quantity_unit"?: + | string + | "tokens" + | "1000_tokens" + | "duration_seconds" + | "duration_minutes" + | "duration_hours" + | "gibibyte_hours" + | "images" + | "characters" + | null +} +export const CostsResult = Schema.Struct({ + "api_source": Schema.optionalKey( + Schema.Union([Schema.Literals(["agents_api", "unlabeled"]), Schema.Null]).annotate({ + "description": + "When grouped by `api_source`, `agents_api` identifies attributed Agents API activity and `unlabeled` includes all records without published source attribution, including historical and unknown origins. Unlabeled does not imply direct API usage. Without source grouping, this field is null." + }) + ), + "object": Schema.Literal("organization.costs.result"), + "amount": Schema.optionalKey( + Schema.Struct({ + "value": Schema.optionalKey( + Schema.Number.annotate({ "description": "The numeric value of the cost." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ), + "currency": Schema.optionalKey( + Schema.String.annotate({ "description": "Lowercase ISO-4217 currency e.g. \"usd\"" }) + ) + }).annotate({ "description": "The monetary value in its associated currency." }) + ), + "line_item": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=line_item`, this field provides the line item of the grouped costs result." + }), + Schema.Null + ]) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped costs result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped costs result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API Key ID of the grouped costs result." + }), + Schema.Null + ]) + ), + "quantity": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "When `group_by=line_item`, this field provides the quantity of the grouped costs result." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "quantity_unit": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "tokens", + "1000_tokens", + "duration_seconds", + "duration_minutes", + "duration_hours", + "gibibyte_hours", + "images", + "characters" + ]), + Schema.Null + ]).annotate({ + "description": + "The unit of the `quantity` value. If no single supported unit applies to the result, this field is `null`." + }) + ) +}).annotate({ "description": "The aggregated costs details of the specific time bucket.", "identifier": "CostsResult" }) +export type OrganizationDataRetention = { + readonly "object": "organization.data_retention" + readonly "type": + | "zero_data_retention" + | "modified_abuse_monitoring" + | "enhanced_zero_data_retention" + | "enhanced_modified_abuse_monitoring" +} +export const OrganizationDataRetention = Schema.Struct({ + "object": Schema.Literal("organization.data_retention").annotate({ + "description": "The object type, which is always `organization.data_retention`." + }), + "type": Schema.Literals([ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring" + ]).annotate({ "description": "The configured organization data retention type." }) +}).annotate({ + "description": "Represents the organization's data retention control setting.", + "identifier": "OrganizationDataRetention" +}) +export type UpdateOrganizationDataRetentionBody = { + readonly "retention_type": + | "zero_data_retention" + | "modified_abuse_monitoring" + | "enhanced_zero_data_retention" + | "enhanced_modified_abuse_monitoring" +} +export const UpdateOrganizationDataRetentionBody = Schema.Struct({ + "retention_type": Schema.Literals([ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring" + ]).annotate({ "description": "The desired organization data retention type." }) +}).annotate({ + "description": "Parameters for updating organization data retention controls.", + "identifier": "UpdateOrganizationDataRetentionBody" +}) +export type GroupResponse = { + readonly "id": string + readonly "name": string + readonly "created_at": number + readonly "is_scim_managed": boolean + readonly "group_type": "group" | "tenant_group" +} +export const GroupResponse = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier for the group." }), + "name": Schema.String.annotate({ "description": "Display name of the group." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the group was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "is_scim_managed": Schema.Boolean.annotate({ + "description": "Whether the group is managed through SCIM and controlled by your identity provider." + }), + "group_type": Schema.Literals(["group", "tenant_group"]).annotate({ "description": "The type of the group." }) +}).annotate({ "description": "Details about an organization group.", "identifier": "GroupResponse" }) +export type CreateGroupBody = { readonly "name": string } +export const CreateGroupBody = Schema.Struct({ + "name": Schema.String.annotate({ "description": "Human readable name for the group." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(255).annotate({ "expected": "a string with at most 255 code points" })) +}).annotate({ + "description": "Request payload for creating a new group in the organization.", + "identifier": "CreateGroupBody" +}) +export type UpdateGroupBody = { readonly "name": string } +export const UpdateGroupBody = Schema.Struct({ + "name": Schema.String.annotate({ "description": "New display name for the group." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(255).annotate({ "expected": "a string with at most 255 code points" })) +}).annotate({ + "description": "Request payload for updating the details of an existing group.", + "identifier": "UpdateGroupBody" +}) +export type GroupResourceWithSuccess = { + readonly "id": string + readonly "name": string + readonly "created_at": number + readonly "is_scim_managed": boolean +} +export const GroupResourceWithSuccess = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier for the group." }), + "name": Schema.String.annotate({ "description": "Updated display name for the group." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the group was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "is_scim_managed": Schema.Boolean.annotate({ + "description": "Whether the group is managed through SCIM and controlled by your identity provider." + }) +}).annotate({ "description": "Response returned after updating a group.", "identifier": "GroupResourceWithSuccess" }) +export type GroupDeletedResource = { + readonly "object": "group.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const GroupDeletedResource = Schema.Struct({ + "object": Schema.Literal("group.deleted").annotate({ "description": "Always `group.deleted`." }), + "id": Schema.String.annotate({ "description": "Identifier of the deleted group." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the group was deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting a group.", + "identifier": "GroupDeletedResource" +}) +export type AssignedRoleDetails = { + readonly "id": string + readonly "name": string + readonly "permissions": ReadonlyArray + readonly "resource_type": string + readonly "predefined_role": boolean + readonly "description": string | null + readonly "created_at": number | null + readonly "updated_at": number | null + readonly "created_by": string | null + readonly "created_by_user_obj": { readonly [x: string]: Schema.Json } | null + readonly "metadata": { readonly [x: string]: Schema.Json } | null + readonly "assignment_sources": + | ReadonlyArray<{ readonly "principal_id": string; readonly "principal_type": string }> + | null +} +export const AssignedRoleDetails = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier for the role." }), + "name": Schema.String.annotate({ "description": "Name of the role." }), + "permissions": Schema.Array(Schema.String).annotate({ "description": "Permissions associated with the role." }), + "resource_type": Schema.String.annotate({ "description": "Resource type the role applies to." }), + "predefined_role": Schema.Boolean.annotate({ "description": "Whether the role is predefined by OpenAI." }), + "description": Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Description of the role." }), + "created_at": Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ "description": "When the role was created." }), + "updated_at": Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ "description": "When the role was last updated." }), + "created_by": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Identifier of the actor who created the role." + }), + "created_by_user_obj": Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + Schema.Null + ]).annotate({ "description": "User details for the actor that created the role, when available." }), + "metadata": Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + Schema.Null + ]).annotate({ "description": "Arbitrary metadata stored on the role." }), + "assignment_sources": Schema.Union([ + Schema.Array(Schema.Struct({ "principal_id": Schema.String, "principal_type": Schema.String })), + Schema.Null + ]).annotate({ "description": "Principals from which the role assignment is inherited, when available." }) +}).annotate({ + "description": "Detailed information about a role assignment entry returned when listing assignments.", + "identifier": "AssignedRoleDetails" +}) +export type PublicAssignOrganizationGroupRoleBody = { readonly "role_id": string } +export const PublicAssignOrganizationGroupRoleBody = Schema.Struct({ + "role_id": Schema.String.annotate({ "description": "Identifier of the role to assign." }) +}).annotate({ + "description": "Request payload for assigning a role to a group or user.", + "identifier": "PublicAssignOrganizationGroupRoleBody" +}) +export type Group = { + readonly "object": "group" + readonly "id": string + readonly "name": string + readonly "created_at": number + readonly "scim_managed": boolean +} +export const Group = Schema.Struct({ + "object": Schema.Literal("group").annotate({ "description": "Always `group`." }), + "id": Schema.String.annotate({ "description": "Identifier for the group." }), + "name": Schema.String.annotate({ "description": "Display name of the group." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the group was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "scim_managed": Schema.Boolean.annotate({ "description": "Whether the group is managed through SCIM." }) +}).annotate({ + "description": "Summary information about a group returned in role assignment responses.", + "identifier": "Group" +}) +export type Role = { + readonly "object": "role" + readonly "id": string + readonly "name": string + readonly "description": string | null + readonly "permissions": ReadonlyArray + readonly "resource_type": string + readonly "predefined_role": boolean +} +export const Role = Schema.Struct({ + "object": Schema.Literal("role").annotate({ "description": "Always `role`." }), + "id": Schema.String.annotate({ "description": "Identifier for the role." }), + "name": Schema.String.annotate({ "description": "Unique name for the role." }), + "description": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Optional description of the role." + }), + "permissions": Schema.Array(Schema.String).annotate({ "description": "Permissions granted by the role." }), + "resource_type": Schema.String.annotate({ + "description": "Resource type the role is bound to (for example `api.organization` or `api.project`)." + }), + "predefined_role": Schema.Boolean.annotate({ "description": "Whether the role is predefined and managed by OpenAI." }) +}).annotate({ + "description": "Details about a role that can be assigned through the public Roles API.", + "identifier": "Role" +}) +export type DeletedRoleAssignmentResource = { readonly "object": string; readonly "deleted": boolean } +export const DeletedRoleAssignmentResource = Schema.Struct({ + "object": Schema.String.annotate({ + "description": "Identifier for the deleted assignment, such as `group.role.deleted` or `user.role.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the assignment was removed." }) +}).annotate({ + "description": "Confirmation payload returned after unassigning a role.", + "identifier": "DeletedRoleAssignmentResource" +}) +export type GroupUser = { readonly "id": string; readonly "name": string; readonly "email": string | null } +export const GroupUser = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.String.annotate({ "description": "The name of the user." }), + "email": Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The email address of the user." }) +}).annotate({ + "description": "Represents an individual user returned when inspecting group membership.", + "identifier": "GroupUser" +}) +export type CreateGroupUserBody = { readonly "user_id": string } +export const CreateGroupUserBody = Schema.Struct({ + "user_id": Schema.String.annotate({ "description": "Identifier of the user to add to the group." }) +}).annotate({ "description": "Request payload for adding a user to a group.", "identifier": "CreateGroupUserBody" }) +export type GroupUserAssignment = { + readonly "object": "group.user" + readonly "user_id": string + readonly "group_id": string +} +export const GroupUserAssignment = Schema.Struct({ + "object": Schema.Literal("group.user").annotate({ "description": "Always `group.user`." }), + "user_id": Schema.String.annotate({ "description": "Identifier of the user that was added." }), + "group_id": Schema.String.annotate({ "description": "Identifier of the group the user was added to." }) +}).annotate({ + "description": "Confirmation payload returned after adding a user to a group.", + "identifier": "GroupUserAssignment" +}) +export type GroupMemberUser = { + readonly "id": string + readonly "name": string + readonly "email": string | null + readonly "picture": string | null + readonly "is_service_account": boolean | null + readonly "user_type": "user" | "tenant_user" +} +export const GroupMemberUser = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier for the user." }), + "name": Schema.String.annotate({ "description": "Display name of the user." }), + "email": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Email address of the user, or `null` for users without an email." + }), + "picture": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "URL of the user's profile picture, if available." + }), + "is_service_account": Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": "Whether the user is a service account." + }), + "user_type": Schema.Literals(["user", "tenant_user"]).annotate({ "description": "The type of user." }) +}).annotate({ + "description": "Details about a user returned from an organization group membership lookup.", + "identifier": "GroupMemberUser" +}) +export type GroupUserDeletedResource = { readonly "object": "group.user.deleted"; readonly "deleted": boolean } +export const GroupUserDeletedResource = Schema.Struct({ + "object": Schema.Literal("group.user.deleted").annotate({ "description": "Always `group.user.deleted`." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the group membership was removed." }) +}).annotate({ + "description": "Confirmation payload returned after removing a user from a group.", + "identifier": "GroupUserDeletedResource" +}) +export type Invite = { + readonly "object": "organization.invite" + readonly "id": string + readonly "email": string + readonly "role": "owner" | "reader" + readonly "status": "accepted" | "expired" | "pending" + readonly "created_at": number + readonly "expires_at"?: number | null + readonly "accepted_at"?: number | null + readonly "projects": ReadonlyArray<{ readonly "id": string; readonly "role": "member" | "owner" }> +} +export const Invite = Schema.Struct({ + "object": Schema.Literal("organization.invite").annotate({ + "description": "The object type, which is always `organization.invite`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "email": Schema.String.annotate({ "description": "The email address of the individual to whom the invite was sent" }), + "role": Schema.Literals(["owner", "reader"]).annotate({ "description": "`owner` or `reader`" }), + "status": Schema.Literals(["accepted", "expired", "pending"]).annotate({ + "description": "`accepted`,`expired`, or `pending`" + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the invite was sent.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ "description": "The Unix timestamp (in seconds) of when the invite expires." }) + ), + "accepted_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ "description": "The Unix timestamp (in seconds) of when the invite was accepted." }) + ), + "projects": Schema.Array( + Schema.Struct({ + "id": Schema.String.annotate({ "description": "Project's public ID" }), + "role": Schema.Literals(["member", "owner"]).annotate({ "description": "Project membership role" }) + }) + ).annotate({ "description": "The projects that were granted membership upon acceptance of the invite." }) +}).annotate({ "description": "Represents an individual `invite` to the organization.", "identifier": "Invite" }) +export type InviteRequest = { + readonly "email": string + readonly "role": "reader" | "owner" + readonly "projects"?: ReadonlyArray<{ readonly "id": string; readonly "role": "member" | "owner" }> +} +export const InviteRequest = Schema.Struct({ + "email": Schema.String.annotate({ "description": "Send an email to this address" }), + "role": Schema.Literals(["reader", "owner"]).annotate({ "description": "`owner` or `reader`" }), + "projects": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "id": Schema.String.annotate({ "description": "Project's public ID" }), + "role": Schema.Literals(["member", "owner"]).annotate({ "description": "Project membership role" }) + }) + ).annotate({ + "description": + "An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project for compatibility with legacy behavior. If empty list is passed, the user will not be invited to any projects, including the default one." + }) + ) +}).annotate({ "identifier": "InviteRequest" }) +export type InviteDeleteResponse = { + readonly "object": "organization.invite.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const InviteDeleteResponse = Schema.Struct({ + "object": Schema.Literal("organization.invite.deleted").annotate({ + "description": "The object type, which is always `organization.invite.deleted`" + }), + "id": Schema.String, + "deleted": Schema.Boolean +}).annotate({ "identifier": "InviteDeleteResponse" }) +export type PublicProjectResidency = + | "GLOBAL" + | "US_STORAGE_PROCESSING" + | "EU_STORAGE_PROCESSING" + | "JP_STORAGE" + | "KR_STORAGE" + | "CA_STORAGE" + | "SG_STORAGE" + | "IN_STORAGE" + | "AU_STORAGE" + | "GB_STORAGE" + | "AE_STORAGE" + | "AE_STORAGE_PROCESSING" +export const PublicProjectResidency = Schema.Literals([ + "GLOBAL", + "US_STORAGE_PROCESSING", + "EU_STORAGE_PROCESSING", + "JP_STORAGE", + "KR_STORAGE", + "CA_STORAGE", + "SG_STORAGE", + "IN_STORAGE", + "AU_STORAGE", + "GB_STORAGE", + "AE_STORAGE", + "AE_STORAGE_PROCESSING" +]).annotate({ "identifier": "PublicProjectResidency" }) +export type ProjectUpdateRequest = { + readonly "name"?: string | null + readonly "external_key_id"?: string | null + readonly "geography"?: string | null +} +export const ProjectUpdateRequest = Schema.Struct({ + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The updated name of the project, this name appears in reports." + }) + ), + "external_key_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "External key ID to associate with the project." + }) + ), + "geography": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "Geography for the project.\nDeprecated: use `residency` when creating a project to configure data residency. This field is retained for backward compatibility." + }) + ) +}).annotate({ "identifier": "ProjectUpdateRequest" }) +export type ProjectApiKeyOwnerUser = { + readonly "id": string + readonly "email": string + readonly "name": string + readonly "created_at": number + readonly "role": string +} +export const ProjectApiKeyOwnerUser = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "email": Schema.String.annotate({ "description": "The email address of the user." }), + "name": Schema.String.annotate({ "description": "The name of the user." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the user was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "role": Schema.String.annotate({ "description": "The user's project role." }) +}).annotate({ "description": "The user that owns a project API key.", "identifier": "ProjectApiKeyOwnerUser" }) +export type ProjectApiKeyOwnerServiceAccount = { + readonly "id": string + readonly "name": string + readonly "created_at": number + readonly "role": string +} +export const ProjectApiKeyOwnerServiceAccount = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.String.annotate({ "description": "The name of the service account." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the service account was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "role": Schema.String.annotate({ "description": "The service account's project role." }) +}).annotate({ + "description": "The service account that owns a project API key.", + "identifier": "ProjectApiKeyOwnerServiceAccount" +}) +export type ProjectApiKeyDeleteResponse = { + readonly "object": "organization.project.api_key.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const ProjectApiKeyDeleteResponse = Schema.Struct({ + "object": Schema.Literal("organization.project.api_key.deleted"), + "id": Schema.String, + "deleted": Schema.Boolean +}).annotate({ "identifier": "ProjectApiKeyDeleteResponse" }) +export type OrganizationProjectCertificate = { + readonly "object": "organization.project.certificate" + readonly "id": string + readonly "name": string | null + readonly "created_at": number + readonly "certificate_details": { readonly "valid_at"?: number; readonly "expires_at"?: number } + readonly "active": boolean +} +export const OrganizationProjectCertificate = Schema.Struct({ + "object": Schema.Literal("organization.project.certificate").annotate({ + "description": "The object type, which is always `organization.project.certificate`." + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the certificate." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate was uploaded.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "certificate_details": Schema.Struct({ + "valid_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate becomes valid.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the certificate expires.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }), + "active": Schema.Boolean.annotate({ + "description": "Whether the certificate is currently active at the project level." + }) +}).annotate({ + "description": "Represents an individual certificate configured at the project level.", + "identifier": "OrganizationProjectCertificate" +}) +export type ProjectDataRetention = { + readonly "object": "project.data_retention" + readonly "type": + | "organization_default" + | "none" + | "zero_data_retention" + | "modified_abuse_monitoring" + | "enhanced_zero_data_retention" + | "enhanced_modified_abuse_monitoring" +} +export const ProjectDataRetention = Schema.Struct({ + "object": Schema.Literal("project.data_retention").annotate({ + "description": "The object type, which is always `project.data_retention`." + }), + "type": Schema.Literals([ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring" + ]).annotate({ "description": "The configured project data retention type." }) +}).annotate({ + "description": "Represents a project's data retention control setting.", + "identifier": "ProjectDataRetention" +}) +export type UpdateProjectDataRetentionBody = { + readonly "retention_type": + | "organization_default" + | "none" + | "zero_data_retention" + | "modified_abuse_monitoring" + | "enhanced_zero_data_retention" + | "enhanced_modified_abuse_monitoring" +} +export const UpdateProjectDataRetentionBody = Schema.Struct({ + "retention_type": Schema.Literals([ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring" + ]).annotate({ "description": "The desired project data retention type." }) +}).annotate({ + "description": "Parameters for updating project data retention controls.", + "identifier": "UpdateProjectDataRetentionBody" +}) +export type ProjectGroup = { + readonly "object": "project.group" + readonly "project_id": string + readonly "group_id": string + readonly "group_name": string + readonly "group_type": "group" | "tenant_group" + readonly "created_at": number +} +export const ProjectGroup = Schema.Struct({ + "object": Schema.Literal("project.group").annotate({ "description": "Always `project.group`." }), + "project_id": Schema.String.annotate({ "description": "Identifier of the project." }), + "group_id": Schema.String.annotate({ "description": "Identifier of the group that has access to the project." }), + "group_name": Schema.String.annotate({ "description": "Display name of the group." }), + "group_type": Schema.Literals(["group", "tenant_group"]).annotate({ "description": "The type of the group." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the group was granted project access.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "description": "Details about a group's membership in a project.", "identifier": "ProjectGroup" }) +export type InviteProjectGroupBody = { readonly "group_id": string; readonly "role": string } +export const InviteProjectGroupBody = Schema.Struct({ + "group_id": Schema.String.annotate({ "description": "Identifier of the group to add to the project." }), + "role": Schema.String.annotate({ "description": "Identifier of the project role to grant to the group." }) +}).annotate({ + "description": "Request payload for granting a group access to a project.", + "identifier": "InviteProjectGroupBody" +}) +export type ProjectGroupDeletedResource = { readonly "object": "project.group.deleted"; readonly "deleted": boolean } +export const ProjectGroupDeletedResource = Schema.Struct({ + "object": Schema.Literal("project.group.deleted").annotate({ "description": "Always `project.group.deleted`." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the group membership in the project was removed." }) +}).annotate({ + "description": "Confirmation payload returned after removing a group from a project.", + "identifier": "ProjectGroupDeletedResource" +}) +export type HostedToolPermission = { readonly "enabled": boolean } +export const HostedToolPermission = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Whether the hosted tool is enabled for the project." }) +}).annotate({ + "description": "Permission state for a single hosted tool on a project.", + "identifier": "HostedToolPermission" +}) +export type HostedToolPermissionUpdate = { readonly "enabled": boolean } +export const HostedToolPermissionUpdate = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Whether to enable the hosted tool for the project." }) +}).annotate({ "identifier": "HostedToolPermissionUpdate" }) +export type ProjectModelPermissions = { + readonly "object": "project.model_permissions" + readonly "mode": "allow_list" | "deny_list" + readonly "model_ids": ReadonlyArray +} +export const ProjectModelPermissions = Schema.Struct({ + "object": Schema.Literal("project.model_permissions").annotate({ + "description": "The object type, which is always `project.model_permissions`." + }), + "mode": Schema.Literals(["allow_list", "deny_list"]).annotate({ + "description": "Whether the project uses an allowlist or a denylist." + }), + "model_ids": Schema.Array(Schema.String).annotate({ + "description": "The model IDs included in the model permissions policy." + }) +}).annotate({ + "description": "Represents the model allowlist or denylist policy for a project.", + "identifier": "ProjectModelPermissions" +}) +export type ProjectModelPermissionsUpdateRequest = { + readonly "mode": "allow_list" | "deny_list" + readonly "model_ids": ReadonlyArray +} +export const ProjectModelPermissionsUpdateRequest = Schema.Struct({ + "mode": Schema.Literals(["allow_list", "deny_list"]).annotate({ + "description": "The model permissions mode to apply." + }), + "model_ids": Schema.Array(Schema.String).annotate({ + "description": "The model IDs included in this permissions policy." + }) +}).annotate({ "identifier": "ProjectModelPermissionsUpdateRequest" }) +export type ProjectModelPermissionsDeleteResponse = { + readonly "object": "project.model_permissions.deleted" + readonly "deleted": boolean +} +export const ProjectModelPermissionsDeleteResponse = Schema.Struct({ + "object": Schema.Literal("project.model_permissions.deleted").annotate({ + "description": "The object type, which is always `project.model_permissions.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the project model permissions were deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting project model permissions.", + "identifier": "ProjectModelPermissionsDeleteResponse" +}) +export type ProjectRateLimit = { + readonly "object": "project.rate_limit" + readonly "id": string + readonly "model": string + readonly "max_requests_per_1_minute": number + readonly "max_tokens_per_1_minute": number + readonly "max_images_per_1_minute"?: number + readonly "max_audio_megabytes_per_1_minute"?: number + readonly "max_requests_per_1_day"?: number + readonly "batch_1_day_max_input_tokens"?: number +} +export const ProjectRateLimit = Schema.Struct({ + "object": Schema.Literal("project.rate_limit").annotate({ + "description": "The object type, which is always `project.rate_limit`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "model": Schema.String.annotate({ "description": "The model this rate limit applies to." }), + "max_requests_per_1_minute": Schema.Number.annotate({ "description": "The maximum requests per minute." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "max_tokens_per_1_minute": Schema.Number.annotate({ "description": "The maximum tokens per minute." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "max_images_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum images per minute. Only present for relevant models." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "max_audio_megabytes_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum audio megabytes per minute. Only present for relevant models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "max_requests_per_1_day": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum requests per day. Only present for relevant models." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "batch_1_day_max_input_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum batch input tokens per day. Only present for relevant models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) +}).annotate({ "description": "Represents a project rate limit config.", "identifier": "ProjectRateLimit" }) +export type ProjectRateLimitUpdateRequest = { + readonly "max_requests_per_1_minute"?: number + readonly "max_tokens_per_1_minute"?: number + readonly "max_images_per_1_minute"?: number + readonly "max_audio_megabytes_per_1_minute"?: number + readonly "max_requests_per_1_day"?: number + readonly "batch_1_day_max_input_tokens"?: number +} +export const ProjectRateLimitUpdateRequest = Schema.Struct({ + "max_requests_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum requests per minute." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "max_tokens_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum tokens per minute." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "max_images_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum images per minute. Only relevant for certain models." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "max_audio_megabytes_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum audio megabytes per minute. Only relevant for certain models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "max_requests_per_1_day": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum requests per day. Only relevant for certain models." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "batch_1_day_max_input_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum batch input tokens per day. Only relevant for certain models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) +}).annotate({ "identifier": "ProjectRateLimitUpdateRequest" }) +export type ProjectServiceAccount = { + readonly "object": "organization.project.service_account" + readonly "id": string + readonly "name": string + readonly "role": "owner" | "member" | "none" + readonly "created_at": number +} +export const ProjectServiceAccount = Schema.Struct({ + "object": Schema.Literal("organization.project.service_account").annotate({ + "description": "The object type, which is always `organization.project.service_account`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.String.annotate({ "description": "The name of the service account" }), + "role": Schema.Literals(["owner", "member", "none"]).annotate({ "description": "`owner`, `member`, or `none`" }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the service account was created", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Represents an individual service account in a project.", + "identifier": "ProjectServiceAccount" +}) +export type ProjectServiceAccountCreateRequest = { + readonly "name": string + readonly "create_service_account_only"?: boolean | null + readonly "expires_in_seconds"?: number | null +} +export const ProjectServiceAccountCreateRequest = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the service account being created." }), + "create_service_account_only": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ "description": "Create the service account without default roles or an API key." }), + Schema.Null + ]) + ), + "expires_in_seconds": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(31536000).annotate({ "expected": "a value less than or equal to 31536000" })), + Schema.Null + ]).annotate({ + "description": + "Number of seconds until the initial API key expires. If omitted or null, the key does not expire unless the effective organization or project policy requires an expiration. When a policy sets a maximum lifetime, this value must be provided and must not exceed that limit. A non-null value cannot be used when `create_service_account_only` is true." + }) + ) +}).annotate({ "identifier": "ProjectServiceAccountCreateRequest" }) +export type ProjectServiceAccountApiKey = { + readonly "object": "organization.project.service_account.api_key" + readonly "value": string + readonly "name": string + readonly "created_at": number + readonly "expires_at"?: number | null + readonly "id": string +} +export const ProjectServiceAccountApiKey = Schema.Struct({ + "object": Schema.Literal("organization.project.service_account.api_key").annotate({ + "description": "The object type, which is always `organization.project.service_account.api_key`" + }), + "value": Schema.String, + "name": Schema.String, + "created_at": Schema.Number.annotate({ "format": "unixtime" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ + "description": "The Unix timestamp (in seconds) when the API key expires, or null if it does not expire." + }) + ), + "id": Schema.String +}).annotate({ "identifier": "ProjectServiceAccountApiKey" }) +export type UpdateProjectServiceAccountBody = { readonly "name"?: string; readonly "role"?: "member" | "owner" } +export const UpdateProjectServiceAccountBody = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The updated service account name." })), + "role": Schema.optionalKey( + Schema.Literals(["member", "owner"]).annotate({ "description": "The updated service account role." }) + ) +}).annotate({ + "description": "Parameters for updating a project service account.", + "identifier": "UpdateProjectServiceAccountBody" +}) +export type ProjectServiceAccountDeleteResponse = { + readonly "object": "organization.project.service_account.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const ProjectServiceAccountDeleteResponse = Schema.Struct({ + "object": Schema.Literal("organization.project.service_account.deleted"), + "id": Schema.String, + "deleted": Schema.Boolean +}).annotate({ "identifier": "ProjectServiceAccountDeleteResponse" }) +export type SpendAlertNotificationChannel = { + readonly "type": "email" + readonly "recipients": ReadonlyArray + readonly "subject_prefix"?: string | null +} +export const SpendAlertNotificationChannel = Schema.Struct({ + "type": Schema.Literal("email").annotate({ + "description": "The notification channel type. Currently only `email` is supported." + }), + "recipients": Schema.Array(Schema.String).annotate({ + "description": "Email addresses that receive the spend alert notification." + }), + "subject_prefix": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Optional subject prefix for alert emails." }) + ) +}).annotate({ + "description": "Email notification settings for a spend alert.", + "identifier": "SpendAlertNotificationChannel" +}) +export type ProjectSpendAlertDeletedResource = { + readonly "id": string + readonly "object": "project.spend_alert.deleted" + readonly "deleted": boolean +} +export const ProjectSpendAlertDeletedResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The deleted spend alert ID." }), + "object": Schema.Literal("project.spend_alert.deleted").annotate({ + "description": "Always `project.spend_alert.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the spend alert was deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting a project spend alert.", + "identifier": "ProjectSpendAlertDeletedResource" +}) +export type ProjectUser = { + readonly "object": "organization.project.user" + readonly "id": string + readonly "name"?: string | null + readonly "email"?: string | null + readonly "role": string + readonly "added_at": number +} +export const ProjectUser = Schema.Struct({ + "object": Schema.Literal("organization.project.user").annotate({ + "description": "The object type, which is always `organization.project.user`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the user" }) + ), + "email": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The email address of the user" }) + ), + "role": Schema.String.annotate({ "description": "`owner` or `member`" }), + "added_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the project was added.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "description": "Represents an individual user in a project.", "identifier": "ProjectUser" }) +export type ProjectUserCreateRequest = { + readonly "user_id"?: string | null + readonly "email"?: string | null + readonly "role": string +} +export const ProjectUserCreateRequest = Schema.Struct({ + "user_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The ID of the user." }) + ), + "email": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Email of the user to add." }) + ), + "role": Schema.String.annotate({ "description": "`owner` or `member`" }) +}).annotate({ "identifier": "ProjectUserCreateRequest" }) +export type ProjectUserUpdateRequest = { readonly "role"?: string | null } +export const ProjectUserUpdateRequest = Schema.Struct({ + "role": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "`owner` or `member`" }) + ) +}).annotate({ "identifier": "ProjectUserUpdateRequest" }) +export type ProjectUserDeleteResponse = { + readonly "object": "organization.project.user.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const ProjectUserDeleteResponse = Schema.Struct({ + "object": Schema.Literal("organization.project.user.deleted"), + "id": Schema.String, + "deleted": Schema.Boolean +}).annotate({ "identifier": "ProjectUserDeleteResponse" }) +export type PublicCreateOrganizationRoleBody = { + readonly "role_name": string + readonly "permissions": ReadonlyArray + readonly "description"?: string | null +} +export const PublicCreateOrganizationRoleBody = Schema.Struct({ + "role_name": Schema.String.annotate({ "description": "Unique name for the role." }), + "permissions": Schema.Array(Schema.String).annotate({ "description": "Permissions to grant to the role." }), + "description": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Optional description of the role." }) + ) +}).annotate({ + "description": "Request payload for creating a custom role.", + "identifier": "PublicCreateOrganizationRoleBody" +}) +export type PublicUpdateOrganizationRoleBody = { + readonly "permissions"?: ReadonlyArray | null + readonly "description"?: string | null + readonly "role_name"?: string | null +} +export const PublicUpdateOrganizationRoleBody = Schema.Struct({ + "permissions": Schema.optionalKey( + Schema.Union([Schema.Array(Schema.String), Schema.Null]).annotate({ + "description": "Updated set of permissions for the role." + }) + ), + "description": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "New description for the role." }) + ), + "role_name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "New name for the role." }) + ) +}).annotate({ + "description": "Request payload for updating an existing role.", + "identifier": "PublicUpdateOrganizationRoleBody" +}) +export type RoleDeletedResource = { + readonly "object": "role.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const RoleDeletedResource = Schema.Struct({ + "object": Schema.Literal("role.deleted").annotate({ "description": "Always `role.deleted`." }), + "id": Schema.String.annotate({ "description": "Identifier of the deleted role." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the role was deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting a role.", + "identifier": "RoleDeletedResource" +}) +export type OrganizationSpendAlertDeletedResource = { + readonly "id": string + readonly "object": "organization.spend_alert.deleted" + readonly "deleted": boolean +} +export const OrganizationSpendAlertDeletedResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The deleted spend alert ID." }), + "object": Schema.Literal("organization.spend_alert.deleted").annotate({ + "description": "Always `organization.spend_alert.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the spend alert was deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting an organization spend alert.", + "identifier": "OrganizationSpendAlertDeletedResource" +}) +export type User = { + readonly "object": "organization.user" + readonly "id": string + readonly "name"?: string | null + readonly "email"?: string | null + readonly "role"?: string | null + readonly "added_at": number + readonly "is_default"?: boolean + readonly "created"?: number + readonly "user"?: { + readonly "object": "user" + readonly "id": string + readonly "email"?: string | null + readonly "name"?: string | null + readonly "picture"?: string | null + readonly "enabled"?: boolean | null + readonly "banned"?: boolean | null + readonly "banned_at"?: number | null + } + readonly "is_service_account"?: boolean + readonly "is_scale_tier_authorized_purchaser"?: boolean | null + readonly "is_scim_managed"?: boolean + readonly "api_key_last_used_at"?: number | null + readonly "technical_level"?: string | null + readonly "developer_persona"?: string | null + readonly "projects"?: { + readonly "object": "list" + readonly "data": ReadonlyArray< + { readonly "id"?: string | null; readonly "name"?: string | null; readonly "role"?: string | null } + > + } | null +} +export const User = Schema.Struct({ + "object": Schema.Literal("organization.user").annotate({ + "description": "The object type, which is always `organization.user`" + }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the user" }) + ), + "email": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The email address of the user" }) + ), + "role": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "`owner` or `reader`" }) + ), + "added_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the user was added.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "is_default": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this is the organization's default user." }) + ), + "created": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the user was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "user": Schema.optionalKey( + Schema.Struct({ + "object": Schema.Literal("user"), + "id": Schema.String, + "email": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "name": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "picture": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "enabled": Schema.optionalKey(Schema.Union([Schema.Boolean, Schema.Null])), + "banned": Schema.optionalKey(Schema.Union([Schema.Boolean, Schema.Null])), + "banned_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) + }).annotate({ "description": "Nested user details." }) + ), + "is_service_account": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the user is a service account." }) + ), + "is_scale_tier_authorized_purchaser": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": "Whether the user is an authorized purchaser for Scale Tier." + }) + ), + "is_scim_managed": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the user is managed through SCIM." }) + ), + "api_key_last_used_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ "description": "The Unix timestamp (in seconds) of the user's last API key usage." }) + ), + "technical_level": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The technical level metadata for the user." }) + ), + "developer_persona": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The developer persona metadata for the user." + }) + ), + "projects": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array( + Schema.Struct({ + "id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "name": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "role": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) + }) + ) + }), + Schema.Null + ]).annotate({ "description": "Projects associated with the user, if included." }) + ) +}).annotate({ "description": "Represents an individual `user` within an organization.", "identifier": "User" }) +export type UserRoleUpdateRequest = { + readonly "role"?: string | null + readonly "role_id"?: string | null + readonly "technical_level"?: string | null + readonly "developer_persona"?: string | null +} +export const UserRoleUpdateRequest = Schema.Struct({ + "role": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "`owner` or `reader`" }) + ), + "role_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Role ID to assign to the user." }) + ), + "technical_level": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Technical level metadata." }) + ), + "developer_persona": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Developer persona metadata." }) + ) +}).annotate({ "identifier": "UserRoleUpdateRequest" }) +export type UserDeleteResponse = { + readonly "object": "organization.user.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const UserDeleteResponse = Schema.Struct({ + "object": Schema.Literal("organization.user.deleted"), + "id": Schema.String, + "deleted": Schema.Boolean +}).annotate({ "identifier": "UserDeleteResponse" }) +export type RealtimeAudioFormats = { readonly "type"?: "audio/pcm"; readonly "rate"?: 24000 } | { + readonly "type"?: "audio/pcmu" +} | { readonly "type"?: "audio/pcma" } +export const RealtimeAudioFormats = Schema.Union([ + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("audio/pcm").annotate({ "description": "The audio format. Always `audio/pcm`." }) + ), + "rate": Schema.optionalKey( + Schema.Literal(24000).annotate({ "description": "The sample rate of the audio. Always `24000`." }) + ) + }).annotate({ + "title": "PCM audio format", + "description": "The PCM audio format. Only a 24kHz sample rate is supported." + }), + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("audio/pcmu").annotate({ "description": "The audio format. Always `audio/pcmu`." }) + ) + }).annotate({ "title": "PCMU audio format", "description": "The G.711 μ-law format." }), + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("audio/pcma").annotate({ "description": "The audio format. Always `audio/pcma`." }) + ) + }).annotate({ "title": "PCMA audio format", "description": "The G.711 A-law format." }) +]).annotate({ "identifier": "RealtimeAudioFormats" }) +export type AudioTranscription = { + readonly "model"?: + | string + | "whisper-1" + | "gpt-transcribe" + | "gpt-live-transcribe" + | "gpt-4o-mini-transcribe" + | "gpt-4o-mini-transcribe-2025-12-15" + | "gpt-4o-transcribe" + | "gpt-4o-transcribe-diarize" + | "gpt-realtime-whisper" + readonly "language"?: string + readonly "languages"?: ReadonlyArray + readonly "keywords"?: ReadonlyArray + readonly "prompt"?: string + readonly "delay"?: "minimal" | "low" | "medium" | "high" | "xhigh" +} +export const AudioTranscription = Schema.Struct({ + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "whisper-1", + "gpt-transcribe", + "gpt-live-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + "gpt-realtime-whisper" + ]) + ]).annotate({ + "description": + "The model to use for transcription. Current options are `whisper-1`, `gpt-transcribe`, `gpt-live-transcribe`, `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, `gpt-4o-transcribe-diarize`, and `gpt-realtime-whisper`. Use `gpt-4o-transcribe-diarize` when you need diarization with speaker labels.\n" + }) + ), + "language": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format\nwill improve accuracy and latency.\n" + }) + ), + "languages": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Possible languages of the input audio, in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. Supported by `gpt-transcribe` and `gpt-live-transcribe`.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ), + "keywords": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Words or phrases to guide transcription of the input audio. Supported by `gpt-transcribe` and `gpt-live-transcribe`.\n" + }) + ), + "prompt": Schema.optionalKey(Schema.String.annotate({ + "description": + "An optional text to guide the model's style or continue a previous audio\nsegment.\nFor `whisper-1`, the [prompt is a list of keywords](https://developers.openai.com/api/docs/guides/speech-to-text#prompting).\nFor `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the prompt is a free text string, for example \"expect words related to technology\".\nPrompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions.\n" + })), + "delay": Schema.optionalKey( + Schema.Literals(["minimal", "low", "medium", "high", "xhigh"]).annotate({ + "description": + "Controls how long the model waits before emitting transcription text.\nHigher values can improve transcription accuracy at the cost of latency.\nOnly supported with `gpt-realtime-whisper` in GA Realtime sessions.\n" + }) + ) +}).annotate({ "identifier": "AudioTranscription" }) +export type NoiseReductionType = "near_field" | "far_field" +export const NoiseReductionType = Schema.Literals(["near_field", "far_field"]).annotate({ + "description": + "Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.\n", + "identifier": "NoiseReductionType" +}) +export type RealtimeTurnDetection = { + readonly "type": "server_vad" + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + readonly "create_response"?: boolean + readonly "interrupt_response"?: boolean + readonly "idle_timeout_ms"?: number | null +} | { + readonly "type": "semantic_vad" + readonly "eagerness"?: "low" | "medium" | "high" | "auto" + readonly "create_response"?: boolean + readonly "interrupt_response"?: boolean +} | null +export const RealtimeTurnDetection = Schema.Union([ + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("server_vad").annotate({ + "description": "Type of turn detection, `server_vad` to turn on simple Server VAD.\n" + }), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "create_response": Schema.optionalKey(Schema.Boolean.annotate({ + "description": + "Whether or not to automatically generate a response when a VAD stop event occurs. If `interrupt_response` is set to `false` this may fail to create a response if the model is already responding.\n\nIf both `create_response` and `interrupt_response` are set to `false`, the model will never respond automatically but VAD events will still be emitted.\n" + })), + "interrupt_response": Schema.optionalKey(Schema.Boolean.annotate({ + "description": + "Whether or not to automatically interrupt (cancel) any ongoing response with output to the default\nconversation (i.e. `conversation` of `auto`) when a VAD start event occurs. If `true` then the response will be cancelled, otherwise it will continue until complete.\n\nIf both `create_response` and `interrupt_response` are set to `false`, the model will never respond automatically but VAD events will still be emitted.\n" + })), + "idle_timeout_ms": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "Optional timeout after which a model response will be triggered automatically. This is\nuseful for situations in which a long pause from the user is unexpected, such as a phone\ncall. The model will effectively prompt the user to continue the conversation based\non the current context.\n\nThe timeout value will be applied after the last model response's audio has finished playing,\ni.e. it's set to the `response.done` time plus audio playback duration.\n\nAn `input_audio_buffer.timeout_triggered` event (plus events\nassociated with the Response) will be emitted when the timeout is reached.\nIdle timeout is currently only supported for `server_vad` mode.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(5000).annotate({ "expected": "a value greater than or equal to 5000" }) + ).check(Schema.isLessThanOrEqualTo(30000).annotate({ "expected": "a value less than or equal to 30000" })), + Schema.Null + ])) + }).annotate({ + "title": "Server VAD", + "description": + "Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence." + }), + Schema.Struct({ + "type": Schema.Literal("semantic_vad").annotate({ + "description": "Type of turn detection, `semantic_vad` to turn on Semantic VAD.\n" + }), + "eagerness": Schema.optionalKey( + Schema.Literals(["low", "medium", "high", "auto"]).annotate({ + "description": + "Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, 4s, and 2s respectively.\n" + }) + ), + "create_response": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether or not to automatically generate a response when a VAD stop event occurs.\n" + }) + ), + "interrupt_response": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether or not to automatically interrupt any ongoing response with output to the default\nconversation (i.e. `conversation` of `auto`) when a VAD start event occurs.\n" + }) + ) + }).annotate({ + "title": "Semantic VAD", + "description": + "Server-side semantic turn detection which uses a model to determine when the user has finished speaking." + }) + ], { mode: "oneOf" }).annotate({ + "title": "Realtime Turn Detection", + "description": + "Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.\n\nServer VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\n\nSemantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with \"uhhm\", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.\n\nFor `gpt-realtime-whisper` transcription sessions, turn detection must be\nset to `null`; VAD is not supported.\n" + }), + Schema.Null +]).annotate({ "identifier": "RealtimeTurnDetection" }) +export type RealtimeFunctionTool = { + readonly "type"?: "function" + readonly "name"?: string + readonly "description"?: string + readonly "parameters"?: { readonly [x: string]: Schema.Json } +} +export const RealtimeFunctionTool = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("function").annotate({ "description": "The type of the tool, i.e. `function`." }) + ), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the function." })), + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The description of the function, including guidance on when and how\nto call it, and guidance about what to tell the user when calling\n(if anything).\n" + }) + ), + "parameters": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Parameters of the function in JSON Schema." + }) + ) +}).annotate({ "title": "Function tool", "identifier": "RealtimeFunctionTool" }) +export type ToolChoiceOptions = "none" | "auto" | "required" +export const ToolChoiceOptions = Schema.Literals(["none", "auto", "required"]).annotate({ + "title": "Tool choice mode", + "description": + "Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n", + "identifier": "ToolChoiceOptions" +}) +export type ToolChoiceFunction = { readonly "type": "function"; readonly "name": string } +export const ToolChoiceFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ + "description": "For function calling, the type is always `function`." + }), + "name": Schema.String.annotate({ "description": "The name of the function to call." }) +}).annotate({ + "title": "Function tool", + "description": "Use this option to force the model to call a specific function.\n", + "identifier": "ToolChoiceFunction" +}) +export type ToolChoiceMCP = { readonly "type": "mcp"; readonly "server_label": string; readonly "name"?: string | null } +export const ToolChoiceMCP = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "For MCP tools, the type is always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server to use.\n" }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the tool to call on the server.\n" }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP tool", + "description": "Use this option to force the model to call a specific tool on a remote MCP server.\n", + "identifier": "ToolChoiceMCP" +}) +export type RealtimeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" +export const RealtimeReasoningEffort = Schema.Literals(["minimal", "low", "medium", "high", "xhigh"]).annotate({ + "description": "Constrains effort on reasoning for reasoning-capable Realtime models such as\n`gpt-realtime-2`.\n", + "identifier": "RealtimeReasoningEffort" +}) +export type RealtimeTruncation = "auto" | "disabled" | { + readonly "type": "retention_ratio" + readonly "retention_ratio": number + readonly "token_limits"?: { readonly "post_instructions"?: number } +} +export const RealtimeTruncation = Schema.Union([ + Schema.Literals(["auto", "disabled"]).annotate({ + "description": + "The truncation strategy to use for the session. `auto` is the default truncation strategy. `disabled` will disable truncation and emit errors when the conversation exceeds the input token limit." + }), + Schema.Struct({ + "type": Schema.Literal("retention_ratio").annotate({ "description": "Use retention ratio truncation." }), + "retention_ratio": Schema.Number.annotate({ + "description": + "Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when the conversation exceeds the input token limit. Setting this to `0.8` means that messages will be dropped until 80% of the maximum allowed tokens are used. This helps reduce the frequency of truncations and improve cache rates.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + "token_limits": Schema.optionalKey( + Schema.Struct({ + "post_instructions": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Maximum tokens allowed in the conversation after instructions (which including tool definitions). For example, setting this to 5,000 would mean that truncation would occur when the conversation exceeds 5,000 tokens after instructions. This cannot be higher than the model's context window size minus the maximum output tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) + ) + }).annotate({ + "description": + "Optional custom token limits for this truncation strategy. If not provided, the model's default token limits will be used." + }) + ) + }).annotate({ + "title": "Retention ratio truncation", + "description": + "Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage." + }) +], { mode: "oneOf" }).annotate({ + "title": "Realtime Truncation Controls", + "description": + "When the number of tokens in a conversation exceeds the model's input token limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before truncation occurs.\n\nClients can configure truncation behavior to truncate with a lower max token limit, which is an effective way to control token usage and cost.\n\nTruncation will reduce the number of cached tokens on the next turn (busting the cache), since messages are dropped from the beginning of the context. However, clients can also configure truncation to retain messages up to a fraction of the maximum context size, which will reduce the need for future truncations and thus improve the cache rate.\n\nTruncation can be disabled entirely, which means the server will never truncate but would instead return an error if the conversation exceeds the model's input token limit.\n", + "identifier": "RealtimeTruncation" +}) +export type RealtimeCallReferRequest = { readonly "target_uri": string } +export const RealtimeCallReferRequest = Schema.Struct({ + "target_uri": Schema.String.annotate({ + "description": + "URI that should appear in the SIP Refer-To header. Supports values like\n`tel:+14155550123` or `sip:agent@example.com`." + }) +}).annotate({ + "title": "Realtime call refer request", + "description": "Parameters required to transfer a SIP call to a new destination using the\nRealtime API.", + "identifier": "RealtimeCallReferRequest" +}) +export type RealtimeCallRejectRequest = { readonly "status_code"?: number } +export const RealtimeCallRejectRequest = Schema.Struct({ + "status_code": Schema.optionalKey( + Schema.Number.annotate({ + "description": "SIP response code to send back to the caller. Defaults to `603` (Decline)\nwhen omitted." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) +}).annotate({ + "title": "Realtime call reject request", + "description": "Parameters used to decline an incoming SIP call handled by the Realtime API.", + "identifier": "RealtimeCallRejectRequest" +}) +export type AudioTranscriptionResponse = { + readonly "model"?: + | string + | "whisper-1" + | "gpt-transcribe" + | "gpt-live-transcribe" + | "gpt-4o-mini-transcribe" + | "gpt-4o-mini-transcribe-2025-12-15" + | "gpt-4o-transcribe" + | "gpt-4o-transcribe-diarize" + | "gpt-realtime-whisper" + readonly "language"?: string | null + readonly "languages"?: ReadonlyArray + readonly "prompt"?: string +} +export const AudioTranscriptionResponse = Schema.Struct({ + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "whisper-1", + "gpt-transcribe", + "gpt-live-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + "gpt-realtime-whisper" + ]) + ]).annotate({ + "description": + "The model used for transcription. Current options are `whisper-1`, `gpt-transcribe`, `gpt-live-transcribe`, `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, `gpt-4o-transcribe-diarize`, and `gpt-realtime-whisper`.\n" + }) + ), + "language": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The language of the input audio.\n" }) + ), + "languages": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The possible input audio languages configured for transcription, in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ), + "prompt": Schema.optionalKey( + Schema.String.annotate({ "description": "The prompt configured for input audio transcription, when present.\n" }) + ) +}).annotate({ "identifier": "AudioTranscriptionResponse" }) +export type ToolChoiceAllowed = { + readonly "type": "allowed_tools" + readonly "mode": "auto" | "required" + readonly "tools": ReadonlyArray<{ readonly [x: string]: Schema.Json }> +} +export const ToolChoiceAllowed = Schema.Struct({ + "type": Schema.Literal("allowed_tools").annotate({ + "description": "Allowed tool configuration type. Always `allowed_tools`." + }), + "mode": Schema.Literals(["auto", "required"]).annotate({ + "description": + "Constrains the tools available to the model to a pre-defined set.\n\n`auto` allows the model to pick from among the allowed tools and generate a\nmessage.\n\n`required` requires the model to call one or more of the allowed tools.\n" + }), + "tools": Schema.Array( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A tool definition that the model should be allowed to call.\n" + }) + ).annotate({ + "description": + "A list of tool definitions that the model should be allowed to call.\n\nFor the Responses API, the list of tool definitions might look like:\n```json\n[\n { \"type\": \"function\", \"name\": \"get_weather\" },\n { \"type\": \"mcp\", \"server_label\": \"deepwiki\" },\n { \"type\": \"image_generation\" }\n]\n```\n" + }) +}).annotate({ + "title": "Allowed tools", + "description": "Constrains the tools available to the model to a pre-defined set.\n", + "identifier": "ToolChoiceAllowed" +}) +export type ToolChoiceTypes = { + readonly "type": + | "file_search" + | "web_search_preview" + | "computer" + | "computer_use_preview" + | "computer_use" + | "web_search_preview_2025_03_11" + | "image_generation" + | "code_interpreter" +} +export const ToolChoiceTypes = Schema.Struct({ + "type": Schema.Literals([ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter" + ]).annotate({ + "description": + "The type of hosted tool the model should to use. Learn more about\n[built-in tools](https://developers.openai.com/api/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer`\n- `computer_use_preview`\n- `computer_use`\n- `code_interpreter`\n- `image_generation`\n" + }) +}).annotate({ + "title": "Hosted tool", + "description": + "Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).\n", + "identifier": "ToolChoiceTypes" +}) +export type ToolChoiceCustom = { readonly "type": "custom"; readonly "name": string } +export const ToolChoiceCustom = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "For custom tool calling, the type is always `custom`." }), + "name": Schema.String.annotate({ "description": "The name of the custom tool to call." }) +}).annotate({ + "title": "Custom tool", + "description": "Use this option to force the model to call a specific custom tool.\n", + "identifier": "ToolChoiceCustom" +}) +export type SpecificProgrammaticToolCallingParam = { readonly "type": "programmatic_tool_calling" } +export const SpecificProgrammaticToolCallingParam = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The tool to call. Always `programmatic_tool_calling`." + }) +}).annotate({ "identifier": "SpecificProgrammaticToolCallingParam" }) +export type SpecificApplyPatchParam = { readonly "type": "apply_patch" } +export const SpecificApplyPatchParam = Schema.Struct({ + "type": Schema.Literal("apply_patch").annotate({ "description": "The tool to call. Always `apply_patch`." }) +}).annotate({ + "title": "Specific apply patch tool choice", + "description": "Forces the model to call the apply_patch tool when executing a tool call.", + "identifier": "SpecificApplyPatchParam" +}) +export type SpecificFunctionShellParam = { readonly "type": "shell" } +export const SpecificFunctionShellParam = Schema.Struct({ + "type": Schema.Literal("shell").annotate({ "description": "The tool to call. Always `shell`." }) +}).annotate({ + "title": "Specific shell tool choice", + "description": "Forces the model to call the shell tool when a tool call is required.", + "identifier": "SpecificFunctionShellParam" +}) +export type CyberAccessProgramEnum = "standard" | "daybreak_blue" | "daybreak_red" +export const CyberAccessProgramEnum = Schema.Literals(["standard", "daybreak_blue", "daybreak_red"]).annotate({ + "identifier": "CyberAccessProgramEnum" +}) +export type ServiceTierResponses = "auto" | "default" | "flex" | "scale" | "priority" | "fast" | "ultrafast" | null +export const ServiceTierResponses = Schema.Union([ + Schema.Literals(["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]).annotate({ + "description": + "Specifies the processing type used for serving the request.\n - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.\n - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.\n - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.\n - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.\n - If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.\n - When not set, the default behavior is 'auto'.\n\n When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.\n" + }), + Schema.Null +]).annotate({ "identifier": "ServiceTierResponses" }) +export type ReasoningModeEnum = string | "standard" | "pro" +export const ReasoningModeEnum = Schema.Union([Schema.String, Schema.Literals(["standard", "pro"])]).annotate({ + "identifier": "ReasoningModeEnum" +}) +export type ResponseStreamOptions = { readonly "include_obfuscation"?: boolean } | null +export const ResponseStreamOptions = Schema.Union([ + Schema.Struct({ + "include_obfuscation": Schema.optionalKey(Schema.Boolean.annotate({ + "description": + "When true, stream obfuscation will be enabled. Stream obfuscation adds\nrandom characters to an `obfuscation` field on streaming delta events to\nnormalize payload sizes as a mitigation to certain side-channel attacks.\nThese obfuscation fields are included by default, but add a small amount\nof overhead to the data stream. You can set `include_obfuscation` to\nfalse to optimize for bandwidth if you trust the network links between\nyour application and the OpenAI API.\n" + })) + }).annotate({ "description": "Options for streaming responses. Only set this when you set `stream: true`.\n" }), + Schema.Null +]).annotate({ "identifier": "ResponseStreamOptions" }) +export type ConversationParam_2 = { readonly "id": string } +export const ConversationParam_2 = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the conversation." }) +}).annotate({ + "title": "Conversation object", + "description": "The conversation that this response belongs to.", + "identifier": "ConversationParam-2" +}) +export type ContextManagementParam = { readonly "type": string; readonly "compact_threshold"?: number | null } +export const ContextManagementParam = Schema.Struct({ + "type": Schema.String.annotate({ + "description": "The context management entry type. Currently only 'compaction' is supported." + }), + "compact_threshold": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Token threshold at which compaction should be triggered for this entry." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1000).annotate({ "expected": "a value greater than or equal to 1000" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "ContextManagementParam" }) +export type ResponseErrorCode = + | "server_error" + | "rate_limit_exceeded" + | "invalid_prompt" + | "data_residency_mismatch" + | "bio_policy" + | "misalignment_policy_violation" + | "vector_store_timeout" + | "invalid_image" + | "invalid_image_format" + | "invalid_base64_image" + | "invalid_image_url" + | "image_too_large" + | "image_too_small" + | "image_parse_error" + | "image_content_policy_violation" + | "invalid_image_mode" + | "image_file_too_large" + | "unsupported_image_media_type" + | "empty_image_file" + | "failed_to_download_image" + | "image_file_not_found" +export const ResponseErrorCode = Schema.Literals([ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "data_residency_mismatch", + "bio_policy", + "misalignment_policy_violation", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found" +]).annotate({ "description": "The error code for the response.\n", "identifier": "ResponseErrorCode" }) +export type ResponseUsage = { + readonly "input_tokens": number + readonly "input_tokens_details": { readonly "cached_tokens": number; readonly "cache_write_tokens"?: number } + readonly "output_tokens": number + readonly "output_tokens_details": { readonly "reasoning_tokens": number } + readonly "total_tokens": number +} +export const ResponseUsage = Schema.Struct({ + "input_tokens": Schema.Number.annotate({ "description": "The number of input tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "input_tokens_details": Schema.Struct({ + "cached_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that were retrieved from the cache.\n[More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "cache_write_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The number of input tokens that were written to the cache." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ) + }).annotate({ "description": "A detailed breakdown of the input tokens." }), + "output_tokens": Schema.Number.annotate({ "description": "The number of output tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_tokens_details": Schema.Struct({ + "reasoning_tokens": Schema.Number.annotate({ "description": "The number of reasoning tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "A detailed breakdown of the output tokens." }), + "total_tokens": Schema.Number.annotate({ "description": "The total number of tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": + "Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n", + "identifier": "ResponseUsage" +}) +export type CacheMissReasonTypeEnum = + | "model_changed" + | "prompt_cache_key_changed" + | "tools_changed" + | "text_format_changed" + | "reasoning_effort_changed" + | "verbosity_changed" + | "context_compacted" + | "input_changed" + | "service_tier_changed" +export const CacheMissReasonTypeEnum = Schema.Literals([ + "model_changed", + "prompt_cache_key_changed", + "tools_changed", + "text_format_changed", + "reasoning_effort_changed", + "verbosity_changed", + "context_compacted", + "input_changed", + "service_tier_changed" +]).annotate({ "identifier": "CacheMissReasonTypeEnum" }) +export type PromptCacheHitDiagnosticsBody = { readonly "type": "cache_hit" } +export const PromptCacheHitDiagnosticsBody = Schema.Struct({ "type": Schema.Literal("cache_hit") }).annotate({ + "identifier": "PromptCacheHitDiagnosticsBody" +}) +export type PromptCacheComparisonResponseNotFoundDiagnosticsBody = { readonly "type": "comparison_response_not_found" } +export const PromptCacheComparisonResponseNotFoundDiagnosticsBody = Schema.Struct({ + "type": Schema.Literal("comparison_response_not_found") +}).annotate({ "identifier": "PromptCacheComparisonResponseNotFoundDiagnosticsBody" }) +export type PromptCacheUnavailableDiagnosticsBody = { readonly "type": "unavailable" } +export const PromptCacheUnavailableDiagnosticsBody = Schema.Struct({ "type": Schema.Literal("unavailable") }).annotate({ + "identifier": "PromptCacheUnavailableDiagnosticsBody" +}) +export type ModerationErrorBody = { readonly "type": "error"; readonly "code": string; readonly "message": string } +export const ModerationErrorBody = Schema.Struct({ + "type": Schema.Literal("error").annotate({ + "description": "The object type, which was always `error` for moderation failures." + }), + "code": Schema.String.annotate({ "description": "The error code." }), + "message": Schema.String.annotate({ "description": "The error message." }) +}).annotate({ + "title": "Moderation error", + "description": "An error produced while attempting moderation for the response input or output.", + "identifier": "ModerationErrorBody" +}) +export type ResponseConversation = { readonly "id": string } +export const ResponseConversation = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The unique ID of the conversation that this response was associated with." + }) +}).annotate({ + "title": "Conversation", + "description": + "The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.", + "identifier": "ResponseConversation" +}) +export type ResponseAudioDeltaEvent = { + readonly "type": "response.audio.delta" + readonly "sequence_number": number + readonly "delta": string +} +export const ResponseAudioDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.audio.delta").annotate({ + "description": "The type of the event. Always `response.audio.delta`.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "A sequence number for this chunk of the stream response.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "A chunk of Base64 encoded response audio bytes.\n" }) +}).annotate({ + "description": "Emitted when there is a partial audio response.", + "identifier": "ResponseAudioDeltaEvent" +}) +export type ResponseAudioDoneEvent = { + readonly "type": "response.audio.done" + readonly "sequence_number": number + readonly "response_id": Schema.Json +} +export const ResponseAudioDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.audio.done").annotate({ + "description": "The type of the event. Always `response.audio.done`.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the delta.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ "description": "Emitted when the audio response is complete.", "identifier": "ResponseAudioDoneEvent" }) +export type ResponseAudioTranscriptDeltaEvent = { + readonly "type": "response.audio.transcript.delta" + readonly "delta": string + readonly "sequence_number": number + readonly "response_id": Schema.Json +} +export const ResponseAudioTranscriptDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.audio.transcript.delta").annotate({ + "description": "The type of the event. Always `response.audio.transcript.delta`.\n" + }), + "delta": Schema.String.annotate({ "description": "The partial transcript of the audio response.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "description": "Emitted when there is a partial transcript of audio.", + "identifier": "ResponseAudioTranscriptDeltaEvent" +}) +export type ResponseAudioTranscriptDoneEvent = { + readonly "type": "response.audio.transcript.done" + readonly "sequence_number": number + readonly "response_id": Schema.Json +} +export const ResponseAudioTranscriptDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.audio.transcript.done").annotate({ + "description": "The type of the event. Always `response.audio.transcript.done`.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "description": "Emitted when the full audio transcript is completed.", + "identifier": "ResponseAudioTranscriptDoneEvent" +}) +export type ResponseCodeInterpreterCallCodeDeltaEvent = { + readonly "type": "response.code_interpreter_call_code.delta" + readonly "output_index": number + readonly "item_id": string + readonly "delta": string + readonly "sequence_number": number +} +export const ResponseCodeInterpreterCallCodeDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.code_interpreter_call_code.delta").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call_code.delta`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code is being streamed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "delta": Schema.String.annotate({ + "description": "The partial code snippet being streamed by the code interpreter." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a partial code snippet is streamed by the code interpreter.", + "identifier": "ResponseCodeInterpreterCallCodeDeltaEvent" +}) +export type ResponseCodeInterpreterCallCodeDoneEvent = { + readonly "type": "response.code_interpreter_call_code.done" + readonly "output_index": number + readonly "item_id": string + readonly "code": string + readonly "sequence_number": number +} +export const ResponseCodeInterpreterCallCodeDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.code_interpreter_call_code.done").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call_code.done`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code is finalized." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "code": Schema.String.annotate({ "description": "The final code snippet output by the code interpreter." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when the code snippet is finalized by the code interpreter.", + "identifier": "ResponseCodeInterpreterCallCodeDoneEvent" +}) +export type ResponseCodeInterpreterCallCompletedEvent = { + readonly "type": "response.code_interpreter_call.completed" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseCodeInterpreterCallCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.code_interpreter_call.completed").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call.completed`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code interpreter call is completed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when the code interpreter call is completed.", + "identifier": "ResponseCodeInterpreterCallCompletedEvent" +}) +export type ResponseCodeInterpreterCallInProgressEvent = { + readonly "type": "response.code_interpreter_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseCodeInterpreterCallInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.code_interpreter_call.in_progress").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call.in_progress`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code interpreter call is in progress." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a code interpreter call is in progress.", + "identifier": "ResponseCodeInterpreterCallInProgressEvent" +}) +export type ResponseCodeInterpreterCallInterpretingEvent = { + readonly "type": "response.code_interpreter_call.interpreting" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseCodeInterpreterCallInterpretingEvent = Schema.Struct({ + "type": Schema.Literal("response.code_interpreter_call.interpreting").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call.interpreting`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code interpreter is interpreting code." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when the code interpreter is actively interpreting the code snippet.", + "identifier": "ResponseCodeInterpreterCallInterpretingEvent" +}) +export type ResponseCompactionCompactingStreamingEvent = { + readonly "type": "response.compaction.compacting" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string +} +export const ResponseCompactionCompactingStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.compaction.compacting").annotate({ + "description": "The type of the event, always `response.compaction.compacting`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_index": Schema.Number.annotate({ "description": "The index of the compaction output item." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ "description": "The ID of the compaction output item." }) +}).annotate({ + "title": "Response compaction compacting event", + "description": "Emitted when new summary content is sampled for a compaction trigger. Contains no summary content.", + "identifier": "ResponseCompactionCompactingStreamingEvent" +}) +export type ResponseErrorEvent = { + readonly "type": "error" + readonly "code": string | null + readonly "message": string + readonly "param": string | null + readonly "sequence_number": number +} +export const ResponseErrorEvent = Schema.Struct({ + "type": Schema.Literal("error").annotate({ "description": "The type of the event. Always `error`.\n" }), + "code": Schema.Union([Schema.String.annotate({ "description": "The error code.\n" }), Schema.Null]), + "message": Schema.String.annotate({ "description": "The error message.\n" }), + "param": Schema.Union([Schema.String.annotate({ "description": "The error parameter.\n" }), Schema.Null]), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when an error occurs.", "identifier": "ResponseErrorEvent" }) +export type ResponseFileSearchCallCompletedEvent = { + readonly "type": "response.file_search_call.completed" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseFileSearchCallCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.file_search_call.completed").annotate({ + "description": "The type of the event. Always `response.file_search_call.completed`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the file search call is initiated.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the file search call is initiated.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a file search call is completed (results found).", + "identifier": "ResponseFileSearchCallCompletedEvent" +}) +export type ResponseFileSearchCallInProgressEvent = { + readonly "type": "response.file_search_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseFileSearchCallInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.file_search_call.in_progress").annotate({ + "description": "The type of the event. Always `response.file_search_call.in_progress`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the file search call is initiated.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the file search call is initiated.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a file search call is initiated.", + "identifier": "ResponseFileSearchCallInProgressEvent" +}) +export type ResponseFileSearchCallSearchingEvent = { + readonly "type": "response.file_search_call.searching" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseFileSearchCallSearchingEvent = Schema.Struct({ + "type": Schema.Literal("response.file_search_call.searching").annotate({ + "description": "The type of the event. Always `response.file_search_call.searching`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the file search call is searching.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the file search call is initiated.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a file search is currently searching.", + "identifier": "ResponseFileSearchCallSearchingEvent" +}) +export type ResponseFunctionCallArgumentsDeltaEvent = { + readonly "type": "response.function_call_arguments.delta" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number + readonly "delta": string +} +export const ResponseFunctionCallArgumentsDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.function_call_arguments.delta").annotate({ + "description": "The type of the event. Always `response.function_call_arguments.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the function-call arguments delta is added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the function-call arguments delta is added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "delta": Schema.String.annotate({ "description": "The function-call arguments delta that is added.\n" }) +}).annotate({ + "description": "Emitted when there is a partial function-call arguments delta.", + "identifier": "ResponseFunctionCallArgumentsDeltaEvent" +}) +export type ResponseFunctionCallArgumentsDoneEvent = { + readonly "type": "response.function_call_arguments.done" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number + readonly "arguments": string + readonly "name"?: string +} +export const ResponseFunctionCallArgumentsDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.function_call_arguments.done"), + "item_id": Schema.String.annotate({ "description": "The ID of the item." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "arguments": Schema.String.annotate({ "description": "The function-call arguments." }), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the function that was called." })) +}).annotate({ + "description": "Emitted when function-call arguments are finalized.", + "identifier": "ResponseFunctionCallArgumentsDoneEvent" +}) +export type ResponseShellCallCommandAddedStreamingEvent = { + readonly "type": "response.shell_call_command.added" + readonly "sequence_number": number + readonly "output_index": number + readonly "command_index": number + readonly "command": string +} +export const ResponseShellCallCommandAddedStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_command.added").annotate({ + "description": "The type of the event, always `response.shell_call_command.added`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that was added." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command": Schema.String.annotate({ "description": "The shell command that was added." }) +}).annotate({ + "title": "Response shell command added event", + "description": "A streaming event that indicated a shell command was added to a tool call.", + "identifier": "ResponseShellCallCommandAddedStreamingEvent" +}) +export type ResponseShellCallCommandDeltaStreamingEvent = { + readonly "type": "response.shell_call_command.delta" + readonly "sequence_number": number + readonly "output_index": number + readonly "command_index": number + readonly "delta": string + readonly "obfuscation"?: string +} +export const ResponseShellCallCommandDeltaStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_command.delta").annotate({ + "description": "The type of the event, always `response.shell_call_command.delta`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "delta": Schema.String.annotate({ "description": "The shell command delta that was appended." }), + "obfuscation": Schema.optionalKey( + Schema.String.annotate({ "description": "An obfuscation string that was added to pad the event payload." }) + ) +}).annotate({ + "title": "Response shell command delta event", + "description": "A streaming event that indicated a shell command was incrementally updated.", + "identifier": "ResponseShellCallCommandDeltaStreamingEvent" +}) +export type ResponseShellCallCommandDoneStreamingEvent = { + readonly "type": "response.shell_call_command.done" + readonly "sequence_number": number + readonly "output_index": number + readonly "command_index": number + readonly "command": string +} +export const ResponseShellCallCommandDoneStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_command.done").annotate({ + "description": "The type of the event, always `response.shell_call_command.done`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that was completed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "command": Schema.String.annotate({ "description": "The final shell command that was emitted." }) +}).annotate({ + "title": "Response shell command done event", + "description": "A streaming event that indicated a shell command was completed.", + "identifier": "ResponseShellCallCommandDoneStreamingEvent" +}) +export type ShellCallOutputDelta = { readonly "stdout"?: string; readonly "stderr"?: string } +export const ShellCallOutputDelta = Schema.Struct({ + "stdout": Schema.optionalKey(Schema.String.annotate({ "description": "The stdout delta that was emitted." })), + "stderr": Schema.optionalKey(Schema.String.annotate({ "description": "The stderr delta that was emitted." })) +}).annotate({ + "title": "Shell call output delta", + "description": "A delta of stdout/stderr emitted while a shell call was running.", + "identifier": "ShellCallOutputDelta" +}) +export type ResponseReasoningSummaryPartAddedEvent = { + readonly "type": "response.reasoning_summary_part.added" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "sequence_number": number + readonly "part": { readonly "type": "summary_text"; readonly "text": string } +} +export const ResponseReasoningSummaryPartAddedEvent = Schema.Struct({ + "type": Schema.Literal("response.reasoning_summary_part.added").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_part.added`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this summary part is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary part is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ + "description": "The type of the summary part. Always `summary_text`." + }), + "text": Schema.String.annotate({ "description": "The text of the summary part." }) + }).annotate({ "description": "The summary part that was added.\n" }) +}).annotate({ + "description": "Emitted when a new reasoning summary part is added.", + "identifier": "ResponseReasoningSummaryPartAddedEvent" +}) +export type ResponseReasoningSummaryPartDoneEvent = { + readonly "type": "response.reasoning_summary_part.done" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "status"?: "incomplete" + readonly "sequence_number": number + readonly "part": { readonly "type": "summary_text"; readonly "text": string } +} +export const ResponseReasoningSummaryPartDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.reasoning_summary_part.done").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_part.done`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this summary part is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary part is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "status": Schema.optionalKey( + Schema.Literal("incomplete").annotate({ + "description": + "The completion status of the summary part. Omitted when the part completed\nnormally and set to `incomplete` when generation was interrupted.\n" + }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ + "description": "The type of the summary part. Always `summary_text`." + }), + "text": Schema.String.annotate({ "description": "The text of the summary part." }) + }).annotate({ "description": "The completed summary part.\n" }) +}).annotate({ + "description": "Emitted when a reasoning summary part is completed.", + "identifier": "ResponseReasoningSummaryPartDoneEvent" +}) +export type ResponseReasoningSummaryTextDeltaEvent = { + readonly "type": "response.reasoning_summary_text.delta" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "delta": string + readonly "sequence_number": number +} +export const ResponseReasoningSummaryTextDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.reasoning_summary_text.delta").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_text.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the item this summary text delta is associated with.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary text delta is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The text delta that was added to the summary.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a delta is added to a reasoning summary text.", + "identifier": "ResponseReasoningSummaryTextDeltaEvent" +}) +export type ResponseReasoningSummaryTextDoneEvent = { + readonly "type": "response.reasoning_summary_text.done" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "text": string + readonly "sequence_number": number +} +export const ResponseReasoningSummaryTextDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.reasoning_summary_text.done").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_text.done`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this summary text is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary text is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "text": Schema.String.annotate({ "description": "The full text of the completed reasoning summary.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a reasoning summary text is completed.", + "identifier": "ResponseReasoningSummaryTextDoneEvent" +}) +export type ResponseReasoningTextDeltaEvent = { + readonly "type": "response.reasoning_text.delta" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string + readonly "sequence_number": number +} +export const ResponseReasoningTextDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.reasoning_text.delta").annotate({ + "description": "The type of the event. Always `response.reasoning_text.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the item this reasoning text delta is associated with.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this reasoning text delta is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the reasoning content part this delta is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The text delta that was added to the reasoning content.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a delta is added to a reasoning text.", + "identifier": "ResponseReasoningTextDeltaEvent" +}) +export type ResponseReasoningTextDoneEvent = { + readonly "type": "response.reasoning_text.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "text": string + readonly "sequence_number": number +} +export const ResponseReasoningTextDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.reasoning_text.done").annotate({ + "description": "The type of the event. Always `response.reasoning_text.done`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this reasoning text is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this reasoning text is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the reasoning content part.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "text": Schema.String.annotate({ "description": "The full text of the completed reasoning content.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a reasoning text is completed.", + "identifier": "ResponseReasoningTextDoneEvent" +}) +export type ResponseRefusalDeltaEvent = { + readonly "type": "response.refusal.delta" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string + readonly "sequence_number": number +} +export const ResponseRefusalDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.refusal.delta").annotate({ + "description": "The type of the event. Always `response.refusal.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the refusal text is added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the refusal text is added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the refusal text is added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The refusal text that is added.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when there is a partial refusal text.", + "identifier": "ResponseRefusalDeltaEvent" +}) +export type ResponseRefusalDoneEvent = { + readonly "type": "response.refusal.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "refusal": string + readonly "sequence_number": number +} +export const ResponseRefusalDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.refusal.done").annotate({ + "description": "The type of the event. Always `response.refusal.done`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the refusal text is finalized.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the refusal text is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the refusal text is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "refusal": Schema.String.annotate({ "description": "The refusal text that is finalized.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when refusal text is finalized.", "identifier": "ResponseRefusalDoneEvent" }) +export type ResponseLogProb = { + readonly "token": string + readonly "logprob": number + readonly "top_logprobs"?: ReadonlyArray<{ readonly "token"?: string; readonly "logprob"?: number }> +} +export const ResponseLogProb = Schema.Struct({ + "token": Schema.String.annotate({ "description": "A possible text token." }), + "logprob": Schema.Number.annotate({ "description": "The log probability of this token.\n" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "top_logprobs": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "token": Schema.optionalKey(Schema.String.annotate({ "description": "A possible text token." })), + "logprob": Schema.optionalKey( + Schema.Number.annotate({ "description": "The log probability of this token." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ) + })).annotate({ "description": "The log probabilities of up to 20 of the most likely tokens.\n" }) + ) +}).annotate({ + "description": + "A logprob is the logarithmic probability that the model assigns to producing \na particular token at a given position in the sequence. Less-negative (higher) \nlogprob values indicate greater model confidence in that token choice.\n", + "identifier": "ResponseLogProb" +}) +export type ResponseWebSearchCallCompletedEvent = { + readonly "type": "response.web_search_call.completed" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseWebSearchCallCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.web_search_call.completed").annotate({ + "description": "The type of the event. Always `response.web_search_call.completed`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the web search call is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "Unique ID for the output item associated with the web search call.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the web search call being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a web search call is completed.", + "identifier": "ResponseWebSearchCallCompletedEvent" +}) +export type ResponseWebSearchCallInProgressEvent = { + readonly "type": "response.web_search_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseWebSearchCallInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.web_search_call.in_progress").annotate({ + "description": "The type of the event. Always `response.web_search_call.in_progress`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the web search call is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "Unique ID for the output item associated with the web search call.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the web search call being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a web search call is initiated.", + "identifier": "ResponseWebSearchCallInProgressEvent" +}) +export type ResponseWebSearchCallSearchingEvent = { + readonly "type": "response.web_search_call.searching" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseWebSearchCallSearchingEvent = Schema.Struct({ + "type": Schema.Literal("response.web_search_call.searching").annotate({ + "description": "The type of the event. Always `response.web_search_call.searching`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the web search call is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "Unique ID for the output item associated with the web search call.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the web search call being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a web search call is executing.", + "identifier": "ResponseWebSearchCallSearchingEvent" +}) +export type ResponseImageGenCallCompletedEvent = { + readonly "type": "response.image_generation_call.completed" + readonly "output_index": number + readonly "sequence_number": number + readonly "item_id": string +} +export const ResponseImageGenCallCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.image_generation_call.completed").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.completed'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }) +}).annotate({ + "title": "ResponseImageGenCallCompletedEvent", + "description": "Emitted when an image generation tool call has completed and the final image is available.\n", + "identifier": "ResponseImageGenCallCompletedEvent" +}) +export type ResponseImageGenCallGeneratingEvent = { + readonly "type": "response.image_generation_call.generating" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseImageGenCallGeneratingEvent = Schema.Struct({ + "type": Schema.Literal("response.image_generation_call.generating").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.generating'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the image generation item being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "ResponseImageGenCallGeneratingEvent", + "description": "Emitted when an image generation tool call is actively generating an image (intermediate state).\n", + "identifier": "ResponseImageGenCallGeneratingEvent" +}) +export type ResponseImageGenCallInProgressEvent = { + readonly "type": "response.image_generation_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const ResponseImageGenCallInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.image_generation_call.in_progress").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.in_progress'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the image generation item being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "ResponseImageGenCallInProgressEvent", + "description": "Emitted when an image generation tool call is in progress.\n", + "identifier": "ResponseImageGenCallInProgressEvent" +}) +export type ResponseImageGenCallPartialImageEvent = { + readonly "type": "response.image_generation_call.partial_image" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number + readonly "partial_image_index": number + readonly "partial_image_b64": string + readonly "size"?: string + readonly "quality"?: string + readonly "background"?: string + readonly "output_format"?: string +} +export const ResponseImageGenCallPartialImageEvent = Schema.Struct({ + "type": Schema.Literal("response.image_generation_call.partial_image").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.partial_image'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the image generation item being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "partial_image_index": Schema.Number.annotate({ + "description": "0-based index for the partial image (backend is 1-based, but this is 0-based for the user)." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "partial_image_b64": Schema.String.annotate({ + "description": "Base64-encoded partial image data, suitable for rendering as an image." + }), + "size": Schema.optionalKey(Schema.String.annotate({ "description": "The image size that was used." })), + "quality": Schema.optionalKey(Schema.String.annotate({ "description": "The image quality that was used." })), + "background": Schema.optionalKey(Schema.String.annotate({ "description": "The background setting that was used." })), + "output_format": Schema.optionalKey(Schema.String.annotate({ "description": "The output format that was used." })) +}).annotate({ + "title": "ResponseImageGenCallPartialImageEvent", + "description": "Emitted when a partial image is available during image generation streaming.\n", + "identifier": "ResponseImageGenCallPartialImageEvent" +}) +export type ResponseMCPCallArgumentsDeltaEvent = { + readonly "type": "response.mcp_call_arguments.delta" + readonly "output_index": number + readonly "item_id": string + readonly "delta": string + readonly "sequence_number": number +} +export const ResponseMCPCallArgumentsDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_call_arguments.delta").annotate({ + "description": "The type of the event. Always 'response.mcp_call_arguments.delta'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the MCP tool call item being processed." + }), + "delta": Schema.String.annotate({ + "description": "A JSON string containing the partial update to the arguments for the MCP tool call.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallArgumentsDeltaEvent", + "description": "Emitted when there is a delta (partial update) to the arguments of an MCP tool call.\n", + "identifier": "ResponseMCPCallArgumentsDeltaEvent" +}) +export type ResponseMCPCallArgumentsDoneEvent = { + readonly "type": "response.mcp_call_arguments.done" + readonly "output_index": number + readonly "item_id": string + readonly "arguments": string + readonly "sequence_number": number +} +export const ResponseMCPCallArgumentsDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_call_arguments.done").annotate({ + "description": "The type of the event. Always 'response.mcp_call_arguments.done'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the MCP tool call item being processed." + }), + "arguments": Schema.String.annotate({ + "description": "A JSON string containing the finalized arguments for the MCP tool call.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallArgumentsDoneEvent", + "description": "Emitted when the arguments for an MCP tool call are finalized.\n", + "identifier": "ResponseMCPCallArgumentsDoneEvent" +}) +export type ResponseMCPCallCompletedEvent = { + readonly "type": "response.mcp_call.completed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const ResponseMCPCallCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_call.completed").annotate({ + "description": "The type of the event. Always 'response.mcp_call.completed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that completed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that completed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallCompletedEvent", + "description": "Emitted when an MCP tool call has completed successfully.\n", + "identifier": "ResponseMCPCallCompletedEvent" +}) +export type ResponseMCPCallFailedEvent = { + readonly "type": "response.mcp_call.failed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const ResponseMCPCallFailedEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_call.failed").annotate({ + "description": "The type of the event. Always 'response.mcp_call.failed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that failed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that failed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallFailedEvent", + "description": "Emitted when an MCP tool call has failed.\n", + "identifier": "ResponseMCPCallFailedEvent" +}) +export type ResponseMCPCallInProgressEvent = { + readonly "type": "response.mcp_call.in_progress" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string +} +export const ResponseMCPCallInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_call.in_progress").annotate({ + "description": "The type of the event. Always 'response.mcp_call.in_progress'." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the MCP tool call item being processed." + }) +}).annotate({ + "title": "ResponseMCPCallInProgressEvent", + "description": "Emitted when an MCP tool call is in progress.\n", + "identifier": "ResponseMCPCallInProgressEvent" +}) +export type ResponseMCPListToolsCompletedEvent = { + readonly "type": "response.mcp_list_tools.completed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const ResponseMCPListToolsCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_list_tools.completed").annotate({ + "description": "The type of the event. Always 'response.mcp_list_tools.completed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that produced this output." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was processed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPListToolsCompletedEvent", + "description": "Emitted when the list of available MCP tools has been successfully retrieved.\n", + "identifier": "ResponseMCPListToolsCompletedEvent" +}) +export type ResponseMCPListToolsFailedEvent = { + readonly "type": "response.mcp_list_tools.failed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const ResponseMCPListToolsFailedEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_list_tools.failed").annotate({ + "description": "The type of the event. Always 'response.mcp_list_tools.failed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that failed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that failed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPListToolsFailedEvent", + "description": "Emitted when the attempt to list available MCP tools has failed.\n", + "identifier": "ResponseMCPListToolsFailedEvent" +}) +export type ResponseMCPListToolsInProgressEvent = { + readonly "type": "response.mcp_list_tools.in_progress" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const ResponseMCPListToolsInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.mcp_list_tools.in_progress").annotate({ + "description": "The type of the event. Always 'response.mcp_list_tools.in_progress'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that is being processed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that is being processed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPListToolsInProgressEvent", + "description": "Emitted when the system is in the process of retrieving the list of available MCP tools.\n", + "identifier": "ResponseMCPListToolsInProgressEvent" +}) +export type ResponseCustomToolCallInputDeltaEvent = { + readonly "type": "response.custom_tool_call_input.delta" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string + readonly "delta": string +} +export const ResponseCustomToolCallInputDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.custom_tool_call_input.delta").annotate({ + "description": "The event type identifier." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output this delta applies to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "Unique identifier for the API item associated with this event." + }), + "delta": Schema.String.annotate({ "description": "The incremental input data (delta) for the custom tool call." }) +}).annotate({ + "title": "ResponseCustomToolCallInputDelta", + "description": "Event representing a delta (partial update) to the input of a custom tool call.\n", + "identifier": "ResponseCustomToolCallInputDeltaEvent" +}) +export type ResponseCustomToolCallInputDoneEvent = { + readonly "type": "response.custom_tool_call_input.done" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string + readonly "input": string +} +export const ResponseCustomToolCallInputDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.custom_tool_call_input.done").annotate({ + "description": "The event type identifier." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output this event applies to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "Unique identifier for the API item associated with this event." + }), + "input": Schema.String.annotate({ "description": "The complete input data for the custom tool call." }) +}).annotate({ + "title": "ResponseCustomToolCallInputDone", + "description": "Event indicating that input for a custom tool call is complete.\n", + "identifier": "ResponseCustomToolCallInputDoneEvent" +}) +export type ResponseKeepAliveEvent = { readonly "type": "keepalive"; readonly "sequence_number": number } +export const ResponseKeepAliveEvent = Schema.Struct({ + "type": Schema.Literal("keepalive").annotate({ + "description": "The type of the keepalive event. Always `keepalive`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this keepalive event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Keep alive", + "description": "A keepalive event emitted during long-running response streams.", + "identifier": "ResponseKeepAliveEvent" +}) +export type ResponseApplyPatchCallOperationDiffDeltaEvent = { + readonly "type": "response.apply_patch_call_operation_diff.delta" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string + readonly "delta": string +} +export const ResponseApplyPatchCallOperationDiffDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.apply_patch_call_operation_diff.delta").annotate({ + "description": "The event type identifier." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output this delta applies to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "Unique identifier for the API item associated with this event." + }), + "delta": Schema.String.annotate({ "description": "The incremental diff data for the apply_patch tool call." }) +}).annotate({ + "title": "ResponseApplyPatchCallOperationDiffDelta", + "description": "Event representing a delta for an apply_patch tool call operation diff.", + "identifier": "ResponseApplyPatchCallOperationDiffDeltaEvent" +}) +export type ResponseApplyPatchCallOperationDiffDoneEvent = { + readonly "type": "response.apply_patch_call_operation_diff.done" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string + readonly "delta"?: string +} +export const ResponseApplyPatchCallOperationDiffDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.apply_patch_call_operation_diff.done").annotate({ + "description": "The event type identifier." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output this event applies to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "Unique identifier for the API item associated with this event." + }), + "delta": Schema.optionalKey( + Schema.String.annotate({ "description": "The final diff data for the apply_patch tool call." }) + ) +}).annotate({ + "title": "ResponseApplyPatchCallOperationDiffDone", + "description": "Event indicating that the operation diff for an apply_patch tool call is complete.", + "identifier": "ResponseApplyPatchCallOperationDiffDoneEvent" +}) +export type MessageContentImageFileObject = { + readonly "type": "image_file" + readonly "image_file": { readonly "file_id": string; readonly "detail"?: "auto" | "low" | "high" } +} +export const MessageContentImageFileObject = Schema.Struct({ + "type": Schema.Literal("image_file").annotate({ "description": "Always `image_file`." }), + "image_file": Schema.Struct({ + "file_id": Schema.String.annotate({ + "description": + "The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose=\"vision\"` when uploading the File if you need to later display the file content." + }), + "detail": Schema.optionalKey( + Schema.Literals(["auto", "low", "high"]).annotate({ + "description": + "Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`." + }) + ) + }) +}).annotate({ + "title": "Image file", + "description": + "References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.", + "identifier": "MessageContentImageFileObject" +}) +export type MessageContentImageUrlObject = { + readonly "type": "image_url" + readonly "image_url": { readonly "url": string; readonly "detail"?: "auto" | "low" | "high" } +} +export const MessageContentImageUrlObject = Schema.Struct({ + "type": Schema.Literal("image_url").annotate({ "description": "The type of the content part." }), + "image_url": Schema.Struct({ + "url": Schema.String.annotate({ + "description": "The external URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.", + "format": "uri" + }), + "detail": Schema.optionalKey( + Schema.Literals(["auto", "low", "high"]).annotate({ + "description": + "Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high resolution using `high`. Default value is `auto`" + }) + ) + }) +}).annotate({ + "title": "Image URL", + "description": "References an image URL in the content of a message.", + "identifier": "MessageContentImageUrlObject" +}) +export type MessageRequestContentTextObject = { readonly "type": "text"; readonly "text": string } +export const MessageRequestContentTextObject = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "Always `text`." }), + "text": Schema.String.annotate({ "description": "Text content to be sent to the model" }) +}).annotate({ + "title": "Text", + "description": "The text content that is part of a message.", + "identifier": "MessageRequestContentTextObject" +}) +export type AssistantToolsFileSearchTypeOnly = { readonly "type": "file_search" } +export const AssistantToolsFileSearchTypeOnly = Schema.Struct({ + "type": Schema.Literal("file_search").annotate({ "description": "The type of tool being defined: `file_search`" }) +}).annotate({ "title": "FileSearch tool", "identifier": "AssistantToolsFileSearchTypeOnly" }) +export type TruncationObject = { readonly "type": "auto" | "last_messages"; readonly "last_messages"?: number | null } +export const TruncationObject = Schema.Struct({ + "type": Schema.Literals(["auto", "last_messages"]).annotate({ + "description": + "The truncation strategy to use for the thread. The default is `auto`. If set to `last_messages`, the thread will be truncated to the n most recent messages in the thread. When set to `auto`, messages in the middle of the thread will be dropped to fit the context length of the model, `max_prompt_tokens`." + }), + "last_messages": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The number of most recent messages from the thread when constructing the context for the run." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Thread Truncation Controls", + "description": + "Controls for how a thread will be truncated prior to the run. Use this to control the initial context window of the run.", + "identifier": "TruncationObject" +}) +export type AssistantsNamedToolChoice = { + readonly "type": "function" | "code_interpreter" | "file_search" + readonly "function"?: { readonly "name": string } +} +export const AssistantsNamedToolChoice = Schema.Struct({ + "type": Schema.Literals(["function", "code_interpreter", "file_search"]).annotate({ + "description": "The type of the tool. If type is `function`, the function name must be set" + }), + "function": Schema.optionalKey( + Schema.Struct({ "name": Schema.String.annotate({ "description": "The name of the function to call." }) }) + ) +}).annotate({ + "description": "Specifies a tool the model should use. Use to force the model to call a specific tool.", + "identifier": "AssistantsNamedToolChoice" +}) +export type RunToolCallObject = { + readonly "id": string + readonly "type": "function" + readonly "function": { readonly "name": string; readonly "arguments": string } +} +export const RunToolCallObject = Schema.Struct({ + "id": Schema.String.annotate({ + "description": + "The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](https://developers.openai.com/api/docs/assistants/migration) endpoint." + }), + "type": Schema.Literal("function").annotate({ + "description": "The type of tool call the output is required for. For now, this is always `function`." + }), + "function": Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the function." }), + "arguments": Schema.String.annotate({ + "description": "The arguments that the model expects you to pass to the function." + }) + }).annotate({ "description": "The function definition." }) +}).annotate({ "description": "Tool call objects", "identifier": "RunToolCallObject" }) +export type RunCompletionUsage = { + readonly "completion_tokens": number + readonly "prompt_tokens": number + readonly "total_tokens": number +} | null +export const RunCompletionUsage = Schema.Union([ + Schema.Struct({ + "completion_tokens": Schema.Number.annotate({ + "description": "Number of completion tokens used over the course of the run." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "prompt_tokens": Schema.Number.annotate({ + "description": "Number of prompt tokens used over the course of the run." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "total_tokens": Schema.Number.annotate({ "description": "Total number of tokens used (prompt + completion)." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ + "description": + "Usage statistics related to the run. This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.)." + }), + Schema.Null +]).annotate({ "identifier": "RunCompletionUsage" }) +export type RunStepDetailsMessageCreationObject = { + readonly "type": "message_creation" + readonly "message_creation": { readonly "message_id": string } +} +export const RunStepDetailsMessageCreationObject = Schema.Struct({ + "type": Schema.Literal("message_creation").annotate({ "description": "Always `message_creation`." }), + "message_creation": Schema.Struct({ + "message_id": Schema.String.annotate({ "description": "The ID of the message that was created by this run step." }) + }) +}).annotate({ + "title": "Message creation", + "description": "Details of the message creation by the run step.", + "identifier": "RunStepDetailsMessageCreationObject" +}) +export type RunStepDetailsToolCallsCodeOutputLogsObject = { readonly "type": "logs"; readonly "logs": string } +export const RunStepDetailsToolCallsCodeOutputLogsObject = Schema.Struct({ + "type": Schema.Literal("logs").annotate({ "description": "Always `logs`." }), + "logs": Schema.String.annotate({ "description": "The text output from the Code Interpreter tool call." }) +}).annotate({ + "title": "Code Interpreter log output", + "description": "Text output from the Code Interpreter tool call as part of a run step.", + "identifier": "RunStepDetailsToolCallsCodeOutputLogsObject" +}) +export type RunStepDetailsToolCallsCodeOutputImageObject = { + readonly "type": "image" + readonly "image": { readonly "file_id": string } +} +export const RunStepDetailsToolCallsCodeOutputImageObject = Schema.Struct({ + "type": Schema.Literal("image").annotate({ "description": "Always `image`." }), + "image": Schema.Struct({ + "file_id": Schema.String.annotate({ + "description": "The [file](https://developers.openai.com/api/reference/resources/files) ID of the image." + }) + }) +}).annotate({ "title": "Code Interpreter image output", "identifier": "RunStepDetailsToolCallsCodeOutputImageObject" }) +export type RunStepDetailsToolCallsFileSearchResultObject = { + readonly "file_id": string + readonly "file_name": string + readonly "score": number + readonly "content"?: ReadonlyArray<{ readonly "type"?: "text"; readonly "text"?: string }> +} +export const RunStepDetailsToolCallsFileSearchResultObject = Schema.Struct({ + "file_id": Schema.String.annotate({ "description": "The ID of the file that result was found in." }), + "file_name": Schema.String.annotate({ "description": "The name of the file that result was found in." }), + "score": Schema.Number.annotate({ + "description": "The score of the result. All values must be a floating point number between 0 and 1." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + "content": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "type": Schema.optionalKey(Schema.Literal("text").annotate({ "description": "The type of the content." })), + "text": Schema.optionalKey(Schema.String.annotate({ "description": "The text content of the file." })) + }) + ).annotate({ + "description": + "The content of the result that was found. The content is only included if requested via the include query parameter." + }) + ) +}).annotate({ + "title": "File search tool call result", + "description": "A result instance of the file search.", + "identifier": "RunStepDetailsToolCallsFileSearchResultObject" +}) +export type RunStepDetailsToolCallsFunctionObject = { + readonly "id": string + readonly "type": "function" + readonly "function": { readonly "name": string; readonly "arguments": string; readonly "output": string | null } +} +export const RunStepDetailsToolCallsFunctionObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the tool call object." }), + "type": Schema.Literal("function").annotate({ + "description": "The type of tool call. This is always going to be `function` for this type of tool call." + }), + "function": Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the function." }), + "arguments": Schema.String.annotate({ "description": "The arguments passed to the function." }), + "output": Schema.Union([ + Schema.String.annotate({ + "description": + "The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet." + }), + Schema.Null + ]) + }).annotate({ "description": "The definition of the function that was called." }) +}).annotate({ "title": "Function tool call", "identifier": "RunStepDetailsToolCallsFunctionObject" }) +export type RunStepCompletionUsage = { + readonly "completion_tokens": number + readonly "prompt_tokens": number + readonly "total_tokens": number +} | null +export const RunStepCompletionUsage = Schema.Union([ + Schema.Struct({ + "completion_tokens": Schema.Number.annotate({ + "description": "Number of completion tokens used over the course of the run step." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "prompt_tokens": Schema.Number.annotate({ + "description": "Number of prompt tokens used over the course of the run step." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "total_tokens": Schema.Number.annotate({ "description": "Total number of tokens used (prompt + completion)." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ + "description": + "Usage statistics related to the run step. This value will be `null` while the run step's status is `in_progress`." + }), + Schema.Null +]).annotate({ "identifier": "RunStepCompletionUsage" }) +export type RunStepDeltaStepDetailsMessageCreationObject = { + readonly "type": "message_creation" + readonly "message_creation"?: { readonly "message_id"?: string } +} +export const RunStepDeltaStepDetailsMessageCreationObject = Schema.Struct({ + "type": Schema.Literal("message_creation").annotate({ "description": "Always `message_creation`." }), + "message_creation": Schema.optionalKey( + Schema.Struct({ + "message_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the message that was created by this run step." }) + ) + }) + ) +}).annotate({ + "title": "Message creation", + "description": "Details of the message creation by the run step.", + "identifier": "RunStepDeltaStepDetailsMessageCreationObject" +}) +export type RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject = { + readonly "index": number + readonly "type": "logs" + readonly "logs"?: string +} +export const RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the output in the outputs array." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("logs").annotate({ "description": "Always `logs`." }), + "logs": Schema.optionalKey( + Schema.String.annotate({ "description": "The text output from the Code Interpreter tool call." }) + ) +}).annotate({ + "title": "Code interpreter log output", + "description": "Text output from the Code Interpreter tool call as part of a run step.", + "identifier": "RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject" +}) +export type RunStepDeltaStepDetailsToolCallsCodeOutputImageObject = { + readonly "index": number + readonly "type": "image" + readonly "image"?: { readonly "file_id"?: string } +} +export const RunStepDeltaStepDetailsToolCallsCodeOutputImageObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the output in the outputs array." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("image").annotate({ "description": "Always `image`." }), + "image": Schema.optionalKey( + Schema.Struct({ + "file_id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The [file](https://developers.openai.com/api/reference/resources/files) ID of the image." + }) + ) + }) + ) +}).annotate({ + "title": "Code interpreter image output", + "identifier": "RunStepDeltaStepDetailsToolCallsCodeOutputImageObject" +}) +export type RunStepDeltaStepDetailsToolCallsFileSearchObject = { + readonly "index": number + readonly "id"?: string + readonly "type": "file_search" + readonly "file_search": { readonly [x: string]: Schema.Json } +} +export const RunStepDeltaStepDetailsToolCallsFileSearchObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the tool call in the tool calls array." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the tool call object." })), + "type": Schema.Literal("file_search").annotate({ + "description": "The type of tool call. This is always going to be `file_search` for this type of tool call." + }), + "file_search": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "For now, this is always going to be an empty object." + }) +}).annotate({ "title": "File search tool call", "identifier": "RunStepDeltaStepDetailsToolCallsFileSearchObject" }) +export type RunStepDeltaStepDetailsToolCallsFunctionObject = { + readonly "index": number + readonly "id"?: string + readonly "type": "function" + readonly "function"?: { readonly "name"?: string; readonly "arguments"?: string; readonly "output"?: string | null } +} +export const RunStepDeltaStepDetailsToolCallsFunctionObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the tool call in the tool calls array." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the tool call object." })), + "type": Schema.Literal("function").annotate({ + "description": "The type of tool call. This is always going to be `function` for this type of tool call." + }), + "function": Schema.optionalKey( + Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the function." })), + "arguments": Schema.optionalKey( + Schema.String.annotate({ "description": "The arguments passed to the function." }) + ), + "output": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet." + }), + Schema.Null + ]) + ) + }).annotate({ "description": "The definition of the function that was called." }) + ) +}).annotate({ "title": "Function tool call", "identifier": "RunStepDeltaStepDetailsToolCallsFunctionObject" }) +export type MessageContentTextAnnotationsFileCitationObject = { + readonly "type": "file_citation" + readonly "text": string + readonly "file_citation": { readonly "file_id": string } + readonly "start_index": number + readonly "end_index": number +} +export const MessageContentTextAnnotationsFileCitationObject = Schema.Struct({ + "type": Schema.Literal("file_citation").annotate({ "description": "Always `file_citation`." }), + "text": Schema.String.annotate({ "description": "The text in the message content that needs to be replaced." }), + "file_citation": Schema.Struct({ + "file_id": Schema.String.annotate({ "description": "The ID of the specific File the citation is from." }) + }), + "start_index": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "end_index": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) +}).annotate({ + "title": "File citation", + "description": + "A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the \"file_search\" tool to search files.", + "identifier": "MessageContentTextAnnotationsFileCitationObject" +}) +export type MessageContentTextAnnotationsFilePathObject = { + readonly "type": "file_path" + readonly "text": string + readonly "file_path": { readonly "file_id": string } + readonly "start_index": number + readonly "end_index": number +} +export const MessageContentTextAnnotationsFilePathObject = Schema.Struct({ + "type": Schema.Literal("file_path").annotate({ "description": "Always `file_path`." }), + "text": Schema.String.annotate({ "description": "The text in the message content that needs to be replaced." }), + "file_path": Schema.Struct({ + "file_id": Schema.String.annotate({ "description": "The ID of the file that was generated." }) + }), + "start_index": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "end_index": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) +}).annotate({ + "title": "File path", + "description": + "A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.", + "identifier": "MessageContentTextAnnotationsFilePathObject" +}) +export type MessageContentRefusalObject = { readonly "type": "refusal"; readonly "refusal": string } +export const MessageContentRefusalObject = Schema.Struct({ + "type": Schema.Literal("refusal").annotate({ "description": "Always `refusal`." }), + "refusal": Schema.String +}).annotate({ + "title": "Refusal", + "description": "The refusal content generated by the assistant.", + "identifier": "MessageContentRefusalObject" +}) +export type MessageDeltaContentImageFileObject = { + readonly "index": number + readonly "type": "image_file" + readonly "image_file"?: { readonly "file_id"?: string; readonly "detail"?: "auto" | "low" | "high" } +} +export const MessageDeltaContentImageFileObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the content part in the message." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("image_file").annotate({ "description": "Always `image_file`." }), + "image_file": Schema.optionalKey(Schema.Struct({ + "file_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose=\"vision\"` when uploading the File if you need to later display the file content." + }) + ), + "detail": Schema.optionalKey( + Schema.Literals(["auto", "low", "high"]).annotate({ + "description": + "Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`." + }) + ) + })) +}).annotate({ + "title": "Image file", + "description": + "References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.", + "identifier": "MessageDeltaContentImageFileObject" +}) +export type MessageDeltaContentTextAnnotationsFileCitationObject = { + readonly "index": number + readonly "type": "file_citation" + readonly "text"?: string + readonly "file_citation"?: { readonly "file_id"?: string; readonly "quote"?: string } + readonly "start_index"?: number + readonly "end_index"?: number +} +export const MessageDeltaContentTextAnnotationsFileCitationObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the annotation in the text content part." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("file_citation").annotate({ "description": "Always `file_citation`." }), + "text": Schema.optionalKey( + Schema.String.annotate({ "description": "The text in the message content that needs to be replaced." }) + ), + "file_citation": Schema.optionalKey( + Schema.Struct({ + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the specific File the citation is from." }) + ), + "quote": Schema.optionalKey(Schema.String.annotate({ "description": "The specific quote in the file." })) + }) + ), + "start_index": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) + ), + "end_index": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) + ) +}).annotate({ + "title": "File citation", + "description": + "A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the \"file_search\" tool to search files.", + "identifier": "MessageDeltaContentTextAnnotationsFileCitationObject" +}) +export type MessageDeltaContentTextAnnotationsFilePathObject = { + readonly "index": number + readonly "type": "file_path" + readonly "text"?: string + readonly "file_path"?: { readonly "file_id"?: string } + readonly "start_index"?: number + readonly "end_index"?: number +} +export const MessageDeltaContentTextAnnotationsFilePathObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the annotation in the text content part." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("file_path").annotate({ "description": "Always `file_path`." }), + "text": Schema.optionalKey( + Schema.String.annotate({ "description": "The text in the message content that needs to be replaced." }) + ), + "file_path": Schema.optionalKey( + Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the file that was generated." })) + }) + ), + "start_index": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) + ), + "end_index": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) + ) +}).annotate({ + "title": "File path", + "description": + "A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.", + "identifier": "MessageDeltaContentTextAnnotationsFilePathObject" +}) +export type MessageDeltaContentRefusalObject = { + readonly "index": number + readonly "type": "refusal" + readonly "refusal"?: string +} +export const MessageDeltaContentRefusalObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the refusal part in the message." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("refusal").annotate({ "description": "Always `refusal`." }), + "refusal": Schema.optionalKey(Schema.String) +}).annotate({ + "title": "Refusal", + "description": "The refusal content that is part of a message.", + "identifier": "MessageDeltaContentRefusalObject" +}) +export type MessageDeltaContentImageUrlObject = { + readonly "index": number + readonly "type": "image_url" + readonly "image_url"?: { readonly "url"?: string; readonly "detail"?: "auto" | "low" | "high" } +} +export const MessageDeltaContentImageUrlObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the content part in the message." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("image_url").annotate({ "description": "Always `image_url`." }), + "image_url": Schema.optionalKey(Schema.Struct({ + "url": Schema.optionalKey( + Schema.String.annotate({ + "description": "The URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.", + "format": "uri" + }) + ), + "detail": Schema.optionalKey( + Schema.Literals(["auto", "low", "high"]).annotate({ + "description": + "Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high resolution using `high`." + }) + ) + })) +}).annotate({ + "title": "Image URL", + "description": "References an image URL in the content of a message.", + "identifier": "MessageDeltaContentImageUrlObject" +}) +export type DoneEvent = { readonly "event": "done"; readonly "data": "[DONE]" } +export const DoneEvent = Schema.Struct({ "event": Schema.Literal("done"), "data": Schema.Literal("[DONE]") }).annotate({ + "description": "Occurs when a stream ends.", + "identifier": "DoneEvent" +}) +export type DeleteThreadResponse = { + readonly "id": string + readonly "deleted": boolean + readonly "object": "thread.deleted" +} +export const DeleteThreadResponse = Schema.Struct({ + "id": Schema.String, + "deleted": Schema.Boolean, + "object": Schema.Literal("thread.deleted") +}).annotate({ "identifier": "DeleteThreadResponse" }) +export type DeleteMessageResponse = { + readonly "id": string + readonly "deleted": boolean + readonly "object": "thread.message.deleted" +} +export const DeleteMessageResponse = Schema.Struct({ + "id": Schema.String, + "deleted": Schema.Boolean, + "object": Schema.Literal("thread.message.deleted") +}).annotate({ "identifier": "DeleteMessageResponse" }) +export type SubmitToolOutputsRunRequest = { + readonly "tool_outputs": ReadonlyArray<{ readonly "tool_call_id"?: string; readonly "output"?: string }> + readonly "stream"?: boolean | null +} +export const SubmitToolOutputsRunRequest = Schema.Struct({ + "tool_outputs": Schema.Array(Schema.Struct({ + "tool_call_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The ID of the tool call in the `required_action` object within the run object the output is being submitted for." + }) + ), + "output": Schema.optionalKey( + Schema.String.annotate({ "description": "The output of the tool call to be submitted to continue the run." }) + ) + })).annotate({ "description": "A list of tools for which the outputs are being submitted." }), + "stream": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "SubmitToolOutputsRunRequest" }) +export type CompleteUploadRequest = { readonly "part_ids": ReadonlyArray; readonly "md5"?: string } +export const CompleteUploadRequest = Schema.Struct({ + "part_ids": Schema.Array(Schema.String).annotate({ "description": "The ordered list of Part IDs.\n" }), + "md5": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The optional md5 checksum for the file contents to verify if the bytes uploaded matches what you expect.\n" + }) + ) +}).annotate({ "identifier": "CompleteUploadRequest" }) +export type UploadPart = { + readonly "id": string + readonly "created_at": number + readonly "upload_id": string + readonly "object": "upload.part" +} +export const UploadPart = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The upload Part unique identifier, which can be referenced in API endpoints." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the Part was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "upload_id": Schema.String.annotate({ "description": "The ID of the Upload object that this Part was added to." }), + "object": Schema.Literal("upload.part").annotate({ "description": "The object type, which is always `upload.part`." }) +}).annotate({ + "title": "UploadPart", + "description": "The upload Part represents a chunk of bytes we can add to an Upload object.\n", + "identifier": "UploadPart" +}) +export type VectorStoreExpirationAfter = { readonly "anchor": "last_active_at"; readonly "days": number } +export const VectorStoreExpirationAfter = Schema.Struct({ + "anchor": Schema.Literal("last_active_at").annotate({ + "description": "Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`." + }), + "days": Schema.Number.annotate({ + "description": "The number of days after the anchor time that the vector store will expire." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(365).annotate({ "expected": "a value less than or equal to 365" })) +}).annotate({ + "title": "Vector store expiration policy", + "description": "The expiration policy for a vector store.", + "identifier": "VectorStoreExpirationAfter" +}) +export type AutoChunkingStrategyRequestParam = { readonly "type": "auto" } +export const AutoChunkingStrategyRequestParam = Schema.Struct({ + "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }) +}).annotate({ + "title": "Auto Chunking Strategy", + "description": + "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.", + "identifier": "AutoChunkingStrategyRequestParam" +}) +export type StaticChunkingStrategy = { + readonly "max_chunk_size_tokens": number + readonly "chunk_overlap_tokens": number +} +export const StaticChunkingStrategy = Schema.Struct({ + "max_chunk_size_tokens": Schema.Number.annotate({ + "description": + "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(100).annotate({ "expected": "a value greater than or equal to 100" }) + ).check(Schema.isLessThanOrEqualTo(4096).annotate({ "expected": "a value less than or equal to 4096" })), + "chunk_overlap_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "identifier": "StaticChunkingStrategy" }) +export type DeleteVectorStoreResponse = { + readonly "id": string + readonly "deleted": boolean + readonly "object": "vector_store.deleted" +} +export const DeleteVectorStoreResponse = Schema.Struct({ + "id": Schema.String, + "deleted": Schema.Boolean, + "object": Schema.Literal("vector_store.deleted") +}).annotate({ "identifier": "DeleteVectorStoreResponse" }) +export type VectorStoreFileBatchObject = { + readonly "id": string + readonly "object": "vector_store.file_batch" + readonly "created_at": number + readonly "vector_store_id": string + readonly "status": "in_progress" | "completed" | "cancelled" | "failed" + readonly "file_counts": { + readonly "in_progress": number + readonly "completed": number + readonly "failed": number + readonly "cancelled": number + readonly "total": number + } +} +export const VectorStoreFileBatchObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("vector_store.file_batch").annotate({ + "description": "The object type, which is always `vector_store.file_batch`." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the vector store files batch was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "vector_store_id": Schema.String.annotate({ + "description": + "The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to." + }), + "status": Schema.Literals(["in_progress", "completed", "cancelled", "failed"]).annotate({ + "description": + "The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`." + }), + "file_counts": Schema.Struct({ + "in_progress": Schema.Number.annotate({ "description": "The number of files that are currently being processed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "completed": Schema.Number.annotate({ "description": "The number of files that have been processed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "failed": Schema.Number.annotate({ "description": "The number of files that have failed to process." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "cancelled": Schema.Number.annotate({ "description": "The number of files that where cancelled." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "total": Schema.Number.annotate({ "description": "The total number of files." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }) +}).annotate({ + "title": "Vector store file batch", + "description": "A batch of files attached to a vector store.", + "identifier": "VectorStoreFileBatchObject" +}) +export type OtherChunkingStrategyResponseParam = { readonly "type": "other" } +export const OtherChunkingStrategyResponseParam = Schema.Struct({ + "type": Schema.Literal("other").annotate({ "description": "Always `other`." }) +}).annotate({ + "title": "Other Chunking Strategy", + "description": + "This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API.", + "identifier": "OtherChunkingStrategyResponseParam" +}) +export type DeleteVectorStoreFileResponse = { + readonly "id": string + readonly "deleted": boolean + readonly "object": "vector_store.file.deleted" +} +export const DeleteVectorStoreFileResponse = Schema.Struct({ + "id": Schema.String, + "deleted": Schema.Boolean, + "object": Schema.Literal("vector_store.file.deleted") +}).annotate({ "identifier": "DeleteVectorStoreFileResponse" }) +export type VectorStoreFileContentResponse = { + readonly "object": "vector_store.file_content.page" + readonly "data": ReadonlyArray<{ readonly "type"?: string; readonly "text"?: string }> + readonly "has_more": boolean + readonly "next_page": string | null +} +export const VectorStoreFileContentResponse = Schema.Struct({ + "object": Schema.Literal("vector_store.file_content.page").annotate({ + "description": "The object type, which is always `vector_store.file_content.page`" + }), + "data": Schema.Array( + Schema.Struct({ + "type": Schema.optionalKey( + Schema.String.annotate({ "description": "The content type (currently only `\"text\"`)" }) + ), + "text": Schema.optionalKey(Schema.String.annotate({ "description": "The text content" })) + }) + ).annotate({ "description": "Parsed content of the file." }), + "has_more": Schema.Boolean.annotate({ "description": "Indicates if there are more content pages to fetch." }), + "next_page": Schema.Union([ + Schema.String.annotate({ "description": "The token for the next page, if any." }), + Schema.Null + ]) +}).annotate({ + "description": "Represents the parsed content of a vector store file.", + "identifier": "VectorStoreFileContentResponse" +}) +export type VectorStoreSearchResultContentObject = { readonly "type": "text"; readonly "text": string } +export const VectorStoreSearchResultContentObject = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "The type of content." }), + "text": Schema.String.annotate({ "description": "The text content returned from search." }) +}).annotate({ "identifier": "VectorStoreSearchResultContentObject" }) +export type DeletedConversationResource = { + readonly "object": "conversation.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const DeletedConversationResource = Schema.Struct({ + "object": Schema.Literal("conversation.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String +}).annotate({ "identifier": "DeletedConversationResource" }) +export type SafetyCaseNoticeType = "warning" | "deactivation" +export const SafetyCaseNoticeType = Schema.Literals(["warning", "deactivation"]).annotate({ + "identifier": "SafetyCaseNoticeType" +}) +export type SafetyAlertErrorType = + | "potentially_unintended_data_transfer" + | "potentially_unintended_data_access" + | "potentially_unintended_destructive_activity" + | "other" +export const SafetyAlertErrorType = Schema.Literals([ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other" +]).annotate({ "identifier": "SafetyAlertErrorType" }) +export type DecisionsUsageBucketWidth = "1m" | "1h" | "1d" +export const DecisionsUsageBucketWidth = Schema.Literals(["1m", "1h", "1d"]).annotate({ + "identifier": "DecisionsUsageBucketWidth" +}) +export type DecisionsUsageGroupBy = + | "project_id" + | "user_id" + | "api_key_id" + | "model" + | "batch" + | "service_tier" + | "api_source" +export const DecisionsUsageGroupBy = Schema.Literals([ + "project_id", + "user_id", + "api_key_id", + "model", + "batch", + "service_tier", + "api_source" +]).annotate({ "identifier": "DecisionsUsageGroupBy" }) +export type DecisionsUsageApiSource = "agents_api" | "unlabeled" +export const DecisionsUsageApiSource = Schema.Literals(["agents_api", "unlabeled"]).annotate({ + "identifier": "DecisionsUsageApiSource" +}) +export type DecisionsUsageErrorDetails = { + readonly "code": string | null + readonly "message": string + readonly "param": string | null + readonly "type": string +} +export const DecisionsUsageErrorDetails = Schema.Struct({ + "code": Schema.Union([Schema.String, Schema.Null]), + "message": Schema.String, + "param": Schema.Union([Schema.String, Schema.Null]), + "type": Schema.String +}).annotate({ "identifier": "DecisionsUsageErrorDetails" }) +export type ExternalStorageOrder = "asc" | "desc" +export const ExternalStorageOrder = Schema.Literals(["asc", "desc"]).annotate({ "identifier": "ExternalStorageOrder" }) +export type ExternalStorageStatus = "pending" | "validated" | "unhealthy" +export const ExternalStorageStatus = Schema.Literals(["pending", "validated", "unhealthy"]).annotate({ + "identifier": "ExternalStorageStatus" +}) +export type AwsExternalStorageProviderParams = { + readonly "type": "aws" + readonly "bucket": string + readonly "role_arn": string +} +export const AwsExternalStorageProviderParams = Schema.Struct({ + "type": Schema.Literal("aws"), + "bucket": Schema.String, + "role_arn": Schema.String +}).annotate({ "identifier": "AwsExternalStorageProviderParams" }) +export type AzureExternalStorageProviderParams = { + readonly "type": "azure" + readonly "tenant_id": string + readonly "subscription_id": string + readonly "resource_group": string + readonly "account_name": string + readonly "container": string +} +export const AzureExternalStorageProviderParams = Schema.Struct({ + "type": Schema.Literal("azure"), + "tenant_id": Schema.String, + "subscription_id": Schema.String, + "resource_group": Schema.String, + "account_name": Schema.String, + "container": Schema.String +}).annotate({ "identifier": "AzureExternalStorageProviderParams" }) +export type GcpExternalStorageProviderParams = { + readonly "type": "gcp" + readonly "bucket": string + readonly "workload_identity_project_number": string + readonly "workload_identity_pool_id": string + readonly "workload_identity_provider_id": string +} +export const GcpExternalStorageProviderParams = Schema.Struct({ + "type": Schema.Literal("gcp"), + "bucket": Schema.String, + "workload_identity_project_number": Schema.String, + "workload_identity_pool_id": Schema.String, + "workload_identity_provider_id": Schema.String +}).annotate({ "identifier": "GcpExternalStorageProviderParams" }) +export type OciExternalStorageProviderParams = { + readonly "type": "oci" + readonly "tenancy_ocid": string + readonly "region": string + readonly "bucket": string +} +export const OciExternalStorageProviderParams = Schema.Struct({ + "type": Schema.Literal("oci"), + "tenancy_ocid": Schema.String, + "region": Schema.String, + "bucket": Schema.String +}).annotate({ "identifier": "OciExternalStorageProviderParams" }) +export type ExternalStorageDeletedResource = { + readonly "object": "organization.external_storage.deleted" + readonly "id": string + readonly "deleted": boolean +} +export const ExternalStorageDeletedResource = Schema.Struct({ + "object": Schema.Literal("organization.external_storage.deleted"), + "id": Schema.String, + "deleted": Schema.Boolean +}).annotate({ "identifier": "ExternalStorageDeletedResource" }) +export type SpendLimitCurrency = string | "USD" +export const SpendLimitCurrency = Schema.Union([Schema.String, Schema.Literal("USD")]).annotate({ + "identifier": "SpendLimitCurrency" +}) +export type SpendLimitInterval = string | "month" +export const SpendLimitInterval = Schema.Union([Schema.String, Schema.Literal("month")]).annotate({ + "identifier": "SpendLimitInterval" +}) +export type SpendLimitEnforcementStatus = string | "inactive" | "enforcing" +export const SpendLimitEnforcementStatus = Schema.Union([Schema.String, Schema.Literals(["inactive", "enforcing"])]) + .annotate({ "identifier": "SpendLimitEnforcementStatus" }) +export type UpdateOrganizationSpendLimitBody = { + readonly "threshold_amount": number + readonly "currency": "USD" + readonly "interval": "month" +} +export const UpdateOrganizationSpendLimitBody = Schema.Struct({ + "threshold_amount": Schema.Number.annotate({ "description": "The hard spend limit amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" })), + "currency": Schema.Literal("USD").annotate({ + "description": "The currency for the threshold amount. Currently, only `USD` is supported." + }), + "interval": Schema.Literal("month").annotate({ + "description": "The time interval for evaluating spend against the threshold. Currently, only `month` is supported." + }) +}).annotate({ + "description": "Parameters for the hard spend limit you want to create or replace.", + "identifier": "UpdateOrganizationSpendLimitBody" +}) +export type OrganizationSpendLimitDeletedResource = { + readonly "object": "organization.spend_limit.deleted" + readonly "deleted": boolean +} +export const OrganizationSpendLimitDeletedResource = Schema.Struct({ + "object": Schema.Literal("organization.spend_limit.deleted").annotate({ + "description": "The object type, which is always `organization.spend_limit.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the hard spend limit was deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting an organization hard spend limit.", + "identifier": "OrganizationSpendLimitDeletedResource" +}) +export type UpdateProjectSpendLimitBody = { + readonly "threshold_amount": number + readonly "currency": "USD" + readonly "interval": "month" +} +export const UpdateProjectSpendLimitBody = Schema.Struct({ + "threshold_amount": Schema.Number.annotate({ "description": "The hard spend limit amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" })), + "currency": Schema.Literal("USD").annotate({ + "description": "The currency for the threshold amount. Currently, only `USD` is supported." + }), + "interval": Schema.Literal("month").annotate({ + "description": "The time interval for evaluating spend against the threshold. Currently, only `month` is supported." + }) +}).annotate({ + "description": "Parameters for the hard spend limit you want to create or replace.", + "identifier": "UpdateProjectSpendLimitBody" +}) +export type ProjectSpendLimitDeletedResource = { + readonly "object": "project.spend_limit.deleted" + readonly "deleted": boolean +} +export const ProjectSpendLimitDeletedResource = Schema.Struct({ + "object": Schema.Literal("project.spend_limit.deleted").annotate({ + "description": "The object type, which is always `project.spend_limit.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the hard spend limit was deleted." }) +}).annotate({ + "description": "Confirmation payload returned after deleting a project hard spend limit.", + "identifier": "ProjectSpendLimitDeletedResource" +}) +export type CreateProjectServiceAccountApiKeyBody = { + readonly "name"?: string + readonly "scopes"?: ReadonlyArray + readonly "expires_in_seconds"?: number | null +} +export const CreateProjectServiceAccountApiKeyBody = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "API key name." })), + "scopes": Schema.optionalKey( + Schema.Array(Schema.String.annotate({ "description": "API key scope." })).annotate({ + "description": "API key scopes." + }) + ), + "expires_in_seconds": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "Number of seconds until the API key expires." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" })).check( + Schema.isLessThanOrEqualTo(31536000).annotate({ "expected": "a value less than or equal to 31536000" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "description": "The service account API key create request payload.", + "identifier": "CreateProjectServiceAccountApiKeyBody" +}) +export type ServiceAccountApiKeyBody = { + readonly "object": "organization.project.service_account.api_key" + readonly "value": string + readonly "name": string + readonly "created_at": number + readonly "expires_at"?: number | null + readonly "id": string +} +export const ServiceAccountApiKeyBody = Schema.Struct({ + "object": Schema.Literal("organization.project.service_account.api_key").annotate({ + "description": "The object type, which is always `organization.project.service_account.api_key`" + }), + "value": Schema.String.annotate({ "description": "The unredacted API key value." }), + "name": Schema.String.annotate({ "description": "The name of the API key." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) when the API key was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "id": Schema.String.annotate({ "description": "The identifier of the API key." }) +}).annotate({ "identifier": "ServiceAccountApiKeyBody" }) +export type ProvenanceCheckObject = "content_provenance_check" +export const ProvenanceCheckObject = Schema.Literal("content_provenance_check").annotate({ + "identifier": "ProvenanceCheckObject" +}) +export type ProvenanceDetectionResultApi = "detected" | "not_detected" +export const ProvenanceDetectionResultApi = Schema.Literals(["detected", "not_detected"]).annotate({ + "identifier": "ProvenanceDetectionResultApi" +}) +export type C2PAValidationStateApi = "trusted" | "valid" | "invalid" | "not_present" +export const C2PAValidationStateApi = Schema.Literals(["trusted", "valid", "invalid", "not_present"]).annotate({ + "identifier": "C2PAValidationStateApi" +}) +export type OrderEnum = "asc" | "desc" +export const OrderEnum = Schema.Literals(["asc", "desc"]).annotate({ "identifier": "OrderEnum" }) +export type VideoModel = + | string + | "sora-2" + | "sora-2-pro" + | "sora-2-2025-10-06" + | "sora-2-pro-2025-10-06" + | "sora-2-2025-12-08" +export const VideoModel = Schema.Union([ + Schema.String, + Schema.Literals(["sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08"]) +]).annotate({ "identifier": "VideoModel" }) +export type VideoStatus = "queued" | "in_progress" | "completed" | "failed" +export const VideoStatus = Schema.Literals(["queued", "in_progress", "completed", "failed"]).annotate({ + "identifier": "VideoStatus" +}) +export type VideoSize = "720x1280" | "1280x720" | "1024x1792" | "1792x1024" +export const VideoSize = Schema.Literals(["720x1280", "1280x720", "1024x1792", "1792x1024"]).annotate({ + "identifier": "VideoSize" +}) +export type ImageRefParam_2 = { readonly "image_url"?: string; readonly "file_id"?: string } +export const ImageRefParam_2 = Schema.Struct({ + "image_url": Schema.optionalKey( + Schema.String.annotate({ "description": "A fully qualified URL or base64-encoded data URL.", "format": "uri" }) + .check(Schema.isMaxCodePoints(20971520).annotate({ "expected": "a string with at most 20971520 code points" })) + ), + "file_id": Schema.optionalKey(Schema.String) +}).annotate({ "identifier": "ImageRefParam-2" }) +export type VideoSeconds = "4" | "8" | "12" +export const VideoSeconds = Schema.Literals(["4", "8", "12"]).annotate({ "identifier": "VideoSeconds" }) +export type VideoCharacterResource = { + readonly "id": string | null + readonly "name": string | null + readonly "created_at": number +} +export const VideoCharacterResource = Schema.Struct({ + "id": Schema.Union([ + Schema.String.annotate({ "description": "Identifier for the character creation cameo." }), + Schema.Null + ]), + "name": Schema.Union([Schema.String.annotate({ "description": "Display name for the character." }), Schema.Null]), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the character was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "identifier": "VideoCharacterResource" }) +export type VideoReferenceInputParam = { readonly "id": string } +export const VideoReferenceInputParam = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier of the completed video." }) +}).annotate({ "description": "Reference to the completed video.", "identifier": "VideoReferenceInputParam" }) +export type DeletedVideoResource = { + readonly "object": "video.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const DeletedVideoResource = Schema.Struct({ + "object": Schema.Literal("video.deleted").annotate({ + "description": "The object type that signals the deletion response." + }), + "deleted": Schema.Boolean.annotate({ "description": "Indicates that the video resource was deleted." }), + "id": Schema.String.annotate({ "description": "Identifier of the deleted video." }) +}).annotate({ + "title": "Deleted video response", + "description": "Confirmation payload returned after deleting a video.", + "identifier": "DeletedVideoResource" +}) +export type VideoContentVariant = "video" | "thumbnail" | "spritesheet" +export const VideoContentVariant = Schema.Literals(["video", "thumbnail", "spritesheet"]).annotate({ + "identifier": "VideoContentVariant" +}) +export type CreateVideoRemixBody = { readonly "prompt": string } +export const CreateVideoRemixBody = Schema.Struct({ + "prompt": Schema.String.annotate({ "description": "Updated text prompt that directs the remix generation." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" })) +}).annotate({ + "title": "Create video remix request", + "description": "Parameters for remixing an existing generated video.", + "identifier": "CreateVideoRemixBody" +}) +export type TruncationEnum = "auto" | "disabled" +export const TruncationEnum = Schema.Literals(["auto", "disabled"]).annotate({ "identifier": "TruncationEnum" }) +export type PersonalityEnum = string | "friendly" | "pragmatic" +export const PersonalityEnum = Schema.Union([Schema.String, Schema.Literals(["friendly", "pragmatic"])]).annotate({ + "identifier": "PersonalityEnum" +}) +export type TokenCountsResource = { readonly "object": "response.input_tokens"; readonly "input_tokens": number } +export const TokenCountsResource = Schema.Struct({ + "object": Schema.Literal("response.input_tokens"), + "input_tokens": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "title": "Token counts", "identifier": "TokenCountsResource" }) +export type PromptCacheRetentionEnum = "in_memory" | "in-memory" | "24h" +export const PromptCacheRetentionEnum = Schema.Literals(["in_memory", "in-memory", "24h"]).annotate({ + "identifier": "PromptCacheRetentionEnum" +}) +export type ServiceTierEnum = "auto" | "default" | "fast" | "flex" | "priority" +export const ServiceTierEnum = Schema.Literals(["auto", "default", "fast", "flex", "priority"]).annotate({ + "identifier": "ServiceTierEnum" +}) +export type SkillResource = { + readonly "id": string + readonly "object": "skill" + readonly "name": string + readonly "description": string + readonly "created_at": number + readonly "default_version": string + readonly "latest_version": string +} +export const SkillResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Unique identifier for the skill." }), + "object": Schema.Literal("skill").annotate({ "description": "The object type, which is `skill`." }), + "name": Schema.String.annotate({ "description": "Name of the skill." }), + "description": Schema.String.annotate({ "description": "Description of the skill." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (seconds) for when the skill was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "default_version": Schema.String.annotate({ "description": "Default version for the skill." }), + "latest_version": Schema.String.annotate({ "description": "Latest version for the skill." }) +}).annotate({ "identifier": "SkillResource" }) +export type CreateSkillBody = { readonly "files": ReadonlyArray | string } +export const CreateSkillBody = Schema.Struct({ + "files": Schema.Union([ + Schema.Array(Schema.String.annotate({ "format": "binary" })).annotate({ + "description": "Skill files to upload (directory upload) or a single zip file." + }).check(Schema.isMaxLength(500).annotate({ "expected": "a value with a length of at most 500" })), + Schema.String.annotate({ "description": "Skill zip file to upload.", "format": "binary" }) + ], { mode: "oneOf" }) +}).annotate({ + "title": "Create skill request", + "description": "Uploads a skill either as a directory (multipart `files[]`) or as a single zip file.", + "identifier": "CreateSkillBody" +}) +export type SetDefaultSkillVersionBody = { readonly "default_version": string } +export const SetDefaultSkillVersionBody = Schema.Struct({ + "default_version": Schema.String.annotate({ "description": "The skill version number to set as default." }) +}).annotate({ + "title": "Update skill request", + "description": "Updates the default version pointer for a skill.", + "identifier": "SetDefaultSkillVersionBody" +}) +export type DeletedSkillResource = { + readonly "object": "skill.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const DeletedSkillResource = Schema.Struct({ + "object": Schema.Literal("skill.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String +}).annotate({ "identifier": "DeletedSkillResource" }) +export type SkillVersionResource = { + readonly "object": "skill.version" + readonly "id": string + readonly "skill_id": string + readonly "version": string + readonly "created_at": number + readonly "name": string + readonly "description": string +} +export const SkillVersionResource = Schema.Struct({ + "object": Schema.Literal("skill.version").annotate({ "description": "The object type, which is `skill.version`." }), + "id": Schema.String.annotate({ "description": "Unique identifier for the skill version." }), + "skill_id": Schema.String.annotate({ "description": "Identifier of the skill for this version." }), + "version": Schema.String.annotate({ "description": "Version number for this skill." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (seconds) for when the version was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "name": Schema.String.annotate({ "description": "Name of the skill version." }), + "description": Schema.String.annotate({ "description": "Description of the skill version." }) +}).annotate({ "identifier": "SkillVersionResource" }) +export type CreateSkillVersionBody = { readonly "files": ReadonlyArray | string; readonly "default"?: boolean } +export const CreateSkillVersionBody = Schema.Struct({ + "files": Schema.Union([ + Schema.Array(Schema.String.annotate({ "format": "binary" })).annotate({ + "description": "Skill files to upload (directory upload) or a single zip file." + }).check(Schema.isMaxLength(500).annotate({ "expected": "a value with a length of at most 500" })), + Schema.String.annotate({ "description": "Skill zip file to upload.", "format": "binary" }) + ], { mode: "oneOf" }), + "default": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether to set this version as the default." }) + ) +}).annotate({ + "title": "Create skill version request", + "description": "Uploads a new immutable version of a skill.", + "identifier": "CreateSkillVersionBody" +}) +export type DeletedSkillVersionResource = { + readonly "object": "skill.version.deleted" + readonly "deleted": boolean + readonly "id": string + readonly "version": string +} +export const DeletedSkillVersionResource = Schema.Struct({ + "object": Schema.Literal("skill.version.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String, + "version": Schema.String.annotate({ "description": "The deleted skill version." }) +}).annotate({ "identifier": "DeletedSkillVersionResource" }) +export type ChatkitWorkflowTracing = { readonly "enabled": boolean } +export const ChatkitWorkflowTracing = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Indicates whether tracing is enabled." }) +}).annotate({ + "title": "Tracing Configuration", + "description": "Controls diagnostic tracing during the session.", + "identifier": "ChatkitWorkflowTracing" +}) +export type ChatSessionRateLimits = { readonly "max_requests_per_1_minute": number } +export const ChatSessionRateLimits = Schema.Struct({ + "max_requests_per_1_minute": Schema.Number.annotate({ + "description": "Maximum allowed requests per one-minute window." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "Rate limits", + "description": "Active per-minute request limit for the session.", + "identifier": "ChatSessionRateLimits" +}) +export type ChatSessionStatus = "active" | "expired" | "cancelled" +export const ChatSessionStatus = Schema.Literals(["active", "expired", "cancelled"]).annotate({ + "identifier": "ChatSessionStatus" +}) +export type ChatSessionAutomaticThreadTitling = { readonly "enabled": boolean } +export const ChatSessionAutomaticThreadTitling = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Whether automatic thread titling is enabled." }) +}).annotate({ + "title": "Automatic thread titling", + "description": "Automatic thread title preferences for the session.", + "identifier": "ChatSessionAutomaticThreadTitling" +}) +export type ChatSessionFileUpload = { + readonly "enabled": boolean + readonly "max_file_size": number | null + readonly "max_files": number | null +} +export const ChatSessionFileUpload = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Indicates if uploads are enabled for the session." }), + "max_file_size": Schema.Union([ + Schema.Number.annotate({ "description": "Maximum upload size in megabytes." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]), + "max_files": Schema.Union([ + Schema.Number.annotate({ "description": "Maximum number of uploads allowed during the session." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) +}).annotate({ + "title": "File upload settings", + "description": "Upload permissions and limits applied to the session.", + "identifier": "ChatSessionFileUpload" +}) +export type ChatSessionHistory = { readonly "enabled": boolean; readonly "recent_threads": number | null } +export const ChatSessionHistory = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Indicates if chat history is persisted for the session." }), + "recent_threads": Schema.Union([ + Schema.Number.annotate({ + "description": "Number of prior threads surfaced in history views. Defaults to null when all history is retained." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) +}).annotate({ + "title": "History settings", + "description": "History retention preferences returned for the session.", + "identifier": "ChatSessionHistory" +}) +export type WorkflowTracingParam = { readonly "enabled"?: boolean } +export const WorkflowTracingParam = Schema.Struct({ + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether tracing is enabled during the session. Defaults to true." }) + ) +}).annotate({ + "title": "Tracing Configuration", + "description": "Controls diagnostic tracing during the session.", + "identifier": "WorkflowTracingParam" +}) +export type ExpiresAfterParam = { readonly "anchor": "created_at"; readonly "seconds": number } +export const ExpiresAfterParam = Schema.Struct({ + "anchor": Schema.Literal("created_at").annotate({ + "description": "Base timestamp used to calculate expiration. Currently fixed to `created_at`." + }), + "seconds": Schema.Number.annotate({ + "description": "Number of seconds after the anchor when the session expires.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(600).annotate({ "expected": "a value less than or equal to 600" })) +}).annotate({ + "title": "Expiration overrides", + "description": "Controls when the session expires relative to an anchor timestamp.", + "identifier": "ExpiresAfterParam" +}) +export type RateLimitsParam = { readonly "max_requests_per_1_minute"?: number } +export const RateLimitsParam = Schema.Struct({ + "max_requests_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Maximum number of requests allowed per minute for the session. Defaults to 10." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ) +}).annotate({ + "title": "Rate limit overrides", + "description": "Controls request rate limits for the session.", + "identifier": "RateLimitsParam" +}) +export type AutomaticThreadTitlingParam = { readonly "enabled"?: boolean } +export const AutomaticThreadTitlingParam = Schema.Struct({ + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Enable automatic thread title generation. Defaults to true." }) + ) +}).annotate({ + "title": "Automatic thread titling configuration", + "description": "Controls whether ChatKit automatically generates thread titles.", + "identifier": "AutomaticThreadTitlingParam" +}) +export type FileUploadParam = { + readonly "enabled"?: boolean + readonly "max_file_size"?: number + readonly "max_files"?: number +} +export const FileUploadParam = Schema.Struct({ + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Enable uploads for this session. Defaults to false." }) + ), + "max_file_size": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Maximum size in megabytes for each uploaded file. Defaults to 512 MB, which is the maximum allowable size." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(512).annotate({ "expected": "a value less than or equal to 512" })) + ), + "max_files": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Maximum number of files that can be uploaded to the session. Defaults to 10." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ) +}).annotate({ + "title": "File upload configuration", + "description": "Controls whether users can upload files.", + "identifier": "FileUploadParam" +}) +export type HistoryParam = { readonly "enabled"?: boolean; readonly "recent_threads"?: number } +export const HistoryParam = Schema.Struct({ + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Enables chat users to access previous ChatKit threads. Defaults to true." + }) + ), + "recent_threads": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Number of recent ChatKit threads users have access to. Defaults to unlimited when unset." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ) +}).annotate({ + "title": "Chat history configuration", + "description": "Controls how much historical context is retained for the session.", + "identifier": "HistoryParam" +}) +export type UserMessageInputText = { readonly "type": "input_text"; readonly "text": string } +export const UserMessageInputText = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "Type discriminator that is always `input_text`." }), + "text": Schema.String.annotate({ "description": "Plain-text content supplied by the user." }) +}).annotate({ + "title": "User message input", + "description": "Text block that a user contributed to the thread.", + "identifier": "UserMessageInputText" +}) +export type UserMessageQuotedText = { readonly "type": "quoted_text"; readonly "text": string } +export const UserMessageQuotedText = Schema.Struct({ + "type": Schema.Literal("quoted_text").annotate({ "description": "Type discriminator that is always `quoted_text`." }), + "text": Schema.String.annotate({ "description": "Quoted text content." }) +}).annotate({ + "title": "User message quoted text", + "description": "Quoted snippet that the user referenced in their message.", + "identifier": "UserMessageQuotedText" +}) +export type AttachmentType = "image" | "file" +export const AttachmentType = Schema.Literals(["image", "file"]).annotate({ "identifier": "AttachmentType" }) +export type ToolChoice = { readonly "id": string } +export const ToolChoice = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the requested tool." }) +}).annotate({ + "title": "Tool choice", + "description": "Tool selection that the assistant should honor when executing the item.", + "identifier": "ToolChoice" +}) +export type FileAnnotationSource = { readonly "type": "file"; readonly "filename": string } +export const FileAnnotationSource = Schema.Struct({ + "type": Schema.Literal("file").annotate({ "description": "Type discriminator that is always `file`." }), + "filename": Schema.String.annotate({ "description": "Filename referenced by the annotation." }) +}).annotate({ + "title": "File annotation source", + "description": "Attachment source referenced by an annotation.", + "identifier": "FileAnnotationSource" +}) +export type UrlAnnotationSource = { readonly "type": "url"; readonly "url": string } +export const UrlAnnotationSource = Schema.Struct({ + "type": Schema.Literal("url").annotate({ "description": "Type discriminator that is always `url`." }), + "url": Schema.String.annotate({ "description": "URL referenced by the annotation.", "format": "uri" }) +}).annotate({ + "title": "URL annotation source", + "description": "URL backing an annotation entry.", + "identifier": "UrlAnnotationSource" +}) +export type WidgetMessageItem = { + readonly "id": string + readonly "object": "chatkit.thread_item" + readonly "created_at": number + readonly "thread_id": string + readonly "type": "chatkit.widget" + readonly "widget": string +} +export const WidgetMessageItem = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread item." }), + "object": Schema.Literal("chatkit.thread_item").annotate({ + "description": "Type discriminator that is always `chatkit.thread_item`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the item was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ "description": "Identifier of the parent thread." }), + "type": Schema.Literal("chatkit.widget").annotate({ + "description": "Type discriminator that is always `chatkit.widget`." + }), + "widget": Schema.String.annotate({ "description": "Serialized widget payload rendered in the UI." }) +}).annotate({ + "title": "Widget message", + "description": "Thread item that renders a widget payload.", + "identifier": "WidgetMessageItem" +}) +export type ClientToolCallStatus = "in_progress" | "completed" +export const ClientToolCallStatus = Schema.Literals(["in_progress", "completed"]).annotate({ + "identifier": "ClientToolCallStatus" +}) +export type TaskType = "custom" | "thought" +export const TaskType = Schema.Literals(["custom", "thought"]).annotate({ "identifier": "TaskType" }) +export type ActiveStatus = { readonly "type": "active" } +export const ActiveStatus = Schema.Struct({ + "type": Schema.Literal("active").annotate({ "description": "Status discriminator that is always `active`." }) +}).annotate({ + "title": "Active thread status", + "description": "Indicates that a thread is active.", + "identifier": "ActiveStatus" +}) +export type LockedStatus = { readonly "type": "locked"; readonly "reason": string | null } +export const LockedStatus = Schema.Struct({ + "type": Schema.Literal("locked").annotate({ "description": "Status discriminator that is always `locked`." }), + "reason": Schema.Union([ + Schema.String.annotate({ + "description": "Reason that the thread was locked. Defaults to null when no reason is recorded." + }), + Schema.Null + ]) +}).annotate({ + "title": "Locked thread status", + "description": "Indicates that a thread is locked and cannot accept new input.", + "identifier": "LockedStatus" +}) +export type ClosedStatus = { readonly "type": "closed"; readonly "reason": string | null } +export const ClosedStatus = Schema.Struct({ + "type": Schema.Literal("closed").annotate({ "description": "Status discriminator that is always `closed`." }), + "reason": Schema.Union([ + Schema.String.annotate({ + "description": "Reason that the thread was closed. Defaults to null when no reason is recorded." + }), + Schema.Null + ]) +}).annotate({ + "title": "Closed thread status", + "description": "Indicates that a thread has been closed.", + "identifier": "ClosedStatus" +}) +export type DeletedThreadResource = { + readonly "id": string + readonly "object": "chatkit.thread.deleted" + readonly "deleted": boolean +} +export const DeletedThreadResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the deleted thread." }), + "object": Schema.Literal("chatkit.thread.deleted").annotate({ + "description": "Type discriminator that is always `chatkit.thread.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Indicates that the thread has been deleted." }) +}).annotate({ + "title": "Deleted thread", + "description": "Confirmation payload returned after deleting a thread.", + "identifier": "DeletedThreadResource" +}) +export type DecisionInputText = { readonly "type": "input_text"; readonly "text": string } +export const DecisionInputText = Schema.Struct({ + "type": Schema.Literal("input_text"), + "text": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })) +}).annotate({ "identifier": "DecisionInputText" }) +export type ImageDetailParam = "low" | "high" | "auto" | "original" +export const ImageDetailParam = Schema.Literals(["low", "high", "auto", "original"]).annotate({ + "identifier": "ImageDetailParam" +}) +export type QuestionParamPredicate = { + readonly "type": "predicate" + readonly "name"?: string + readonly "instructions": string +} +export const QuestionParamPredicate = Schema.Struct({ + "type": Schema.Literal("predicate").annotate({ "description": "The type of the object. Always `predicate`." }), + "name": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "instructions": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) +}).annotate({ + "description": "Estimate how likely it is that a statement about the input is true.", + "identifier": "QuestionParamPredicate" +}) +export type ChoiceValueParam = string | boolean +export const ChoiceValueParam = Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + Schema.Boolean +], { mode: "oneOf" }).annotate({ + "description": "Choice values are typed: a string and a boolean with the same text are distinct.", + "identifier": "ChoiceValueParam" +}) +export type ScoreLevelParam = { readonly "label": string; readonly "description"?: string } +export const ScoreLevelParam = Schema.Struct({ + "label": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "description": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}).annotate({ "identifier": "ScoreLevelParam" }) +export type AnswerResourcePredicate = { + readonly "type": "predicate" + readonly "name": string | null + readonly "probability": number +} +export const AnswerResourcePredicate = Schema.Struct({ + "type": Schema.Literal("predicate").annotate({ "description": "The type of the object. Always `predicate`." }), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]), + "probability": Schema.Finite +}).annotate({ "identifier": "AnswerResourcePredicate" }) +export type ChoiceValueResource = string | boolean +export const ChoiceValueResource = Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Boolean +], { mode: "oneOf" }).annotate({ + "description": "Choice values are typed: a string and a boolean with the same text are distinct.", + "identifier": "ChoiceValueResource" +}) +export type ScoreProbabilityResource = { + readonly "value": number + readonly "label": string + readonly "probability": number +} +export const ScoreProbabilityResource = Schema.Struct({ + "value": Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "label": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "probability": Schema.Finite +}).annotate({ "identifier": "ScoreProbabilityResource" }) +export type AnswerResourceRefusal = { readonly "type": "refusal"; readonly "name": string | null } +export const AnswerResourceRefusal = Schema.Struct({ + "type": Schema.Literal("refusal").annotate({ "description": "The type of the object. Always `refusal`." }), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]) +}).annotate({ + "description": + "The model declined to answer this question. Other questions in the same request can still receive answers.", + "identifier": "AnswerResourceRefusal" +}) +export type InputTokensDetailsResource = { readonly "cached_tokens": number; readonly "cache_write_tokens": number } +export const InputTokensDetailsResource = Schema.Struct({ + "cached_tokens": Schema.Number.annotate({ "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check( + Schema.isGreaterThanOrEqualTo(-2147483648).annotate({ "expected": "a value greater than or equal to -2147483648" }) + ).check(Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" })), + "cache_write_tokens": Schema.Number.annotate({ "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check( + Schema.isGreaterThanOrEqualTo(-2147483648).annotate({ "expected": "a value greater than or equal to -2147483648" }) + ).check(Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" })) +}).annotate({ "identifier": "InputTokensDetailsResource" }) +export type OutputTokensDetailsResource = { readonly "reasoning_tokens": number } +export const OutputTokensDetailsResource = Schema.Struct({ + "reasoning_tokens": Schema.Number.annotate({ "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check( + Schema.isGreaterThanOrEqualTo(-2147483648).annotate({ "expected": "a value greater than or equal to -2147483648" }) + ).check(Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" })) +}).annotate({ "identifier": "OutputTokensDetailsResource" }) +export type ListOrderParam = "asc" | "desc" +export const ListOrderParam = Schema.Literals(["asc", "desc"]).annotate({ + "description": "The order in which paginated resources are returned.", + "identifier": "ListOrderParam" +}) +export type EnvironmentTypeParam = "openai_hosted" +export const EnvironmentTypeParam = Schema.Literal("openai_hosted").annotate({ + "description": "The hosting type of an execution environment.", + "identifier": "EnvironmentTypeParam" +}) +export type EnvironmentTypeResource = "openai_hosted" | "self_hosted" +export const EnvironmentTypeResource = Schema.Literals(["openai_hosted", "self_hosted"]).annotate({ + "description": "The kind of execution environment.", + "identifier": "EnvironmentTypeResource" +}) +export type EnvironmentStatusResource = + | "pending" + | "ready" + | "connected" + | "disconnected" + | "suspended" + | "expired" + | "failed" +export const EnvironmentStatusResource = Schema.Literals([ + "pending", + "ready", + "connected", + "disconnected", + "suspended", + "expired", + "failed" +]).annotate({ + "description": "The public lifecycle status of an execution environment.", + "identifier": "EnvironmentStatusResource" +}) +export type HostedEnvironmentFileResourceFileId = { + readonly "type": "file_id" + readonly "id": string + readonly "file_id": string + readonly "path": string + readonly "size_bytes": number +} +export const HostedEnvironmentFileResourceFileId = Schema.Struct({ + "type": Schema.Literal("file_id").annotate({ "description": "The type of the object. Always `file_id`." }), + "id": Schema.String.annotate({ "description": "The session-scoped ID of the file in the execution environment." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "file_id": Schema.String.annotate({ "description": "The ID of the uploaded file." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "path": Schema.String.annotate({ "description": "The file's absolute path inside the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "size_bytes": Schema.Number.annotate({ "description": "The decoded file size in bytes.", "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })) +}).annotate({ + "description": "A file copied from the OpenAI Files API.", + "identifier": "HostedEnvironmentFileResourceFileId" +}) +export type HostedEnvironmentFileResourceInline = { + readonly "type": "inline" + readonly "id": string + readonly "path": string + readonly "size_bytes": number +} +export const HostedEnvironmentFileResourceInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "id": Schema.String.annotate({ "description": "The session-scoped ID of the file in the execution environment." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "path": Schema.String.annotate({ "description": "The file's absolute path inside the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "size_bytes": Schema.Number.annotate({ "description": "The decoded file size in bytes.", "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })) +}).annotate({ + "description": "A file supplied inline when the session was created.", + "identifier": "HostedEnvironmentFileResourceInline" +}) +export type HostedSkillResourceSkillReference = { + readonly "type": "skill_reference" + readonly "skill_id": string + readonly "version": string + readonly "name": string + readonly "description": string +} +export const HostedSkillResourceSkillReference = Schema.Struct({ + "type": Schema.Literal("skill_reference").annotate({ + "description": "The type of the object. Always `skill_reference`." + }), + "skill_id": Schema.String.annotate({ "description": "The referenced skill ID." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "version": Schema.String.annotate({ "description": "The concrete skill version installed for this session." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.String.annotate({ "description": "The installed skill name." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "description": Schema.String.annotate({ "description": "The installed skill description." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A skill installed from the Skills API.", + "identifier": "HostedSkillResourceSkillReference" +}) +export type HostedSkillResourceInline = { + readonly "type": "inline" + readonly "name": string + readonly "description": string +} +export const HostedSkillResourceInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "name": Schema.String.annotate({ "description": "The installed skill name." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "description": Schema.String.annotate({ "description": "The installed skill description." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A skill installed from an inline ZIP archive.", + "identifier": "HostedSkillResourceInline" +}) +export type HostedPluginResourceInline = { + readonly "type": "inline" + readonly "name": string + readonly "description": string +} +export const HostedPluginResourceInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "name": Schema.String.annotate({ "description": "The installed plugin name." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "description": Schema.String.annotate({ "description": "The installed plugin description." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A plugin installed from an inline ZIP archive.", + "identifier": "HostedPluginResourceInline" +}) +export type ErrorBodyResource = { + readonly "type": string + readonly "code": string + readonly "message": string + readonly "param": string | null +} +export const ErrorBodyResource = Schema.Struct({ + "type": Schema.String.annotate({ "description": "The error type." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "code": Schema.String.annotate({ "description": "A machine-readable error code." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "message": Schema.String.annotate({ "description": "A human-readable error message." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "param": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The request parameter that caused the error, or null for a request-wide error." }) +}).annotate({ "description": "Details about an API error.", "identifier": "ErrorBodyResource" }) +export type EnvironmentPackagesParam = { + readonly "python"?: ReadonlyArray | null + readonly "system"?: ReadonlyArray | null + readonly "npm"?: ReadonlyArray | null +} +export const EnvironmentPackagesParam = Schema.Struct({ + "python": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Python packages to install. Defaults to an empty list." }) + ), + "system": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "System packages to install. Defaults to an empty list." }) + ), + "npm": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "npm packages to install globally. Defaults to an empty list." }) + ) +}).annotate({ + "description": "Packages to install in an OpenAI-hosted environment.", + "identifier": "EnvironmentPackagesParam" +}) +export type SetupCommandParam = { readonly "command": string; readonly "cwd"?: string | null } +export const SetupCommandParam = Schema.Struct({ + "command": Schema.String.annotate({ "description": "The shell command to execute." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(65536).annotate({ "expected": "a string with at most 65536 code points" })), + "cwd": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(4096).annotate({ "expected": "a string with at most 4096 code points" })), + Schema.Null + ]).annotate({ "description": "The absolute working directory. Defaults to `/workspace`." }) + ) +}).annotate({ + "description": "A confidential setup command executed before the hosted agent starts.", + "identifier": "SetupCommandParam" +}) +export type NetworkAccessParam = "enabled" | "disabled" | "restricted" +export const NetworkAccessParam = Schema.Literals(["enabled", "disabled", "restricted"]).annotate({ + "description": "The network access mode for an OpenAI-hosted environment.", + "identifier": "NetworkAccessParam" +}) +export type DesktopParam = { readonly "enabled": boolean } +export const DesktopParam = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Whether to provision the desktop and its browser proxy." }) +}).annotate({ "description": "Desktop configuration for an OpenAI-hosted environment.", "identifier": "DesktopParam" }) +export type HostedSkillParamSkillReference = { + readonly "type": "skill_reference" + readonly "skill_id": string + readonly "version"?: string | null +} +export const HostedSkillParamSkillReference = Schema.Struct({ + "type": Schema.Literal("skill_reference").annotate({ + "description": "The type of the object. Always `skill_reference`." + }), + "skill_id": Schema.String.annotate({ "description": "The ID of the skill created through `/v1/skills`." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "version": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The skill version, a positive integer or `latest`; omission selects the default." }) + ) +}).annotate({ + "description": "References a skill uploaded through the Skills API.", + "identifier": "HostedSkillParamSkillReference" +}) +export type InlineCapabilitySourceParamBase64 = { + readonly "type": "base64" + readonly "media_type": "application/zip" + readonly "data": string +} +export const InlineCapabilitySourceParamBase64 = Schema.Struct({ + "type": Schema.Literal("base64").annotate({ "description": "The type of the object. Always `base64`." }), + "media_type": Schema.Literal("application/zip").annotate({ + "description": "The archive media type, always `application/zip`." + }), + "data": Schema.String.annotate({ "description": "Standard-base64 encoded ZIP archive bytes." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(70254592).annotate({ "expected": "a string with at most 70254592 code points" })) +}).annotate({ + "description": "Provides ZIP bytes encoded with standard base64.", + "identifier": "InlineCapabilitySourceParamBase64" +}) +export type HostedEnvironmentFileParamFileId = { + readonly "type": "file_id" + readonly "file_id": string + readonly "path": string +} +export const HostedEnvironmentFileParamFileId = Schema.Struct({ + "type": Schema.Literal("file_id").annotate({ "description": "The type of the object. Always `file_id`." }), + "file_id": Schema.String.annotate({ "description": "The ID of the uploaded file." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })), + "path": Schema.String.annotate({ "description": "The absolute destination path inside `/workspace`." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(4096).annotate({ "expected": "a string with at most 4096 code points" })) +}).annotate({ + "description": "A file previously uploaded through the OpenAI Files API.", + "identifier": "HostedEnvironmentFileParamFileId" +}) +export type HostedEnvironmentFileParamInline = { + readonly "type": "inline" + readonly "data": string + readonly "path": string +} +export const HostedEnvironmentFileParamInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "data": Schema.String.annotate({ "description": "The standard-base64-encoded file contents." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(6990508).annotate({ "expected": "a string with at most 6990508 code points" })), + "path": Schema.String.annotate({ "description": "The absolute destination path inside `/workspace`." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(4096).annotate({ "expected": "a string with at most 4096 code points" })) +}).annotate({ + "description": "A file supplied directly as standard-base64 data.", + "identifier": "HostedEnvironmentFileParamInline" +}) +export type EnvironmentFilePageObjectResource = "page" +export const EnvironmentFilePageObjectResource = Schema.Literal("page").annotate({ + "description": "The object type for a page of files in an execution environment.", + "identifier": "EnvironmentFilePageObjectResource" +}) +export type EnvironmentFileResource = { + readonly "object": "agent.environment.file" + readonly "environment_id": string + readonly "path": string + readonly "size_bytes": number +} +export const EnvironmentFileResource = Schema.Struct({ + "object": Schema.Literal("agent.environment.file").annotate({ + "description": "The object type. Always `agent.environment.file`." + }), + "environment_id": Schema.String.annotate({ "description": "The ID of the environment containing this file." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "path": Schema.String.annotate({ "description": "The absolute file path inside the environment's workspace." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "size_bytes": Schema.Number.annotate({ "description": "The file size in bytes.", "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })) +}).annotate({ "description": "A live file in an execution environment.", "identifier": "EnvironmentFileResource" }) +export type SubagentObjectResource = "agent.session.subagent" +export const SubagentObjectResource = Schema.Literal("agent.session.subagent").annotate({ + "description": "The object type for a subagent.", + "identifier": "SubagentObjectResource" +}) +export type OutputTextResource = { readonly "type": "output_text"; readonly "text": string } +export const OutputTextResource = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ "description": "The content type. Always `output_text`." }), + "text": Schema.String.annotate({ "description": "The text produced by the agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "A text content part produced by the agent.", "identifier": "OutputTextResource" }) +export type EncryptedContentResource = { readonly "type": "encrypted_content"; readonly "encrypted_content": string } +export const EncryptedContentResource = Schema.Struct({ + "type": Schema.Literal("encrypted_content").annotate({ + "description": "The content type. Always `encrypted_content`." + }), + "encrypted_content": Schema.String.annotate({ "description": "The encrypted content payload." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "Encrypted content exchanged between agents.", "identifier": "EncryptedContentResource" }) +export type SubagentStatusResource = "active" | "closed" +export const SubagentStatusResource = Schema.Literals(["active", "closed"]).annotate({ + "description": "The current status of a subagent.", + "identifier": "SubagentStatusResource" +}) +export type SessionMessageRoleResource = "user" | "assistant" +export const SessionMessageRoleResource = Schema.Literals(["user", "assistant"]).annotate({ + "description": "The author of a session message.", + "identifier": "SessionMessageRoleResource" +}) +export type MessageContentResourceInputText = { readonly "type": "input_text"; readonly "text": string } +export const MessageContentResourceInputText = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the object. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text supplied by the user." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "Text supplied by the user.", "identifier": "MessageContentResourceInputText" }) +export type MessageContentResourceInputImage = { readonly "type": "input_image"; readonly "image_url": string } +export const MessageContentResourceInputImage = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ "description": "The type of the object. Always `input_image`." }), + "image_url": Schema.String.annotate({ + "description": "The URL of the image supplied by the user, which may be a base64-encoded data URL." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) +}).annotate({ "description": "An image supplied by the user.", "identifier": "MessageContentResourceInputImage" }) +export type MessageContentResourceOutputText = { readonly "type": "output_text"; readonly "text": string } +export const MessageContentResourceOutputText = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ "description": "The type of the object. Always `output_text`." }), + "text": Schema.String.annotate({ "description": "The text produced by the assistant." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "Text produced by the assistant.", "identifier": "MessageContentResourceOutputText" }) +export type OutputItemStatusResource = "in_progress" | "completed" | "incomplete" +export const OutputItemStatusResource = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of an agent output item.", + "identifier": "OutputItemStatusResource" +}) +export type MessagePhaseResource = "commentary" | "final_answer" +export const MessagePhaseResource = Schema.Literals(["commentary", "final_answer"]).annotate({ + "description": "The phase of an assistant message.", + "identifier": "MessagePhaseResource" +}) +export type SummaryTextResource = { readonly "type": "summary_text"; readonly "text": string } +export const SummaryTextResource = Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ "description": "The content type. Always `summary_text`." }), + "text": Schema.String.annotate({ "description": "The reasoning summary text." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "A reasoning summary content part.", "identifier": "SummaryTextResource" }) +export type FunctionCallStatusResource = "in_progress" | "completed" | "failed" | "incomplete" +export const FunctionCallStatusResource = Schema.Literals(["in_progress", "completed", "failed", "incomplete"]) + .annotate({ "description": "The status of a tool call.", "identifier": "FunctionCallStatusResource" }) +export type InputContentResourceInputText = { readonly "type": "input_text"; readonly "text": string } +export const InputContentResourceInputText = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the object. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text supplied to the agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "Text input recorded in a session item.", "identifier": "InputContentResourceInputText" }) +export type InputContentResourceInputImage = { readonly "type": "input_image"; readonly "image_url": string } +export const InputContentResourceInputImage = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ "description": "The type of the object. Always `input_image`." }), + "image_url": Schema.String.annotate({ + "description": "The URL of the image supplied to the agent, which may be a base64-encoded data URL." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) +}).annotate({ + "description": "Image input recorded in a session item.", + "identifier": "InputContentResourceInputImage" +}) +export type ComputerScreenshotResource = { readonly "type": "computer_screenshot"; readonly "image_url": string } +export const ComputerScreenshotResource = Schema.Struct({ + "type": Schema.Literal("computer_screenshot").annotate({ + "description": "The content type. Always `computer_screenshot`." + }), + "image_url": Schema.String.annotate({ "description": "The complete JPEG image as a base64 data URL." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A browser screenshot emitted by the model during computer use.", + "identifier": "ComputerScreenshotResource" +}) +export type BrowserAuthenticationFieldResource = { + readonly "id": string + readonly "label": string + readonly "type": string + readonly "required": boolean +} +export const BrowserAuthenticationFieldResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The field ID to submit as field_id in a fields entry." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "label": Schema.String.annotate({ "description": "The label to display beside the control." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "type": Schema.String.annotate({ "description": "The rendering type, such as email, password, or text." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "required": Schema.Boolean.annotate({ "description": "Whether this control requires a nonempty value." }) +}).annotate({ + "description": "A control in a registered browser-login form.", + "identifier": "BrowserAuthenticationFieldResource" +}) +export type BrowserAuthenticationOptionResource = { + readonly "id": string + readonly "label": string + readonly "field_ids": ReadonlyArray +} +export const BrowserAuthenticationOptionResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The option ID to submit as selected_option." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "label": Schema.String.annotate({ "description": "The method label to display." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "field_ids": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "IDs from the registered fields that this method accepts." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +}).annotate({ + "description": "A sign-in method and the fields that belong to it.", + "identifier": "BrowserAuthenticationOptionResource" +}) +export type ComputerUseApprovalResponseKindResourceBrowserAuthenticationSubmitResource = { + readonly "type": "browser_authentication" + readonly "action": "submit" + readonly "selected_option": string | null +} +export const ComputerUseApprovalResponseKindResourceBrowserAuthenticationSubmitResource = Schema.Struct({ + "type": Schema.Literal("browser_authentication"), + "action": Schema.Literal("submit"), + "selected_option": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The chosen sign-in method, or null when no options were offered." }) +}).annotate({ "identifier": "ComputerUseApprovalResponseKindResourceBrowserAuthenticationSubmitResource" }) +export type ComputerUseApprovalResponseKindResourceBrowserAuthenticationCancelResource = { + readonly "type": "browser_authentication" + readonly "action": "cancel" +} +export const ComputerUseApprovalResponseKindResourceBrowserAuthenticationCancelResource = Schema.Struct({ + "type": Schema.Literal("browser_authentication"), + "action": Schema.Literal("cancel") +}).annotate({ "identifier": "ComputerUseApprovalResponseKindResourceBrowserAuthenticationCancelResource" }) +export type WebSearchActionResourceSearch = { + readonly "type": "search" + readonly "query": string | null + readonly "queries": ReadonlyArray | null +} +export const WebSearchActionResourceSearch = Schema.Struct({ + "type": Schema.Literal("search").annotate({ "description": "The type of the object. Always `search`." }), + "query": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The search query, when a single query was used." }), + "queries": Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + Schema.Null + ]).annotate({ "description": "The search queries, when multiple queries were used." }) +}).annotate({ + "description": "A search query or group of search queries.", + "identifier": "WebSearchActionResourceSearch" +}) +export type WebSearchActionResourceOpenPage = { readonly "type": "open_page"; readonly "url": string | null } +export const WebSearchActionResourceOpenPage = Schema.Struct({ + "type": Schema.Literal("open_page").annotate({ "description": "The type of the object. Always `open_page`." }), + "url": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The URL of the page that was opened." }) +}).annotate({ "description": "Opens a web page.", "identifier": "WebSearchActionResourceOpenPage" }) +export type WebSearchActionResourceFindInPage = { + readonly "type": "find_in_page" + readonly "url": string | null + readonly "pattern": string | null +} +export const WebSearchActionResourceFindInPage = Schema.Struct({ + "type": Schema.Literal("find_in_page").annotate({ "description": "The type of the object. Always `find_in_page`." }), + "url": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The URL of the page that was searched." }), + "pattern": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The text pattern that was searched for." }) +}).annotate({ "description": "Finds text within a web page.", "identifier": "WebSearchActionResourceFindInPage" }) +export type WebSearchActionResourceOther = { readonly "type": "other" } +export const WebSearchActionResourceOther = Schema.Struct({ + "type": Schema.Literal("other").annotate({ "description": "The type of the object. Always `other`." }) +}).annotate({ "description": "Another web search action.", "identifier": "WebSearchActionResourceOther" }) +export type TurnObjectResource = "agent.session.turn" +export const TurnObjectResource = Schema.Literal("agent.session.turn").annotate({ + "description": "The object type for a turn.", + "identifier": "TurnObjectResource" +}) +export type TurnStatusResource = "queued" | "in_progress" | "waiting" | "completed" | "failed" | "cancelled" +export const TurnStatusResource = Schema.Literals([ + "queued", + "in_progress", + "waiting", + "completed", + "failed", + "cancelled" +]).annotate({ "description": "The current status of a turn.", "identifier": "TurnStatusResource" }) +export type SessionTurnErrorCodeResource = + | "context_length_exceeded" + | "session_budget_exceeded" + | "usage_limit_exceeded" + | "project_spend_limit_exceeded" + | "organization_spend_limit_exceeded" + | "organization_usage_limit_exceeded" + | "billing_not_active" + | "credit_balance_exhausted" + | "rate_limit_exceeded" + | "flex_unavailable" + | "server_overloaded" + | "cyber_policy" + | "misalignment_policy_violation" + | "connection_failed" + | "server_error" + | "authentication_error" + | "invalid_request" + | "resource_not_found" + | "sandbox_error" + | "executor_version_incompatible" + | "active_turn_not_steerable" + | "request_timeout" + | "internal_error" +export const SessionTurnErrorCodeResource = Schema.Literals([ + "context_length_exceeded", + "session_budget_exceeded", + "usage_limit_exceeded", + "project_spend_limit_exceeded", + "organization_spend_limit_exceeded", + "organization_usage_limit_exceeded", + "billing_not_active", + "credit_balance_exhausted", + "rate_limit_exceeded", + "flex_unavailable", + "server_overloaded", + "cyber_policy", + "misalignment_policy_violation", + "connection_failed", + "server_error", + "authentication_error", + "invalid_request", + "resource_not_found", + "sandbox_error", + "executor_version_incompatible", + "active_turn_not_steerable", + "request_timeout", + "internal_error" +]).annotate({ + "description": "Stable public categories for session request failures.", + "identifier": "SessionTurnErrorCodeResource" +}) +export type InputTokensDetailsResource_2 = { readonly "cached_tokens": number } +export const InputTokensDetailsResource_2 = Schema.Struct({ + "cached_tokens": Schema.Number.annotate({ + "description": "The number of input tokens retrieved from the prompt cache.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "A breakdown of input token usage for a session or turn.", + "identifier": "InputTokensDetailsResource-2" +}) +export type OutputTokensDetailsResource_2 = { readonly "reasoning_tokens": number } +export const OutputTokensDetailsResource_2 = Schema.Struct({ + "reasoning_tokens": Schema.Number.annotate({ + "description": "The number of output tokens used for reasoning.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "A breakdown of output token usage for a session or turn.", + "identifier": "OutputTokensDetailsResource-2" +}) +export type ReasoningEffortResource = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" +export const ReasoningEffortResource = Schema.Literals(["none", "minimal", "low", "medium", "high", "xhigh", "max"]) + .annotate({ + "description": "The amount of reasoning effort used by an agent.", + "identifier": "ReasoningEffortResource" + }) +export type ReasoningSummaryResource = "concise" | "detailed" | "auto" +export const ReasoningSummaryResource = Schema.Literals(["concise", "detailed", "auto"]).annotate({ + "description": "The reasoning summary format requested from an agent.", + "identifier": "ReasoningSummaryResource" +}) +export type TextFormatResourceText = { readonly "type": "text" } +export const TextFormatResourceText = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "The type of the object. Always `text`." }) +}).annotate({ + "description": "Generates ordinary text without a structured-output constraint.", + "identifier": "TextFormatResourceText" +}) +export type TextFormatResourceJsonSchema = { + readonly "type": "json_schema" + readonly "schema": { readonly [x: string]: Schema.Json } +} +export const TextFormatResourceJsonSchema = Schema.Struct({ + "type": Schema.Literal("json_schema").annotate({ "description": "The type of the object. Always `json_schema`." }), + "schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The JSON Schema that generated text must match." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) +}).annotate({ + "description": "Constrains generated text to a JSON Schema.", + "identifier": "TextFormatResourceJsonSchema" +}) +export type VerbosityResource = "low" | "medium" | "high" +export const VerbosityResource = Schema.Literals(["low", "medium", "high"]).annotate({ + "description": "The amount of text produced by an agent.", + "identifier": "VerbosityResource" +}) +export type ServiceTierResource = "auto" | "default" | "flex" | "priority" | "fast" | "ultrafast" +export const ServiceTierResource = Schema.Literals(["auto", "default", "flex", "priority", "fast", "ultrafast"]) + .annotate({ "description": "The service-tier policy configured for an agent.", "identifier": "ServiceTierResource" }) +export type PersistedAgentToolResourceFunction = { + readonly "type": "function" + readonly "name": string + readonly "description": string + readonly "parameters": { readonly [x: string]: Schema.Json } + readonly "defer_loading": boolean +} +export const PersistedAgentToolResourceFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of the object. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "description": Schema.String.annotate({ "description": "A description of what the function does." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "parameters": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON Schema object describing the function's arguments." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "defer_loading": Schema.Boolean.annotate({ + "description": "Whether the function is deferred and discovered through tool search." + }) +}).annotate({ + "description": "A function defined by the application.", + "identifier": "PersistedAgentToolResourceFunction" +}) +export type PersistedAgentToolResourceToolSearch = { readonly "type": "tool_search" } +export const PersistedAgentToolResourceToolSearch = Schema.Struct({ + "type": Schema.Literal("tool_search").annotate({ "description": "The type of the object. Always `tool_search`." }) +}).annotate({ + "description": "Discovers deferred function tools and loads them into the model context.", + "identifier": "PersistedAgentToolResourceToolSearch" +}) +export type PersistedAgentToolResourceProgrammaticToolCalling = { + readonly "type": "programmatic_tool_calling" + readonly "enabled": boolean +} +export const PersistedAgentToolResourceProgrammaticToolCalling = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The type of the object. Always `programmatic_tool_calling`." + }), + "enabled": Schema.Boolean.annotate({ "description": "Whether tools can be called from model-generated code." }) +}).annotate({ + "description": "Enables calling tools from model-generated code.", + "identifier": "PersistedAgentToolResourceProgrammaticToolCalling" +}) +export type PersistedMcpTransportResourceHttp = { + readonly "type": "http" + readonly "server_url": string + readonly "headers": { readonly [x: string]: string } +} +export const PersistedMcpTransportResourceHttp = Schema.Struct({ + "type": Schema.Literal("http").annotate({ "description": "The type of the object. Always `http`." }), + "server_url": Schema.String.annotate({ "description": "The URL of the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "headers": Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Non-secret HTTP headers sent to the MCP server." }).check( + Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) +}).annotate({ + "description": "Connects to an MCP server over HTTP.", + "identifier": "PersistedMcpTransportResourceHttp" +}) +export type PersistedMcpTransportResourceStdio = { + readonly "type": "stdio" + readonly "command": string + readonly "args": ReadonlyArray + readonly "cwd": string + readonly "env_vars": ReadonlyArray +} +export const PersistedMcpTransportResourceStdio = Schema.Struct({ + "type": Schema.Literal("stdio").annotate({ "description": "The type of the object. Always `stdio`." }), + "command": Schema.String.annotate({ "description": "The command used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "args": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Arguments passed to the MCP server command." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "cwd": Schema.String.annotate({ "description": "The working directory used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "env_vars": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Environment variable names inherited from the execution environment." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +}).annotate({ + "description": "Starts an MCP server as a local process.", + "identifier": "PersistedMcpTransportResourceStdio" +}) +export type McpConnectionOriginResource = "service" | "environment" +export const McpConnectionOriginResource = Schema.Literals(["service", "environment"]).annotate({ + "description": "Where outbound MCP HTTP connections originate.", + "identifier": "McpConnectionOriginResource" +}) +export type WebSearchModeResource = "disabled" | "cached" | "live" +export const WebSearchModeResource = Schema.Literals(["disabled", "cached", "live"]).annotate({ + "description": "The source used for web search results.", + "identifier": "WebSearchModeResource" +}) +export type WebSearchContextSizeResource = "low" | "medium" | "high" +export const WebSearchContextSizeResource = Schema.Literals(["low", "medium", "high"]).annotate({ + "description": "The amount of web search context made available to the model.", + "identifier": "WebSearchContextSizeResource" +}) +export type WebSearchLocationResource = { + readonly "country": string | null + readonly "region": string | null + readonly "city": string | null + readonly "timezone": string | null +} +export const WebSearchLocationResource = Schema.Struct({ + "country": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The two-letter ISO country code, such as `US`." }), + "region": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The region or state name." }), + "city": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The city name." }), + "timezone": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The IANA timezone, such as `America/Los_Angeles`." }) +}).annotate({ + "description": "Approximate user location used to localize web search results.", + "identifier": "WebSearchLocationResource" +}) +export type PersistedAgentToolResourceComputerUse = { + readonly "type": "computer_use" + readonly "include_screenshots": boolean +} +export const PersistedAgentToolResourceComputerUse = Schema.Struct({ + "type": Schema.Literal("computer_use").annotate({ "description": "The type of the object. Always `computer_use`." }), + "include_screenshots": Schema.Boolean.annotate({ + "description": "Whether computer tool outputs include screenshots." + }) +}).annotate({ + "description": "Browser use in an OpenAI-hosted session.", + "identifier": "PersistedAgentToolResourceComputerUse" +}) +export type MultiAgentConfigResource = { + readonly "enabled": boolean + readonly "max_concurrent_subagents": number | null +} +export const MultiAgentConfigResource = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Whether subagent tools are enabled. Defaults to false." }), + "max_concurrent_subagents": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check( + Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" }) + ), + Schema.Null + ]).annotate({ + "description": + "Maximum number of subagents that may run concurrently, or null when disabled. Defaults to 6 when enabled.", + "format": "int64" + }) +}).annotate({ + "description": "The resolved configuration for creating and coordinating subagents.", + "identifier": "MultiAgentConfigResource" +}) +export type ReasoningEffortParam = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" +export const ReasoningEffortParam = Schema.Literals(["none", "minimal", "low", "medium", "high", "xhigh", "max"]) + .annotate({ + "description": "The amount of reasoning effort the model should use.", + "identifier": "ReasoningEffortParam" + }) +export type ReasoningSummaryParam = "concise" | "detailed" | "auto" +export const ReasoningSummaryParam = Schema.Literals(["concise", "detailed", "auto"]).annotate({ + "description": "The reasoning summary format requested from the model.", + "identifier": "ReasoningSummaryParam" +}) +export type TextFormatParamText = { readonly "type": "text" } +export const TextFormatParamText = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "The type of the object. Always `text`." }) +}).annotate({ + "description": "Generates ordinary text without a structured-output constraint.", + "identifier": "TextFormatParamText" +}) +export type TextFormatParamJsonSchema = { + readonly "type": "json_schema" + readonly "schema": { readonly [x: string]: Schema.Json } +} +export const TextFormatParamJsonSchema = Schema.Struct({ + "type": Schema.Literal("json_schema").annotate({ "description": "The type of the object. Always `json_schema`." }), + "schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The JSON Schema that generated text must match." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) +}).annotate({ "description": "Constrains generated text to a JSON Schema.", "identifier": "TextFormatParamJsonSchema" }) +export type VerbosityParam = "low" | "medium" | "high" +export const VerbosityParam = Schema.Literals(["low", "medium", "high"]).annotate({ + "description": "The amount of text the model should produce.", + "identifier": "VerbosityParam" +}) +export type ServiceTierParam = "auto" | "default" | "flex" | "priority" | "fast" +export const ServiceTierParam = Schema.Literals(["auto", "default", "flex", "priority", "fast"]).annotate({ + "description": "The service tier used for model requests.", + "identifier": "ServiceTierParam" +}) +export type PersistedAgentToolConfigParamFunction = { + readonly "type": "function" + readonly "name": string + readonly "description": string + readonly "parameters": { readonly [x: string]: Schema.Json } + readonly "defer_loading"?: boolean +} +export const PersistedAgentToolConfigParamFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of the object. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "description": Schema.String.annotate({ "description": "A description of what the function does." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "parameters": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON Schema object describing the function's arguments." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this function is deferred and discovered through tool search. Defaults to `false`." + }) + ) +}).annotate({ + "description": "A function defined by the application.", + "identifier": "PersistedAgentToolConfigParamFunction" +}) +export type PersistedAgentToolConfigParamToolSearch = { readonly "type": "tool_search" } +export const PersistedAgentToolConfigParamToolSearch = Schema.Struct({ + "type": Schema.Literal("tool_search").annotate({ "description": "The type of the object. Always `tool_search`." }) +}).annotate({ + "description": "Discovers deferred function tools and loads them into the model context.", + "identifier": "PersistedAgentToolConfigParamToolSearch" +}) +export type PersistedAgentToolConfigParamProgrammaticToolCalling = { + readonly "type": "programmatic_tool_calling" + readonly "enabled"?: boolean +} +export const PersistedAgentToolConfigParamProgrammaticToolCalling = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The type of the object. Always `programmatic_tool_calling`." + }), + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether tools can be called from model-generated code. Defaults to `true`." + }) + ) +}).annotate({ + "description": "Enables calling tools from model-generated code.", + "identifier": "PersistedAgentToolConfigParamProgrammaticToolCalling" +}) +export type PersistedMcpTransportConfigParamHttp = { + readonly "type": "http" + readonly "server_url": string + readonly "headers"?: { readonly [x: string]: string } | null +} +export const PersistedMcpTransportConfigParamHttp = Schema.Struct({ + "type": Schema.Literal("http").annotate({ "description": "The type of the object. Always `http`." }), + "server_url": Schema.String.annotate({ "description": "The URL of the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "headers": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Non-secret HTTP headers sent to the MCP server." }) + ) +}).annotate({ + "description": "Connects to an MCP server over HTTP.", + "identifier": "PersistedMcpTransportConfigParamHttp" +}) +export type PersistedMcpTransportConfigParamStdio = { + readonly "type": "stdio" + readonly "command": string + readonly "args"?: ReadonlyArray | null + readonly "cwd": string + readonly "env_vars"?: ReadonlyArray | null +} +export const PersistedMcpTransportConfigParamStdio = Schema.Struct({ + "type": Schema.Literal("stdio").annotate({ "description": "The type of the object. Always `stdio`." }), + "command": Schema.String.annotate({ "description": "The command used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "args": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Arguments passed to the MCP server command." }) + ), + "cwd": Schema.String.annotate({ "description": "The working directory used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "env_vars": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Environment variable names to inherit from the selected execution environment." }) + ) +}).annotate({ + "description": "Starts an MCP server as a local process.", + "identifier": "PersistedMcpTransportConfigParamStdio" +}) +export type McpConnectionOriginParam = "service" | "environment" +export const McpConnectionOriginParam = Schema.Literals(["service", "environment"]).annotate({ + "description": "Where outbound MCP HTTP connections originate.", + "identifier": "McpConnectionOriginParam" +}) +export type WebSearchModeParam = "disabled" | "cached" | "live" +export const WebSearchModeParam = Schema.Literals(["disabled", "cached", "live"]).annotate({ + "description": "The source used for web search results.", + "identifier": "WebSearchModeParam" +}) +export type WebSearchContextSizeParam = "low" | "medium" | "high" +export const WebSearchContextSizeParam = Schema.Literals(["low", "medium", "high"]).annotate({ + "description": "The amount of web search context made available to the model.", + "identifier": "WebSearchContextSizeParam" +}) +export type WebSearchLocationParam = { + readonly "country"?: string | null + readonly "region"?: string | null + readonly "city"?: string | null + readonly "timezone"?: string | null +} +export const WebSearchLocationParam = Schema.Struct({ + "country": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The two-letter ISO country code, such as `US`." }) + ), + "region": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The region or state name." }) + ), + "city": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The city name." }) + ), + "timezone": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The IANA timezone, such as `America/Los_Angeles`." }) + ) +}).annotate({ + "description": "Approximate user location used to localize web search results.", + "identifier": "WebSearchLocationParam" +}) +export type PersistedAgentToolConfigParamComputerUse = { + readonly "type": "computer_use" + readonly "include_screenshots"?: boolean +} +export const PersistedAgentToolConfigParamComputerUse = Schema.Struct({ + "type": Schema.Literal("computer_use").annotate({ "description": "The type of the object. Always `computer_use`." }), + "include_screenshots": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether computer tool outputs include screenshots. Defaults to `false`." + }) + ) +}).annotate({ + "description": "Browser use in an OpenAI-hosted session.", + "identifier": "PersistedAgentToolConfigParamComputerUse" +}) +export type MultiAgentConfigCurrentParam = { readonly "enabled": boolean; readonly "max_concurrent_subagents"?: number } +export const MultiAgentConfigCurrentParam = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ "description": "Whether subagent tools are enabled." }), + "max_concurrent_subagents": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Maximum number of subagents that may run concurrently. Defaults to 6.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })) + ) +}).annotate({ + "description": "Explicit configuration for creating and coordinating subagents.", + "identifier": "MultiAgentConfigCurrentParam" +}) +export type DeletedAgentResource = { + readonly "id": string + readonly "object": "agent.deleted" + readonly "deleted": boolean +} +export const DeletedAgentResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.deleted").annotate({ "description": "The object type. Always `agent.deleted`." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the agent was deleted. Always `true`." }) +}).annotate({ "description": "A deleted reusable agent.", "identifier": "DeletedAgentResource" }) +export type EnvironmentPackagesResource = { + readonly "python": ReadonlyArray + readonly "system": ReadonlyArray + readonly "npm": ReadonlyArray +} +export const EnvironmentPackagesResource = Schema.Struct({ + "python": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).annotate({ "description": "Python packages installed in the environment." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })), + "system": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).annotate({ "description": "System packages installed in the environment." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })), + "npm": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).annotate({ "description": "npm packages installed globally in the environment." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })) +}).annotate({ + "description": "Packages installed in an OpenAI-hosted environment.", + "identifier": "EnvironmentPackagesResource" +}) +export type NetworkAccessResource = "enabled" | "disabled" | "restricted" +export const NetworkAccessResource = Schema.Literals(["enabled", "disabled", "restricted"]).annotate({ + "description": "The network access mode for an OpenAI-hosted environment.", + "identifier": "NetworkAccessResource" +}) +export type DesktopResource = { readonly "enabled": boolean } +export const DesktopResource = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ + "description": "Whether the environment provisions a desktop and browser proxy." + }) +}).annotate({ + "description": "The effective desktop configuration for an OpenAI-hosted environment.", + "identifier": "DesktopResource" +}) +export type HostedTemplateSkillResourceSkillReference = { + readonly "type": "skill_reference" + readonly "skill_id": string + readonly "version": string | null +} +export const HostedTemplateSkillResourceSkillReference = Schema.Struct({ + "type": Schema.Literal("skill_reference").annotate({ + "description": "The type of the object. Always `skill_reference`." + }), + "skill_id": Schema.String.annotate({ "description": "The referenced skill ID." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "version": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The requested version selector, including `latest`." }) +}).annotate({ + "description": "A skill resolved afresh from the Skills API whenever a session starts.", + "identifier": "HostedTemplateSkillResourceSkillReference" +}) +export type HostedTemplateSkillResourceInline = { + readonly "type": "inline" + readonly "name": string + readonly "description": string +} +export const HostedTemplateSkillResourceInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "name": Schema.String.annotate({ "description": "The skill name declared in `SKILL.md`." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "description": Schema.String.annotate({ "description": "The skill description declared in `SKILL.md`." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Safe metadata for an inline skill archive.", + "identifier": "HostedTemplateSkillResourceInline" +}) +export type HostedTemplateFileResourceFileId = { + readonly "type": "file_id" + readonly "file_id": string + readonly "path": string +} +export const HostedTemplateFileResourceFileId = Schema.Struct({ + "type": Schema.Literal("file_id").annotate({ "description": "The type of the object. Always `file_id`." }), + "file_id": Schema.String.annotate({ "description": "The ID of the uploaded file." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "path": Schema.String.annotate({ "description": "The file's absolute path inside the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A project-scoped Files API reference resolved separately for each session.", + "identifier": "HostedTemplateFileResourceFileId" +}) +export type HostedTemplateFileResourceInline = { + readonly "type": "inline" + readonly "path": string + readonly "size_bytes": number +} +export const HostedTemplateFileResourceInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "path": Schema.String.annotate({ "description": "The file's absolute path inside the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "size_bytes": Schema.Number.annotate({ + "description": "The decoded size of the inline file in bytes.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) +}).annotate({ + "description": "Metadata for confidential inline file contents.", + "identifier": "HostedTemplateFileResourceInline" +}) +export type DeletedEnvironmentTemplateResource = { + readonly "id": string + readonly "object": "agent.environment.template.deleted" + readonly "deleted": boolean +} +export const DeletedEnvironmentTemplateResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted environment template." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.environment.template.deleted").annotate({ + "description": "The object type. Always `agent.environment.template.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the environment template was deleted. Always `true`." }) +}).annotate({ + "description": "A deleted reusable environment template.", + "identifier": "DeletedEnvironmentTemplateResource" +}) +export type SessionSpendControlResource = { readonly "limit": number; readonly "consumed": number | null } +export const SessionSpendControlResource = Schema.Struct({ + "limit": Schema.Number.annotate({ "description": "The configured positive limit in USD cents.", "format": "int64" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check( + Schema.isLessThanOrEqualTo(4503599627370495).annotate({ + "expected": "a value less than or equal to 4503599627370495" + }) + ), + "consumed": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + Schema.Null + ]).annotate({ "description": "Best-effort recorded spend floored to whole USD cents, or null when unavailable." }) +}).annotate({ + "description": "Session-wide spending control, present only when a limit is configured.", + "identifier": "SessionSpendControlResource" +}) +export type SessionStatusResource = "idle" | "in_progress" | "requires_action" | "failed" +export const SessionStatusResource = Schema.Literals(["idle", "in_progress", "requires_action", "failed"]).annotate({ + "description": "The current status of a session.", + "identifier": "SessionStatusResource" +}) +export type ComputerUseApprovalRequestKindResourceBrowserOriginAccess = { + readonly "type": "browser_origin_access" + readonly "reason": string | null + readonly "origin": string +} +export const ComputerUseApprovalRequestKindResourceBrowserOriginAccess = Schema.Struct({ + "type": Schema.Literal("browser_origin_access").annotate({ + "description": "The type of the object. Always `browser_origin_access`." + }), + "reason": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The browser's explanation for this request, or null when unavailable." }), + "origin": Schema.String.annotate({ "description": "The origin the browser needs permission to access." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A browser origin awaiting the application's approval decision.", + "identifier": "ComputerUseApprovalRequestKindResourceBrowserOriginAccess" +}) +export type SessionRequiredActionResourceFunctionCall = { + readonly "type": "function_call" + readonly "turn_id": string + readonly "call_id": string + readonly "name": string + readonly "arguments": Schema.Json +} +export const SessionRequiredActionResourceFunctionCall = Schema.Struct({ + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the object. Always `function_call`." + }), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that requested the function call." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "call_id": Schema.String.annotate({ "description": "The ID to include when submitting the function result." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.String.annotate({ "description": "The function name." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "arguments": Schema.Json.annotate({ "expected": "JSON value", "description": "The arguments supplied by the model." }) +}).annotate({ + "description": "Run a function tool and submit its result.", + "identifier": "SessionRequiredActionResourceFunctionCall" +}) +export type SessionRequiredActionResourceEnvironmentConnection = { + readonly "type": "environment_connection" + readonly "environment_id": string +} +export const SessionRequiredActionResourceEnvironmentConnection = Schema.Struct({ + "type": Schema.Literal("environment_connection").annotate({ + "description": "The type of the object. Always `environment_connection`." + }), + "environment_id": Schema.String.annotate({ "description": "The ID of the environment to reconnect." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Reconnect a session environment.", + "identifier": "SessionRequiredActionResourceEnvironmentConnection" +}) +export type AgentToolResourceFunction = { + readonly "type": "function" + readonly "name": string + readonly "description": string + readonly "parameters": { readonly [x: string]: Schema.Json } + readonly "defer_loading": boolean +} +export const AgentToolResourceFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of the object. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "description": Schema.String.annotate({ "description": "A description of what the function does." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "parameters": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON Schema object describing the function's arguments." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "defer_loading": Schema.Boolean.annotate({ + "description": "Whether the function is deferred and discovered through tool search." + }) +}).annotate({ "description": "A function defined by the application.", "identifier": "AgentToolResourceFunction" }) +export type AgentToolResourceProgrammaticToolCalling = { + readonly "type": "programmatic_tool_calling" + readonly "enabled": boolean +} +export const AgentToolResourceProgrammaticToolCalling = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The type of the object. Always `programmatic_tool_calling`." + }), + "enabled": Schema.Boolean.annotate({ "description": "Whether tools can be called from model-generated code." }) +}).annotate({ + "description": "Enables calling tools from model-generated code.", + "identifier": "AgentToolResourceProgrammaticToolCalling" +}) +export type McpTransportResourceHttp = { readonly "type": "http"; readonly "server_url": string } +export const McpTransportResourceHttp = Schema.Struct({ + "type": Schema.Literal("http").annotate({ "description": "The type of the object. Always `http`." }), + "server_url": Schema.String.annotate({ "description": "The URL of the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "Connects to an MCP server over HTTP.", "identifier": "McpTransportResourceHttp" }) +export type McpTransportResourceStdio = { + readonly "type": "stdio" + readonly "command": string + readonly "args": ReadonlyArray + readonly "cwd": string + readonly "env_vars": ReadonlyArray +} +export const McpTransportResourceStdio = Schema.Struct({ + "type": Schema.Literal("stdio").annotate({ "description": "The type of the object. Always `stdio`." }), + "command": Schema.String.annotate({ "description": "The command used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "args": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Arguments passed to the MCP server command." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "cwd": Schema.String.annotate({ "description": "The working directory used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "env_vars": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Environment variable names inherited from the execution environment." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +}).annotate({ "description": "Starts an MCP server as a local process.", "identifier": "McpTransportResourceStdio" }) +export type AgentToolResourceComputerUse = { readonly "type": "computer_use"; readonly "include_screenshots": boolean } +export const AgentToolResourceComputerUse = Schema.Struct({ + "type": Schema.Literal("computer_use").annotate({ "description": "The type of the object. Always `computer_use`." }), + "include_screenshots": Schema.Boolean.annotate({ + "description": "Whether computer tool outputs include screenshots." + }) +}).annotate({ "description": "Browser use in an OpenAI-hosted session.", "identifier": "AgentToolResourceComputerUse" }) +export type EnvironmentResourceNone = { readonly "type": "none" } +export const EnvironmentResourceNone = Schema.Struct({ + "type": Schema.Literal("none").annotate({ "description": "The type of the object. Always `none`." }) +}).annotate({ + "description": "The session talks to CCA without selecting or provisioning an execution environment.", + "identifier": "EnvironmentResourceNone" +}) +export type ContainerSizeResource = "small" | "medium" | "large" +export const ContainerSizeResource = Schema.Literals(["small", "medium", "large"]).annotate({ + "description": "The effective CPU and memory tier of an OpenAI-hosted environment.", + "identifier": "ContainerSizeResource" +}) +export type EnvironmentResourceSelfHosted = { + readonly "type": "self_hosted" + readonly "remote_url": string + readonly "id": string + readonly "workspace_directory": string + readonly "capability_directories": ReadonlyArray +} +export const EnvironmentResourceSelfHosted = Schema.Struct({ + "type": Schema.Literal("self_hosted").annotate({ "description": "The type of the object. Always `self_hosted`." }), + "remote_url": Schema.String.annotate({ + "description": "Pass this URL unchanged to `codex exec-server --remote` when connecting this environment." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "id": Schema.String.annotate({ "description": "The public ID of the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "workspace_directory": Schema.String.annotate({ + "description": "The absolute project directory inside the environment. Defaults to `/workspace`." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "capability_directories": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Directories that contain capabilities exposed to the agent." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +}).annotate({ + "description": "An environment hosted by the application.", + "identifier": "EnvironmentResourceSelfHosted" +}) +export type SessionSpendControlParam = { readonly "limit": number | null } +export const SessionSpendControlParam = Schema.Struct({ + "limit": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check( + Schema.isLessThanOrEqualTo(4503599627370495).annotate({ + "expected": "a value less than or equal to 4503599627370495" + }) + ), + Schema.Null + ]).annotate({ "description": "Positive USD cents, or null to remove the limit.", "format": "int64" }) +}).annotate({ + "description": "Sets the session-wide limit without changing recorded spend.", + "identifier": "SessionSpendControlParam" +}) +export type AgentToolConfigParamFunction = { + readonly "type": "function" + readonly "name": string + readonly "description": string + readonly "parameters": { readonly [x: string]: Schema.Json } + readonly "defer_loading"?: boolean +} +export const AgentToolConfigParamFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of the object. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "description": Schema.String.annotate({ "description": "A description of what the function does." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "parameters": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON Schema object describing the function's arguments." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this function is deferred and discovered through tool search. Defaults to `false`." + }) + ) +}).annotate({ "description": "A function defined by the application.", "identifier": "AgentToolConfigParamFunction" }) +export type AgentToolConfigParamToolSearch = { readonly "type": "tool_search" } +export const AgentToolConfigParamToolSearch = Schema.Struct({ + "type": Schema.Literal("tool_search").annotate({ "description": "The type of the object. Always `tool_search`." }) +}).annotate({ + "description": "Discovers deferred function tools and loads them into the model context.", + "identifier": "AgentToolConfigParamToolSearch" +}) +export type AgentToolConfigParamProgrammaticToolCalling = { + readonly "type": "programmatic_tool_calling" + readonly "enabled"?: boolean +} +export const AgentToolConfigParamProgrammaticToolCalling = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The type of the object. Always `programmatic_tool_calling`." + }), + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether tools can be called from model-generated code. Defaults to `true`." + }) + ) +}).annotate({ + "description": "Enables calling tools from model-generated code.", + "identifier": "AgentToolConfigParamProgrammaticToolCalling" +}) +export type McpTransportConfigParamHttp = { + readonly "type": "http" + readonly "server_url": string + readonly "authorization"?: string | null + readonly "headers"?: { readonly [x: string]: string } | null +} +export const McpTransportConfigParamHttp = Schema.Struct({ + "type": Schema.Literal("http").annotate({ "description": "The type of the object. Always `http`." }), + "server_url": Schema.String.annotate({ "description": "The URL of the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "authorization": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The authorization value sent to the MCP server, if any." }) + ), + "headers": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Additional HTTP headers sent to the MCP server." }) + ) +}).annotate({ "description": "Connects to an MCP server over HTTP.", "identifier": "McpTransportConfigParamHttp" }) +export type McpTransportConfigParamStdio = { + readonly "type": "stdio" + readonly "command": string + readonly "args"?: ReadonlyArray | null + readonly "cwd": string + readonly "env"?: { readonly [x: string]: string } | null + readonly "env_vars"?: ReadonlyArray | null +} +export const McpTransportConfigParamStdio = Schema.Struct({ + "type": Schema.Literal("stdio").annotate({ "description": "The type of the object. Always `stdio`." }), + "command": Schema.String.annotate({ "description": "The command used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "args": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Arguments passed to the MCP server command." }) + ), + "cwd": Schema.String.annotate({ "description": "The working directory used to start the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "env": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Environment variables set for the MCP server process." }) + ), + "env_vars": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Environment variable names to inherit from the selected execution environment." }) + ) +}).annotate({ "description": "Starts an MCP server as a local process.", "identifier": "McpTransportConfigParamStdio" }) +export type AgentToolConfigParamComputerUse = { + readonly "type": "computer_use" + readonly "include_screenshots"?: boolean +} +export const AgentToolConfigParamComputerUse = Schema.Struct({ + "type": Schema.Literal("computer_use").annotate({ "description": "The type of the object. Always `computer_use`." }), + "include_screenshots": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether computer tool outputs include screenshots. Defaults to `false`." + }) + ) +}).annotate({ + "description": "Browser use in an OpenAI-hosted session.", + "identifier": "AgentToolConfigParamComputerUse" +}) +export type EnvironmentParamNone = { readonly "type": "none" } +export const EnvironmentParamNone = Schema.Struct({ + "type": Schema.Literal("none").annotate({ "description": "The type of the object. Always `none`." }) +}).annotate({ "description": "Runs the agent without an execution environment.", "identifier": "EnvironmentParamNone" }) +export type ContainerSizeParam = "small" | "medium" | "large" +export const ContainerSizeParam = Schema.Literals(["small", "medium", "large"]).annotate({ + "description": "The CPU and memory tier for a new OpenAI-hosted session environment.", + "identifier": "ContainerSizeParam" +}) +export type EnvironmentParamSelfHosted = { + readonly "type": "self_hosted" + readonly "capability_directories"?: ReadonlyArray | null + readonly "workspace_directory": string +} +export const EnvironmentParamSelfHosted = Schema.Struct({ + "type": Schema.Literal("self_hosted").annotate({ "description": "The type of the object. Always `self_hosted`." }), + "capability_directories": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ + "description": "Directories that contain capabilities exposed to the agent. Defaults to an empty list." + }) + ), + "workspace_directory": Schema.String.annotate({ + "description": "Absolute project directory inside the self-hosted environment." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) +}).annotate({ + "description": "An application-hosted environment configured inline.", + "identifier": "EnvironmentParamSelfHosted" +}) +export type InputContentParamInputText = { readonly "type": "input_text"; readonly "text": string } +export const InputContentParamInputText = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the object. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text sent to the model." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) +}).annotate({ "description": "Text input to the model.", "identifier": "InputContentParamInputText" }) +export type InputContentParamInputImage = { readonly "type": "input_image"; readonly "image_url": string } +export const InputContentParamInputImage = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ "description": "The type of the object. Always `input_image`." }), + "image_url": Schema.String.annotate({ "description": "The URL of the image sent to the model." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) +}).annotate({ "description": "Image input to the model.", "identifier": "InputContentParamInputImage" }) +export type SessionErrorResource = { + readonly "type": string + readonly "code": string | null + readonly "message": string + readonly "param": string | null +} +export const SessionErrorResource = Schema.Struct({ + "type": Schema.String.annotate({ "description": "The error type." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "code": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The machine-readable error code, if any." }), + "message": Schema.String.annotate({ "description": "A customer-safe explanation of the error." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "param": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The request parameter associated with the error, if any." }) +}).annotate({ + "description": "An error payload with the same public fields as Responses API streaming errors.", + "identifier": "SessionErrorResource" +}) +export type SessionEnvironmentStatusResource = + | "pending" + | "ready" + | "connected" + | "disconnected" + | "suspended" + | "expired" + | "failed" +export const SessionEnvironmentStatusResource = Schema.Literals([ + "pending", + "ready", + "connected", + "disconnected", + "suspended", + "expired", + "failed" +]).annotate({ + "description": "The connection status of a session environment.", + "identifier": "SessionEnvironmentStatusResource" +}) +export type SessionEnvironmentErrorResource = { + readonly "type": string + readonly "code": string + readonly "message": string +} +export const SessionEnvironmentErrorResource = Schema.Struct({ + "type": Schema.String.annotate({ "description": "The error type." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "code": Schema.String.annotate({ "description": "A machine-readable error code." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "message": Schema.String.annotate({ "description": "A human-readable error message." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "An error reported while preparing a session environment.", + "identifier": "SessionEnvironmentErrorResource" +}) +export type SessionEventAgentSessionEnvironmentReset = { + readonly "type": "agent.session.environment.reset" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment_id": string + readonly "reset_count": number +} +export const SessionEventAgentSessionEnvironmentReset = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.reset").annotate({ + "description": "The type of the object. Always `agent.session.environment.reset`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The associated turn, when applicable." }), + "environment_id": Schema.String.annotate({ + "description": "The stable environment ID, retained across sandbox replacements." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "reset_count": Schema.Number.annotate({ + "description": "Monotonically increasing reset number. Repeated notifications share this number.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ) +}).annotate({ + "description": + "Emitted after a hosted sandbox is replaced. Conversation history survives; changes to the previous sandbox's files and processes do not.", + "identifier": "SessionEventAgentSessionEnvironmentReset" +}) +export type SessionEventAgentOutputCommandExecutionOutputDelta = { + readonly "type": "agent.output.command_execution_output.delta" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "delta": string +} +export const SessionEventAgentOutputCommandExecutionOutputDelta = Schema.Struct({ + "type": Schema.Literal("agent.output.command_execution_output.delta").annotate({ + "description": "The type of the object. Always `agent.output.command_execution_output.delta`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the command execution item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "delta": Schema.String.annotate({ "description": "The output text that was appended." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Emitted when command execution produces an output delta.", + "identifier": "SessionEventAgentOutputCommandExecutionOutputDelta" +}) +export type SessionEventAgentSessionTurnOutputTextDelta = { + readonly "type": "agent.session.turn.output_text.delta" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string +} +export const SessionEventAgentSessionTurnOutputTextDelta = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.output_text.delta").annotate({ + "description": "The type of the object. Always `agent.session.turn.output_text.delta`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the message item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part in the message.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "delta": Schema.String.annotate({ "description": "The text that was appended." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Emitted when text is appended to an output text content part.", + "identifier": "SessionEventAgentSessionTurnOutputTextDelta" +}) +export type SessionEventAgentSessionTurnOutputTextDone = { + readonly "type": "agent.session.turn.output_text.done" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "text": string +} +export const SessionEventAgentSessionTurnOutputTextDone = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.output_text.done").annotate({ + "description": "The type of the object. Always `agent.session.turn.output_text.done`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the message item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part in the message.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "text": Schema.String.annotate({ "description": "The complete output text." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Emitted when an output text content part is complete.", + "identifier": "SessionEventAgentSessionTurnOutputTextDone" +}) +export type SessionEventAgentSessionTurnReasoningSummaryTextDelta = { + readonly "type": "agent.session.turn.reasoning_summary_text.delta" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "delta": string +} +export const SessionEventAgentSessionTurnReasoningSummaryTextDelta = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.reasoning_summary_text.delta").annotate({ + "description": "The type of the object. Always `agent.session.turn.reasoning_summary_text.delta`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the reasoning item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary content part.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "delta": Schema.String.annotate({ "description": "The summary text that was appended." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Emitted when text is appended to a reasoning summary.", + "identifier": "SessionEventAgentSessionTurnReasoningSummaryTextDelta" +}) +export type SessionEventAgentSessionTurnReasoningSummaryTextDone = { + readonly "type": "agent.session.turn.reasoning_summary_text.done" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "text": string +} +export const SessionEventAgentSessionTurnReasoningSummaryTextDone = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.reasoning_summary_text.done").annotate({ + "description": "The type of the object. Always `agent.session.turn.reasoning_summary_text.done`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the reasoning item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary content part.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "text": Schema.String.annotate({ "description": "The complete reasoning summary text." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "Emitted when a reasoning summary content part is complete.", + "identifier": "SessionEventAgentSessionTurnReasoningSummaryTextDone" +}) +export type DeletedSessionResource = { + readonly "id": string + readonly "object": "agent.session.deleted" + readonly "deleted": boolean +} +export const DeletedSessionResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted session." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.session.deleted").annotate({ + "description": "The object type. Always `agent.session.deleted`." + }), + "deleted": Schema.Boolean.annotate({ + "description": + "Whether the session has been removed from the public API. Always `true`. Physical cleanup may still be in progress." + }) +}).annotate({ + "description": "A Managed Agents session removed from the public API. Physical cleanup may continue asynchronously.", + "identifier": "DeletedSessionResource" +}) +export type SessionArtifactResource = { + readonly "id": string + readonly "object": "agent.session.artifact" + readonly "session_id": string + readonly "environment_id": string + readonly "turn_id": string + readonly "path": string + readonly "size_bytes": number + readonly "created_at": number +} +export const SessionArtifactResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The immutable artifact ID." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.session.artifact").annotate({ + "description": "The object type. Always `agent.session.artifact`." + }), + "session_id": Schema.String.annotate({ "description": "The ID of the session that owns the artifact." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "environment_id": Schema.String.annotate({ "description": "The ID of the environment that produced the artifact." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "turn_id": Schema.String.annotate({ "description": "The ID of the completed turn that published the artifact." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "path": Schema.String.annotate({ "description": "The original absolute file path in the execution environment." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "size_bytes": Schema.Number.annotate({ "description": "The immutable artifact size in bytes.", "format": "int64" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the artifact was published.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "An immutable file published by a completed hosted session turn.", + "identifier": "SessionArtifactResource" +}) +export type DeletedSessionArtifactResource = { + readonly "id": string + readonly "object": "agent.session.artifact.deleted" + readonly "deleted": boolean +} +export const DeletedSessionArtifactResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted session artifact." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.session.artifact.deleted").annotate({ + "description": "The object type. Always `agent.session.artifact.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the session artifact was deleted. Always `true`." }) +}).annotate({ + "description": "Confirmation that an immutable session artifact was deleted.", + "identifier": "DeletedSessionArtifactResource" +}) +export type BrowserAuthenticationFieldValueParam = { readonly "field_id": string; readonly "value": string } +export const BrowserAuthenticationFieldValueParam = Schema.Struct({ + "field_id": Schema.String.annotate({ "description": "The field ID from the required action." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "value": Schema.String.annotate({ "description": "The value to enter into the registered control." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(16384).annotate({ "expected": "a string with at most 16384 code points" })) +}).annotate({ + "description": "One user-entered value, including non-password fields such as an email address.", + "identifier": "BrowserAuthenticationFieldValueParam" +}) +export type BrowserOriginAccessDecisionParam = "approve" | "deny" | "cancel" +export const BrowserOriginAccessDecisionParam = Schema.Literals(["approve", "deny", "cancel"]).annotate({ + "description": "The user's decision for a browser origin access request.", + "identifier": "BrowserOriginAccessDecisionParam" +}) +export type SessionInputParamAgentSessionInputCancel = { readonly "type": "agent.session.input.cancel" } +export const SessionInputParamAgentSessionInputCancel = Schema.Struct({ + "type": Schema.Literal("agent.session.input.cancel").annotate({ + "description": "The type of the object. Always `agent.session.input.cancel`." + }) +}).annotate({ + "description": "Cancels the session's active turn.", + "identifier": "SessionInputParamAgentSessionInputCancel" +}) +export type SessionTurnTraceResource = { + readonly "id": string + readonly "object": "agent.session.trace" + readonly "session_id": string + readonly "created_at": number + readonly "otlp": { readonly [x: string]: Schema.Json } +} +export const SessionTurnTraceResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The root turn ID. Use this ID as the pagination anchor." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.session.trace").annotate({ + "description": "The object type, which is always `agent.session.trace`." + }), + "session_id": Schema.String.annotate({ "description": "The session that owns this trace." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp in seconds when the root turn was created.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "otlp": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "An OTLP JSON ExportTraceServiceRequest containing resourceSpans. Only currently published data is returned; later trace updates are not awaited." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) +}).annotate({ "identifier": "SessionTurnTraceResource" }) +export type VaultStatusParam = "active" | "archived" +export const VaultStatusParam = Schema.Literals(["active", "archived"]).annotate({ + "description": "Whether a vault or credential is active or archived.", + "identifier": "VaultStatusParam" +}) +export type VaultResource = { + readonly "id": string + readonly "object": "vault" + readonly "name": string | null + readonly "metadata": { readonly [x: string]: string } + readonly "created_at": number +} +export const VaultResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the vault." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("vault").annotate({ "description": "The object type. Always `vault`." }), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The human-readable name of the vault, if set." }), + "metadata": Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Key-value pairs associated with the vault, such as an application or team identifier." }) + .check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the vault was created.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "A collection of credentials for MCP servers and OpenAI-hosted environments.", + "identifier": "VaultResource" +}) +export type CreateVaultParams = { + readonly "name"?: string + readonly "metadata"?: { readonly [x: string]: string } | null +} +export const CreateVaultParams = Schema.Struct({ + "name": Schema.optionalKey( + Schema.String.annotate({ + "description": "The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after trimming." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) + ), + "metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ + "description": "Key-value pairs to associate with the vault, such as an application or team identifier." + }) + ) +}).annotate({ + "description": "Parameters for creating a vault to store credentials used by agent tools.", + "identifier": "CreateVaultParams" +}) +export type UpdateVaultParams = { + readonly "metadata"?: { readonly [x: string]: string } + readonly "name"?: string | null +} +export const UpdateVaultParams = Schema.Struct({ + "metadata": Schema.optionalKey( + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).annotate({ + "description": + "Replaces all metadata. Omit to leave unchanged, or pass {} to clear it. Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) + ), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + Schema.Null + ]).annotate({ + "description": + "A replacement name. Omit to leave unchanged, or pass null to clear it. The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after trimming." + }) + ) +}).annotate({ "description": "Fields to replace on an active vault.", "identifier": "UpdateVaultParams" }) +export type DeletedVaultResource = { + readonly "id": string + readonly "object": "vault.deleted" + readonly "deleted": boolean +} +export const DeletedVaultResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted vault." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("vault.deleted").annotate({ "description": "The object type. Always `vault.deleted`." }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the resource was deleted. Always `true`." }) +}).annotate({ "description": "Confirmation that a vault was deleted.", "identifier": "DeletedVaultResource" }) +export type McpOauthTokenEndpointAuthResourceNone = { readonly "type": "none" } +export const McpOauthTokenEndpointAuthResourceNone = Schema.Struct({ + "type": Schema.Literal("none").annotate({ "description": "The type of the object. Always `none`." }) +}).annotate({ + "description": "Sends the client ID without a client secret.", + "identifier": "McpOauthTokenEndpointAuthResourceNone" +}) +export type McpOauthTokenEndpointAuthResourceClientSecretBasic = { readonly "type": "client_secret_basic" } +export const McpOauthTokenEndpointAuthResourceClientSecretBasic = Schema.Struct({ + "type": Schema.Literal("client_secret_basic").annotate({ + "description": "The type of the object. Always `client_secret_basic`." + }) +}).annotate({ + "description": "Sends the client ID and secret using HTTP Basic authentication.", + "identifier": "McpOauthTokenEndpointAuthResourceClientSecretBasic" +}) +export type McpOauthTokenEndpointAuthResourceClientSecretPost = { readonly "type": "client_secret_post" } +export const McpOauthTokenEndpointAuthResourceClientSecretPost = Schema.Struct({ + "type": Schema.Literal("client_secret_post").annotate({ + "description": "The type of the object. Always `client_secret_post`." + }) +}).annotate({ + "description": "Sends the client ID and secret in the token request body.", + "identifier": "McpOauthTokenEndpointAuthResourceClientSecretPost" +}) +export type VaultCredentialAuthResourceStaticBearer = { + readonly "type": "static_bearer" + readonly "mcp_server_url": string +} +export const VaultCredentialAuthResourceStaticBearer = Schema.Struct({ + "type": Schema.Literal("static_bearer").annotate({ + "description": "The type of the object. Always `static_bearer`." + }), + "mcp_server_url": Schema.String.annotate({ "description": "The HTTPS MCP server URL authorized by this credential." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) +}).annotate({ + "description": "Metadata for a bearer-token credential, without automatic OAuth refresh.", + "identifier": "VaultCredentialAuthResourceStaticBearer" +}) +export type VaultCredentialNetworkingResourceUnrestricted = { readonly "type": "unrestricted" } +export const VaultCredentialNetworkingResourceUnrestricted = Schema.Struct({ + "type": Schema.Literal("unrestricted").annotate({ "description": "The type of the object. Always `unrestricted`." }) +}).annotate({ + "description": + "Allows substitution for destinations permitted by the environment network policy. Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`.", + "identifier": "VaultCredentialNetworkingResourceUnrestricted" +}) +export type VaultCredentialNetworkingResourceLimited = { + readonly "type": "limited" + readonly "allowed_hosts": ReadonlyArray +} +export const VaultCredentialNetworkingResourceLimited = Schema.Struct({ + "type": Schema.Literal("limited").annotate({ "description": "The type of the object. Always `limited`." }), + "allowed_hosts": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ + "description": + "The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses are not supported." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" }) + ) +}).annotate({ + "description": + "Allows substitution only for the listed hosts. The environment network policy must also allow these hosts.", + "identifier": "VaultCredentialNetworkingResourceLimited" +}) +export type CreateMcpOauthTokenEndpointAuthParamNone = { readonly "type": "none" } +export const CreateMcpOauthTokenEndpointAuthParamNone = Schema.Struct({ + "type": Schema.Literal("none").annotate({ "description": "The type of the object. Always `none`." }) +}).annotate({ + "description": "Sends the client ID without a client secret.", + "identifier": "CreateMcpOauthTokenEndpointAuthParamNone" +}) +export type CreateMcpOauthTokenEndpointAuthParamClientSecretBasic = { + readonly "type": "client_secret_basic" + readonly "client_secret": string +} +export const CreateMcpOauthTokenEndpointAuthParamClientSecretBasic = Schema.Struct({ + "type": Schema.Literal("client_secret_basic").annotate({ + "description": "The type of the object. Always `client_secret_basic`." + }), + "client_secret": Schema.String.annotate({ + "description": "The OAuth client secret to store. Never returned in credential resources." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) +}).annotate({ + "description": "Sends the client ID and secret using HTTP Basic authentication.", + "identifier": "CreateMcpOauthTokenEndpointAuthParamClientSecretBasic" +}) +export type CreateMcpOauthTokenEndpointAuthParamClientSecretPost = { + readonly "type": "client_secret_post" + readonly "client_secret": string +} +export const CreateMcpOauthTokenEndpointAuthParamClientSecretPost = Schema.Struct({ + "type": Schema.Literal("client_secret_post").annotate({ + "description": "The type of the object. Always `client_secret_post`." + }), + "client_secret": Schema.String.annotate({ + "description": "The OAuth client secret to store. Never returned in credential resources." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) +}).annotate({ + "description": "Sends the client ID and secret in the token request body.", + "identifier": "CreateMcpOauthTokenEndpointAuthParamClientSecretPost" +}) +export type CreateVaultCredentialAuthParamStaticBearer = { + readonly "type": "static_bearer" + readonly "mcp_server_url": string + readonly "token": string +} +export const CreateVaultCredentialAuthParamStaticBearer = Schema.Struct({ + "type": Schema.Literal("static_bearer").annotate({ + "description": "The type of the object. Always `static_bearer`." + }), + "mcp_server_url": Schema.String.annotate({ "description": "The HTTPS MCP server URL authorized by this credential." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "token": Schema.String.annotate({ + "description": "The bearer token to store. This secret is never returned in credential resources." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) +}).annotate({ + "description": "A bearer token for an MCP server, without automatic OAuth refresh.", + "identifier": "CreateVaultCredentialAuthParamStaticBearer" +}) +export type VaultCredentialNetworkingParamUnrestricted = { readonly "type": "unrestricted" } +export const VaultCredentialNetworkingParamUnrestricted = Schema.Struct({ + "type": Schema.Literal("unrestricted").annotate({ "description": "The type of the object. Always `unrestricted`." }) +}).annotate({ + "description": + "Allows substitution for destinations permitted by the environment network policy. Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`.", + "identifier": "VaultCredentialNetworkingParamUnrestricted" +}) +export type VaultCredentialNetworkingParamLimited = { + readonly "type": "limited" + readonly "allowed_hosts": ReadonlyArray +} +export const VaultCredentialNetworkingParamLimited = Schema.Struct({ + "type": Schema.Literal("limited").annotate({ "description": "The type of the object. Always `limited`." }), + "allowed_hosts": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).annotate({ + "description": + "The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses are not supported." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" }) + ) +}).annotate({ + "description": + "Allows substitution only for the listed hosts. The environment network policy must also allow these hosts.", + "identifier": "VaultCredentialNetworkingParamLimited" +}) +export type RotateMcpOauthTokenEndpointAuthParamClientSecretBasic = { + readonly "type": "client_secret_basic" + readonly "client_secret"?: string | null +} +export const RotateMcpOauthTokenEndpointAuthParamClientSecretBasic = Schema.Struct({ + "type": Schema.Literal("client_secret_basic").annotate({ + "description": "The type of the object. Always `client_secret_basic`." + }), + "client_secret": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "The replacement OAuth client secret. Omit or pass `null` to keep the stored secret. This secret is never returned in resources." + }) + ) +}).annotate({ + "description": "Updates credentials sent using HTTP Basic authentication.", + "identifier": "RotateMcpOauthTokenEndpointAuthParamClientSecretBasic" +}) +export type RotateMcpOauthTokenEndpointAuthParamClientSecretPost = { + readonly "type": "client_secret_post" + readonly "client_secret"?: string | null +} +export const RotateMcpOauthTokenEndpointAuthParamClientSecretPost = Schema.Struct({ + "type": Schema.Literal("client_secret_post").annotate({ + "description": "The type of the object. Always `client_secret_post`." + }), + "client_secret": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "The replacement OAuth client secret. Omit or pass `null` to keep the stored secret. This secret is never returned in resources." + }) + ) +}).annotate({ + "description": "Updates credentials sent in the token request body.", + "identifier": "RotateMcpOauthTokenEndpointAuthParamClientSecretPost" +}) +export type RotateVaultCredentialAuthParamStaticBearer = { readonly "type": "static_bearer"; readonly "token": string } +export const RotateVaultCredentialAuthParamStaticBearer = Schema.Struct({ + "type": Schema.Literal("static_bearer").annotate({ + "description": "The type of the object. Always `static_bearer`." + }), + "token": Schema.String.annotate({ + "description": "The replacement bearer token. This secret is never returned in credential resources." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) +}).annotate({ + "description": "Replace the bearer token for the credential's MCP server.", + "identifier": "RotateVaultCredentialAuthParamStaticBearer" +}) +export type RotateVaultCredentialAuthParamEnvironmentVariable = { + readonly "type": "environment_variable" + readonly "secret_value": string +} +export const RotateVaultCredentialAuthParamEnvironmentVariable = Schema.Struct({ + "type": Schema.Literal("environment_variable").annotate({ + "description": "The type of the object. Always `environment_variable`." + }), + "secret_value": Schema.String.annotate({ + "description": + "The write-only replacement secret. Never returned in credential resources or supplied directly to sandbox code. Must be nonempty and must not contain carriage returns, newlines, or NUL bytes." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) +}).annotate({ + "description": + "Replace the secret for an OpenAI-hosted environment credential. The environment variable name and networking configuration remain unchanged.", + "identifier": "RotateVaultCredentialAuthParamEnvironmentVariable" +}) +export type DeletedVaultCredentialResource = { + readonly "id": string + readonly "object": "vault.credential.deleted" + readonly "deleted": boolean +} +export const DeletedVaultCredentialResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted credential." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("vault.credential.deleted").annotate({ + "description": "The object type. Always `vault.credential.deleted`." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the resource was deleted. Always `true`." }) +}).annotate({ + "description": "Confirmation that a vault credential was deleted.", + "identifier": "DeletedVaultCredentialResource" +}) +export type WebhookEndpointBody = { + readonly "id": string + readonly "object": "webhook_endpoint" + readonly "created_at": number + readonly "updated_at"?: number + readonly "name": string + readonly "url": string + readonly "event_types": ReadonlyArray + readonly "signing_secret_hint": string | null +} +export const WebhookEndpointBody = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the webhook endpoint." }), + "object": Schema.Literal("webhook_endpoint").annotate({ + "description": "The object type, which is always webhook_endpoint." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp when the endpoint was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "updated_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The Unix timestamp of the last endpoint configuration or signing-secret change. Initialized at creation; tests and unchanged updates do not advance it.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "name": Schema.String.annotate({ "description": "The human-readable name of the endpoint." }), + "url": Schema.String.annotate({ "description": "The HTTPS URL that receives webhook deliveries." }), + "event_types": Schema.Array(Schema.String).annotate({ + "description": "The event types that trigger deliveries to this endpoint." + }), + "signing_secret_hint": Schema.Union([ + Schema.String.annotate({ "description": "A masked hint for the endpoint's signing secret." }), + Schema.Null + ]) +}).annotate({ "identifier": "WebhookEndpointBody" }) +export type ProjectEventTypeEnum = + | "batch.completed" + | "batch.failed" + | "batch.expired" + | "batch.cancelled" + | "response.completed" + | "response.failed" + | "response.cancelled" + | "response.incomplete" + | "eval.run.succeeded" + | "eval.run.failed" + | "eval.run.canceled" + | "fine_tuning.job.succeeded" + | "fine_tuning.job.failed" + | "fine_tuning.job.cancelled" + | "realtime.call.incoming" + | "video.completed" + | "video.failed" + | "agent.environment.ready" + | "agent.environment.failed" + | "agent.environment.suspended" + | "agent.environment.expired" + | "agent.session.created" + | "agent.session.action_required" + | "agent.session.in_progress" + | "agent.session.idle" + | "agent.session.failed" + | "safety.alert.created" +export const ProjectEventTypeEnum = Schema.Literals([ + "batch.completed", + "batch.failed", + "batch.expired", + "batch.cancelled", + "response.completed", + "response.failed", + "response.cancelled", + "response.incomplete", + "eval.run.succeeded", + "eval.run.failed", + "eval.run.canceled", + "fine_tuning.job.succeeded", + "fine_tuning.job.failed", + "fine_tuning.job.cancelled", + "realtime.call.incoming", + "video.completed", + "video.failed", + "agent.environment.ready", + "agent.environment.failed", + "agent.environment.suspended", + "agent.environment.expired", + "agent.session.created", + "agent.session.action_required", + "agent.session.in_progress", + "agent.session.idle", + "agent.session.failed", + "safety.alert.created" +]).annotate({ "identifier": "ProjectEventTypeEnum" }) +export type WebhookEndpointWithSecretResource = { + readonly "id": string + readonly "object": "webhook_endpoint" + readonly "created_at": number + readonly "updated_at"?: number + readonly "name": string + readonly "url": string + readonly "event_types": ReadonlyArray + readonly "signing_secret_hint": string | null + readonly "signing_secret": string +} +export const WebhookEndpointWithSecretResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the webhook endpoint." }), + "object": Schema.Literal("webhook_endpoint").annotate({ + "description": "The object type, which is always webhook_endpoint." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp when the endpoint was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "updated_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The Unix timestamp of the last endpoint configuration or signing-secret change. Initialized at creation; tests and unchanged updates do not advance it.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "name": Schema.String.annotate({ "description": "The human-readable name of the endpoint." }), + "url": Schema.String.annotate({ "description": "The HTTPS URL that receives webhook deliveries." }), + "event_types": Schema.Array(Schema.String).annotate({ + "description": "The event types that trigger deliveries to this endpoint." + }), + "signing_secret_hint": Schema.Union([ + Schema.String.annotate({ "description": "A masked hint for the endpoint's signing secret." }), + Schema.Null + ]), + "signing_secret": Schema.String.annotate({ + "description": + "The endpoint's signing secret. This is returned only when the endpoint is created or the secret is rotated." + }) +}).annotate({ "identifier": "WebhookEndpointWithSecretResource" }) +export type DeletedWebhookEndpointResource = { + readonly "id": string + readonly "object": "webhook_endpoint.deleted" + readonly "deleted": boolean +} +export const DeletedWebhookEndpointResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the deleted webhook endpoint." }), + "object": Schema.Literal("webhook_endpoint.deleted").annotate({ + "description": "The object type, which is always webhook_endpoint.deleted." + }), + "deleted": Schema.Boolean.annotate({ "description": "Whether the endpoint was deleted." }) +}).annotate({ "identifier": "DeletedWebhookEndpointResource" }) +export type PublicRotateSecretBody = { readonly "keep_old_secret_active_for_24_hours"?: boolean } +export const PublicRotateSecretBody = Schema.Struct({ + "keep_old_secret_active_for_24_hours": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether to keep the previous signing secret valid for 24 hours after rotation. Defaults to false, which invalidates the previous secret immediately." + }) + ) +}).annotate({ "identifier": "PublicRotateSecretBody" }) +export type WebhookEndpointTestResultResource = { + readonly "object": "webhook_endpoint.test" + readonly "webhook_endpoint_id": string + readonly "event_type": string + readonly "status_code": number + readonly "success": true +} +export const WebhookEndpointTestResultResource = Schema.Struct({ + "object": Schema.Literal("webhook_endpoint.test").annotate({ + "description": "The object type, which is always webhook_endpoint.test." + }), + "webhook_endpoint_id": Schema.String.annotate({ + "description": "The ID of the webhook endpoint that received the test." + }), + "event_type": Schema.String.annotate({ "description": "The event type sent in the test." }), + "status_code": Schema.Number.annotate({ "description": "The HTTP status code returned by the endpoint." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "success": Schema.Literal(true).annotate({ + "description": + "Whether the test request completed. Always true for returned results; use status_code to determine the endpoint response." + }) +}).annotate({ "identifier": "WebhookEndpointTestResultResource" }) +export type WebhookEventTypeListResource = { readonly "object": "list"; readonly "data": ReadonlyArray } +export const WebhookEventTypeListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always list." }), + "data": Schema.Array(Schema.String).annotate({ + "description": "The webhook event types available to the authenticated project." + }) +}).annotate({ "identifier": "WebhookEventTypeListResource" }) +export type BetaMetadata = { readonly [x: string]: string } | null +export const BetaMetadata = Schema.Union([ + Schema.Record(Schema.String, Schema.String).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.\n" + }), + Schema.Null +]).annotate({ "identifier": "BetaMetadata" }) +export type BetaPromptCacheTTLEnum = "30m" +export const BetaPromptCacheTTLEnum = Schema.Literal("30m").annotate({ "identifier": "BetaPromptCacheTTLEnum" }) +export type BetaPromptCacheModeEnum = "implicit" | "explicit" +export const BetaPromptCacheModeEnum = Schema.Literals(["implicit", "explicit"]).annotate({ + "identifier": "BetaPromptCacheModeEnum" +}) +export type BetaModelIdsShared = + | string + | "gpt-6-astra" + | "gpt-6.1-sol" + | "gpt-6-sol" + | "gpt-6-luna" + | "gpt-5.6-sol" + | "gpt-5.6-terra" + | "gpt-5.6-luna" + | "gpt-5.5" + | "gpt-5.5-2026-04-23" + | "gpt-5.4" + | "gpt-5.4-mini" + | "gpt-5.4-nano" + | "gpt-5.4-mini-2026-03-17" + | "gpt-5.4-nano-2026-03-17" + | "gpt-5.3-chat-latest" + | "gpt-5.2" + | "gpt-5.2-2025-12-11" + | "gpt-5.2-chat-latest" + | "gpt-5.2-pro" + | "gpt-5.2-pro-2025-12-11" + | "gpt-5.1" + | "gpt-5.1-2025-11-13" + | "gpt-5.1-codex" + | "gpt-5.1-mini" + | "gpt-5.1-chat-latest" + | "gpt-5" + | "gpt-5-mini" + | "gpt-5-nano" + | "gpt-5-2025-08-07" + | "gpt-5-mini-2025-08-07" + | "gpt-5-nano-2025-08-07" + | "gpt-5-chat-latest" + | "gpt-4.1" + | "gpt-4.1-mini" + | "gpt-4.1-nano" + | "gpt-4.1-2025-04-14" + | "gpt-4.1-mini-2025-04-14" + | "gpt-4.1-nano-2025-04-14" + | "o4-mini" + | "o4-mini-2025-04-16" + | "o3" + | "o3-2025-04-16" + | "o3-mini" + | "o3-mini-2025-01-31" + | "o1" + | "o1-2024-12-17" + | "o1-preview" + | "o1-preview-2024-09-12" + | "o1-mini" + | "o1-mini-2024-09-12" + | "gpt-4o" + | "gpt-4o-2024-11-20" + | "gpt-4o-2024-08-06" + | "gpt-4o-2024-05-13" + | "gpt-audio-mini" + | "gpt-audio-mini-2025-12-15" + | "gpt-4o-audio-preview" + | "gpt-4o-audio-preview-2024-10-01" + | "gpt-4o-audio-preview-2024-12-17" + | "gpt-4o-audio-preview-2025-06-03" + | "gpt-4o-mini-audio-preview" + | "gpt-4o-mini-audio-preview-2024-12-17" + | "gpt-4o-search-preview" + | "gpt-4o-mini-search-preview" + | "gpt-4o-search-preview-2025-03-11" + | "gpt-4o-mini-search-preview-2025-03-11" + | "chatgpt-4o-latest" + | "codex-mini-latest" + | "gpt-4o-mini" + | "gpt-4o-mini-2024-07-18" + | "gpt-4-turbo" + | "gpt-4-turbo-2024-04-09" + | "gpt-4-0125-preview" + | "gpt-4-turbo-preview" + | "gpt-4-1106-preview" + | "gpt-4-vision-preview" + | "gpt-4" + | "gpt-4-0314" + | "gpt-4-0613" + | "gpt-4-32k" + | "gpt-4-32k-0314" + | "gpt-4-32k-0613" + | "gpt-3.5-turbo" + | "gpt-3.5-turbo-16k" + | "gpt-3.5-turbo-0301" + | "gpt-3.5-turbo-0613" + | "gpt-3.5-turbo-1106" + | "gpt-3.5-turbo-0125" + | "gpt-3.5-turbo-16k-0613" +export const BetaModelIdsShared = Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-6-astra", + "gpt-6.1-sol", + "gpt-6-sol", + "gpt-6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-audio-mini", + "gpt-audio-mini-2025-12-15", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613" + ]) +]).annotate({ "identifier": "BetaModelIdsShared" }) +export type BetaResponseFormatText = { readonly "type": "text" } +export const BetaResponseFormatText = Schema.Struct({ + "type": Schema.Literal("text").annotate({ + "description": "The type of response format being defined. Always `text`." + }) +}).annotate({ + "title": "Text", + "description": "Default response format. Used to generate text responses.\n", + "identifier": "BetaResponseFormatText" +}) +export type BetaResponseFormatJsonSchemaSchema = { readonly [x: string]: Schema.Json } +export const BetaResponseFormatJsonSchemaSchema = Schema.Record( + Schema.String, + Schema.Json.annotate({ "expected": "JSON value" }) +).annotate({ + "title": "JSON schema", + "description": + "The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n", + "identifier": "BetaResponseFormatJsonSchemaSchema" +}) +export type BetaResponseFormatJsonObject = { readonly "type": "json_object" } +export const BetaResponseFormatJsonObject = Schema.Struct({ + "type": Schema.Literal("json_object").annotate({ + "description": "The type of response format being defined. Always `json_object`." + }) +}).annotate({ + "title": "JSON object", + "description": + "JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n", + "identifier": "BetaResponseFormatJsonObject" +}) +export type BetaVerbosity = "low" | "medium" | "high" | null +export const BetaVerbosity = Schema.Union([ + Schema.Literals(["low", "medium", "high"]).annotate({ + "description": + "Constrains the verbosity of the model's response. Lower values will result in\nmore concise responses, while higher values will result in more verbose responses.\nCurrently supported values are `low`, `medium`, and `high`. The default is\n`medium`.\n" + }), + Schema.Null +]).annotate({ "identifier": "BetaVerbosity" }) +export type BetaCallableToolAllowedCaller = "direct" | "programmatic" +export const BetaCallableToolAllowedCaller = Schema.Literals(["direct", "programmatic"]).annotate({ + "identifier": "BetaCallableToolAllowedCaller" +}) +export type BetaRankerVersionType = "auto" | "default-2024-11-15" +export const BetaRankerVersionType = Schema.Literals(["auto", "default-2024-11-15"]).annotate({ + "identifier": "BetaRankerVersionType" +}) +export type BetaHybridSearchOptions = { readonly "embedding_weight": number; readonly "text_weight": number } +export const BetaHybridSearchOptions = Schema.Struct({ + "embedding_weight": Schema.Number.annotate({ + "description": "The weight of the embedding in the reciprocal ranking fusion." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "text_weight": Schema.Number.annotate({ "description": "The weight of the text in the reciprocal ranking fusion." }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })) +}).annotate({ "identifier": "BetaHybridSearchOptions" }) +export type BetaComparisonFilter = { + readonly "type": "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" + readonly "key": string + readonly "value": string | number | boolean | ReadonlyArray +} +export const BetaComparisonFilter = Schema.Struct({ + "type": Schema.Literals(["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]).annotate({ + "description": + "Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`.\n- `eq`: equals\n- `ne`: not equal\n- `gt`: greater than\n- `gte`: greater than or equal\n- `lt`: less than\n- `lte`: less than or equal\n- `in`: in\n- `nin`: not in\n" + }), + "key": Schema.String.annotate({ "description": "The key to compare against the value." }), + "value": Schema.Union([ + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.Array(Schema.Union([Schema.String, Schema.Finite], { mode: "oneOf" })) + ], { mode: "oneOf" }).annotate({ + "description": "The value to compare against the attribute key; supports string, number, or boolean types." + }) +}).annotate({ + "title": "Comparison Filter", + "description": + "A filter used to compare a specified attribute key to a given value using a defined comparison operation.\n", + "identifier": "BetaComparisonFilter" +}) +export type BetaComputerTool = { readonly "type": "computer" } +export const BetaComputerTool = Schema.Struct({ + "type": Schema.Literal("computer").annotate({ "description": "The type of the computer tool. Always `computer`." }) +}).annotate({ + "title": "Computer", + "description": + "A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).", + "identifier": "BetaComputerTool" +}) +export type BetaComputerEnvironment = "windows" | "mac" | "linux" | "ubuntu" | "browser" +export const BetaComputerEnvironment = Schema.Literals(["windows", "mac", "linux", "ubuntu", "browser"]).annotate({ + "identifier": "BetaComputerEnvironment" +}) +export type BetaWebSearchApproximateLocation = { + readonly "type"?: "approximate" + readonly "country"?: string | null + readonly "region"?: string | null + readonly "city"?: string | null + readonly "timezone"?: string | null +} | null +export const BetaWebSearchApproximateLocation = Schema.Union([ + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("approximate").annotate({ + "description": "The type of location approximation. Always `approximate`." + }) + ), + "country": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`." + }), + Schema.Null + ]) + ), + "region": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the region of the user, e.g. `California`." }), + Schema.Null + ]) + ), + "city": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the city of the user, e.g. `San Francisco`." }), + Schema.Null + ]) + ), + "timezone": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`." + }), + Schema.Null + ]) + ) + }).annotate({ "title": "Web search approximate location", "description": "The approximate location of the user.\n" }), + Schema.Null +]).annotate({ "identifier": "BetaWebSearchApproximateLocation" }) +export type BetaMCPToolFilter = { readonly "tool_names"?: ReadonlyArray; readonly "read_only"?: boolean } +export const BetaMCPToolFilter = Schema.Struct({ + "tool_names": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "title": "MCP allowed tools", "description": "List of allowed tool names." }) + ), + "read_only": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Indicates whether or not a tool modifies data or is read-only. If an\nMCP server is [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint),\nit will match this filter.\n" + }) + ) +}).annotate({ + "title": "MCP tool filter", + "description": "A filter object to specify which tools are allowed.\n", + "identifier": "BetaMCPToolFilter" +}) +export type BetaContainerMemoryLimit = "1g" | "4g" | "16g" | "64g" +export const BetaContainerMemoryLimit = Schema.Literals(["1g", "4g", "16g", "64g"]).annotate({ + "identifier": "BetaContainerMemoryLimit" +}) +export type BetaContainerNetworkPolicyDisabledParam = { readonly "type": "disabled" } +export const BetaContainerNetworkPolicyDisabledParam = Schema.Struct({ + "type": Schema.Literal("disabled").annotate({ "description": "Disable outbound network access. Always `disabled`." }) +}).annotate({ "identifier": "BetaContainerNetworkPolicyDisabledParam" }) +export type BetaContainerNetworkPolicyDomainSecretParam = { + readonly "domain": string + readonly "name": string + readonly "value": string +} +export const BetaContainerNetworkPolicyDomainSecretParam = Schema.Struct({ + "domain": Schema.String.annotate({ "description": "The domain associated with the secret." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "name": Schema.String.annotate({ "description": "The name of the secret to inject for the domain." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "value": Schema.String.annotate({ "description": "The secret value to inject for the domain." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })) +}).annotate({ "identifier": "BetaContainerNetworkPolicyDomainSecretParam" }) +export type BetaProgrammaticToolCallingParam = { readonly "type": "programmatic_tool_calling" } +export const BetaProgrammaticToolCallingParam = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The type of the tool. Always `programmatic_tool_calling`." + }) +}).annotate({ "identifier": "BetaProgrammaticToolCallingParam" }) +export type BetaInputFidelity = "high" | "low" +export const BetaInputFidelity = Schema.Literals(["high", "low"]).annotate({ + "description": + "Control how much effort the model will exert to match the style and features, especially facial features, of input images. Supports `high` and `low` on `gpt-image-1` and `gpt-image-1.5`; `gpt-image-1-mini` supports only `low`. For `gpt-image-2`, omit this parameter. Defaults to `low` on supported models.", + "identifier": "BetaInputFidelity" +}) +export type BetaImageGenActionEnum = "generate" | "edit" | "auto" +export const BetaImageGenActionEnum = Schema.Literals(["generate", "edit", "auto"]).annotate({ + "identifier": "BetaImageGenActionEnum" +}) +export type BetaLocalShellToolParam = { readonly "type": "local_shell" } +export const BetaLocalShellToolParam = Schema.Struct({ + "type": Schema.Literal("local_shell").annotate({ + "description": "The type of the local shell tool. Always `local_shell`." + }) +}).annotate({ + "title": "Local shell tool", + "description": "A tool that allows the model to execute shell commands in a local environment.", + "identifier": "BetaLocalShellToolParam" +}) +export type BetaSkillReferenceParam = { + readonly "type": "skill_reference" + readonly "skill_id": string + readonly "version"?: string +} +export const BetaSkillReferenceParam = Schema.Struct({ + "type": Schema.Literal("skill_reference").annotate({ + "description": "References a skill created with the /v1/skills endpoint." + }), + "skill_id": Schema.String.annotate({ "description": "The ID of the referenced skill." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "version": Schema.optionalKey( + Schema.String.annotate({ + "description": "Optional skill version. Use a positive integer or 'latest'. Omit for default." + }) + ) +}).annotate({ "identifier": "BetaSkillReferenceParam" }) +export type BetaInlineSkillSourceParam = { + readonly "type": "base64" + readonly "media_type": "application/zip" + readonly "data": string +} +export const BetaInlineSkillSourceParam = Schema.Struct({ + "type": Schema.Literal("base64").annotate({ + "description": "The type of the inline skill source. Must be `base64`." + }), + "media_type": Schema.Literal("application/zip").annotate({ + "description": "The media type of the inline skill payload. Must be `application/zip`." + }), + "data": Schema.String.annotate({ "description": "Base64-encoded skill zip bundle." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(70254592).annotate({ "expected": "a string with at most 70254592 code points" })) +}).annotate({ "description": "Inline skill payload", "identifier": "BetaInlineSkillSourceParam" }) +export type BetaLocalSkillParam = { readonly "name": string; readonly "description": string; readonly "path": string } +export const BetaLocalSkillParam = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the skill." }), + "description": Schema.String.annotate({ "description": "The description of the skill." }), + "path": Schema.String.annotate({ "description": "The path to the directory containing the skill." }) +}).annotate({ "identifier": "BetaLocalSkillParam" }) +export type BetaContainerReferenceParam = { readonly "type": "container_reference"; readonly "container_id": string } +export const BetaContainerReferenceParam = Schema.Struct({ + "type": Schema.Literal("container_reference").annotate({ + "description": "References a container created with the /v1/containers endpoint" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the referenced container." }) +}).annotate({ "identifier": "BetaContainerReferenceParam" }) +export type BetaCustomTextFormatParam = { readonly "type": "text" } +export const BetaCustomTextFormatParam = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "Unconstrained text format. Always `text`." }) +}).annotate({ + "title": "Text format", + "description": "Unconstrained free-form text.", + "identifier": "BetaCustomTextFormatParam" +}) +export type BetaGrammarSyntax1 = "lark" | "regex" +export const BetaGrammarSyntax1 = Schema.Literals(["lark", "regex"]).annotate({ "identifier": "BetaGrammarSyntax1" }) +export type BetaEmptyModelParam = { readonly [x: string]: Schema.Json } +export const BetaEmptyModelParam = Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + .annotate({ "identifier": "BetaEmptyModelParam" }) +export type BetaToolSearchExecutionType = "server" | "client" +export const BetaToolSearchExecutionType = Schema.Literals(["server", "client"]).annotate({ + "identifier": "BetaToolSearchExecutionType" +}) +export type BetaApproximateLocation = { + readonly "type": "approximate" + readonly "country"?: string | null + readonly "region"?: string | null + readonly "city"?: string | null + readonly "timezone"?: string | null +} +export const BetaApproximateLocation = Schema.Struct({ + "type": Schema.Literal("approximate").annotate({ + "description": "The type of location approximation. Always `approximate`." + }), + "country": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`." + }), + Schema.Null + ]) + ), + "region": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the region of the user, e.g. `California`." }), + Schema.Null + ]) + ), + "city": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Free text input for the city of the user, e.g. `San Francisco`." }), + Schema.Null + ]) + ), + "timezone": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaApproximateLocation" }) +export type BetaSearchContextSize = "low" | "medium" | "high" +export const BetaSearchContextSize = Schema.Literals(["low", "medium", "high"]).annotate({ + "identifier": "BetaSearchContextSize" +}) +export type BetaSearchContentType = "text" | "image" +export const BetaSearchContentType = Schema.Literals(["text", "image"]).annotate({ + "identifier": "BetaSearchContentType" +}) +export type BetaToolChoiceOptions = "none" | "auto" | "required" +export const BetaToolChoiceOptions = Schema.Literals(["none", "auto", "required"]).annotate({ + "title": "Tool choice mode", + "description": + "Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n", + "identifier": "BetaToolChoiceOptions" +}) +export type BetaToolChoiceAllowed = { + readonly "type": "allowed_tools" + readonly "mode": "auto" | "required" + readonly "tools": ReadonlyArray<{ readonly [x: string]: Schema.Json }> +} +export const BetaToolChoiceAllowed = Schema.Struct({ + "type": Schema.Literal("allowed_tools").annotate({ + "description": "Allowed tool configuration type. Always `allowed_tools`." + }), + "mode": Schema.Literals(["auto", "required"]).annotate({ + "description": + "Constrains the tools available to the model to a pre-defined set.\n\n`auto` allows the model to pick from among the allowed tools and generate a\nmessage.\n\n`required` requires the model to call one or more of the allowed tools.\n" + }), + "tools": Schema.Array( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A tool definition that the model should be allowed to call.\n" + }) + ).annotate({ + "description": + "A list of tool definitions that the model should be allowed to call.\n\nFor the Responses API, the list of tool definitions might look like:\n```json\n[\n { \"type\": \"function\", \"name\": \"get_weather\" },\n { \"type\": \"mcp\", \"server_label\": \"deepwiki\" },\n { \"type\": \"image_generation\" }\n]\n```\n" + }) +}).annotate({ + "title": "Allowed tools", + "description": "Constrains the tools available to the model to a pre-defined set.\n", + "identifier": "BetaToolChoiceAllowed" +}) +export type BetaToolChoiceTypes = { + readonly "type": + | "file_search" + | "web_search_preview" + | "computer" + | "computer_use_preview" + | "computer_use" + | "web_search_preview_2025_03_11" + | "image_generation" + | "code_interpreter" +} +export const BetaToolChoiceTypes = Schema.Struct({ + "type": Schema.Literals([ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter" + ]).annotate({ + "description": + "The type of hosted tool the model should to use. Learn more about\n[built-in tools](https://developers.openai.com/api/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer`\n- `computer_use_preview`\n- `computer_use`\n- `code_interpreter`\n- `image_generation`\n" + }) +}).annotate({ + "title": "Hosted tool", + "description": + "Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).\n", + "identifier": "BetaToolChoiceTypes" +}) +export type BetaToolChoiceFunction = { readonly "type": "function"; readonly "name": string } +export const BetaToolChoiceFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ + "description": "For function calling, the type is always `function`." + }), + "name": Schema.String.annotate({ "description": "The name of the function to call." }) +}).annotate({ + "title": "Function tool", + "description": "Use this option to force the model to call a specific function.\n", + "identifier": "BetaToolChoiceFunction" +}) +export type BetaToolChoiceMCP = { + readonly "type": "mcp" + readonly "server_label": string + readonly "name"?: string | null +} +export const BetaToolChoiceMCP = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "For MCP tools, the type is always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server to use.\n" }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the tool to call on the server.\n" }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP tool", + "description": "Use this option to force the model to call a specific tool on a remote MCP server.\n", + "identifier": "BetaToolChoiceMCP" +}) +export type BetaToolChoiceCustom = { readonly "type": "custom"; readonly "name": string } +export const BetaToolChoiceCustom = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "For custom tool calling, the type is always `custom`." }), + "name": Schema.String.annotate({ "description": "The name of the custom tool to call." }) +}).annotate({ + "title": "Custom tool", + "description": "Use this option to force the model to call a specific custom tool.\n", + "identifier": "BetaToolChoiceCustom" +}) +export type BetaSpecificProgrammaticToolCallingParam = { readonly "type": "programmatic_tool_calling" } +export const BetaSpecificProgrammaticToolCallingParam = Schema.Struct({ + "type": Schema.Literal("programmatic_tool_calling").annotate({ + "description": "The tool to call. Always `programmatic_tool_calling`." + }) +}).annotate({ "identifier": "BetaSpecificProgrammaticToolCallingParam" }) +export type BetaSpecificApplyPatchParam = { readonly "type": "apply_patch" } +export const BetaSpecificApplyPatchParam = Schema.Struct({ + "type": Schema.Literal("apply_patch").annotate({ "description": "The tool to call. Always `apply_patch`." }) +}).annotate({ + "title": "Specific apply patch tool choice", + "description": "Forces the model to call the apply_patch tool when executing a tool call.", + "identifier": "BetaSpecificApplyPatchParam" +}) +export type BetaSpecificFunctionShellParam = { readonly "type": "shell" } +export const BetaSpecificFunctionShellParam = Schema.Struct({ + "type": Schema.Literal("shell").annotate({ "description": "The tool to call. Always `shell`." }) +}).annotate({ + "title": "Specific shell tool choice", + "description": "Forces the model to call the shell tool when a tool call is required.", + "identifier": "BetaSpecificFunctionShellParam" +}) +export type BetaPromptCacheBreakpointConfig = { readonly "mode": "explicit" } +export const BetaPromptCacheBreakpointConfig = Schema.Struct({ + "mode": Schema.Literal("explicit").annotate({ "description": "The breakpoint mode. Always `explicit`." }) +}).annotate({ + "title": "Prompt cache breakpoint", + "description": + "Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.", + "identifier": "BetaPromptCacheBreakpointConfig" +}) +export type BetaImageDetail = "low" | "high" | "auto" | "original" +export const BetaImageDetail = Schema.Literals(["low", "high", "auto", "original"]).annotate({ + "identifier": "BetaImageDetail" +}) +export type BetaFileInputDetail = "auto" | "low" | "high" +export const BetaFileInputDetail = Schema.Literals(["auto", "low", "high"]).annotate({ + "identifier": "BetaFileInputDetail" +}) +export type BetaCyberAccessProgramEnum = "standard" | "daybreak_blue" | "daybreak_red" +export const BetaCyberAccessProgramEnum = Schema.Literals(["standard", "daybreak_blue", "daybreak_red"]).annotate({ + "identifier": "BetaCyberAccessProgramEnum" +}) +export type BetaServiceTierResponses = "auto" | "default" | "flex" | "scale" | "priority" | "fast" | "ultrafast" | null +export const BetaServiceTierResponses = Schema.Union([ + Schema.Literals(["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]).annotate({ + "description": + "Specifies the processing type used for serving the request.\n - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.\n - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.\n - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.\n - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.\n - If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.\n - When not set, the default behavior is 'auto'.\n\n When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.\n" + }), + Schema.Null +]).annotate({ "identifier": "BetaServiceTierResponses" }) +export type BetaReasoningModeEnum = string | "standard" | "pro" +export const BetaReasoningModeEnum = Schema.Union([Schema.String, Schema.Literals(["standard", "pro"])]).annotate({ + "identifier": "BetaReasoningModeEnum" +}) +export type BetaReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null +export const BetaReasoningEffort = Schema.Union([ + Schema.Literals(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).annotate({ + "description": + "Constrains effort on reasoning for reasoning models. Currently supported\nvalues are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.\nReducing reasoning effort can result in faster responses and fewer tokens\nused on reasoning in a response. Not all reasoning models support every\nvalue. See the\n[reasoning guide](https://developers.openai.com/api/docs/guides/reasoning)\nfor model-specific support.\n" + }), + Schema.Null +]).annotate({ "identifier": "BetaReasoningEffort" }) +export type BetaMessagePhase = "commentary" | "final_answer" +export const BetaMessagePhase = Schema.Literals(["commentary", "final_answer"]).annotate({ + "description": + "Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`).\nFor models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend\nphase on all assistant messages — dropping it can degrade performance. Not used for user messages.\n", + "identifier": "BetaMessagePhase" +}) +export type BetaAgentTag = { readonly "agent_name": string } +export const BetaAgentTag = Schema.Struct({ + "agent_name": Schema.String.annotate({ "description": "The canonical name of the agent that produced this item." }) +}).annotate({ "identifier": "BetaAgentTag" }) +export type BetaFileCitationBody = { + readonly "type": "file_citation" + readonly "file_id": string + readonly "index": number + readonly "filename": string +} +export const BetaFileCitationBody = Schema.Struct({ + "type": Schema.Literal("file_citation").annotate({ + "description": "The type of the file citation. Always `file_citation`." + }), + "file_id": Schema.String.annotate({ "description": "The ID of the file." }), + "index": Schema.Number.annotate({ + "description": "The index in the output text at which to insert the file citation." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "filename": Schema.String.annotate({ "description": "The filename of the file cited." }) +}).annotate({ "title": "File citation", "description": "A citation to a file.", "identifier": "BetaFileCitationBody" }) +export type BetaUrlCitationBody = { + readonly "type": "url_citation" + readonly "url": string + readonly "start_index": number + readonly "end_index": number + readonly "title": string +} +export const BetaUrlCitationBody = Schema.Struct({ + "type": Schema.Literal("url_citation").annotate({ + "description": "The type of the URL citation. Always `url_citation`." + }), + "url": Schema.String.annotate({ "description": "The URL of the web resource.", "format": "uri" }), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the URL citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the URL citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "title": Schema.String.annotate({ "description": "The title of the web resource." }) +}).annotate({ + "title": "URL citation", + "description": "A citation for a web resource used to generate a model response.", + "identifier": "BetaUrlCitationBody" +}) +export type BetaContainerFileCitationBody = { + readonly "type": "container_file_citation" + readonly "container_id": string + readonly "file_id": string + readonly "start_index": number + readonly "end_index": number + readonly "filename": string +} +export const BetaContainerFileCitationBody = Schema.Struct({ + "type": Schema.Literal("container_file_citation").annotate({ + "description": "The type of the container file citation. Always `container_file_citation`." + }), + "container_id": Schema.String.annotate({ "description": "The ID of the container file." }), + "file_id": Schema.String.annotate({ "description": "The ID of the file." }), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the container file citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the container file citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "filename": Schema.String.annotate({ "description": "The filename of the container file cited." }) +}).annotate({ + "title": "Container file citation", + "description": "A citation for a container file used to generate a model response.", + "identifier": "BetaContainerFileCitationBody" +}) +export type BetaFilePath = { readonly "type": "file_path"; readonly "file_id": string; readonly "index": number } +export const BetaFilePath = Schema.Struct({ + "type": Schema.Literal("file_path").annotate({ "description": "The type of the file path. Always `file_path`.\n" }), + "file_id": Schema.String.annotate({ "description": "The ID of the file.\n" }), + "index": Schema.Number.annotate({ "description": "The index of the file in the list of files.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "title": "File path", "description": "A path to a file.\n", "identifier": "BetaFilePath" }) +export type BetaTopLogProb = { + readonly "token": string + readonly "logprob": number + readonly "bytes": ReadonlyArray +} +export const BetaTopLogProb = Schema.Struct({ + "token": Schema.String, + "logprob": Schema.Finite, + "bytes": Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) +}).annotate({ + "title": "Top log probability", + "description": "The top log probability of a token.", + "identifier": "BetaTopLogProb" +}) +export type BetaRefusalContent = { readonly "type": "refusal"; readonly "refusal": string } +export const BetaRefusalContent = Schema.Struct({ + "type": Schema.Literal("refusal").annotate({ "description": "The type of the refusal. Always `refusal`." }), + "refusal": Schema.String.annotate({ "description": "The refusal explanation from the model." }) +}).annotate({ "title": "Refusal", "description": "A refusal from the model.", "identifier": "BetaRefusalContent" }) +export type BetaVectorStoreFileAttributes = { readonly [x: string]: string | number | boolean } | null +export const BetaVectorStoreFileAttributes = Schema.Union([ + Schema.Record( + Schema.String, + Schema.Union([ + Schema.String.check( + Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" }) + ), + Schema.Finite, + Schema.Boolean + ], { mode: "oneOf" }) + ).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard. Keys are strings\nwith a maximum length of 64 characters. Values are strings with a maximum\nlength of 512 characters, booleans, or numbers.\n" + }).check(Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null +]).annotate({ "identifier": "BetaVectorStoreFileAttributes" }) +export type BetaClickButtonType = "left" | "right" | "wheel" | "back" | "forward" +export const BetaClickButtonType = Schema.Literals(["left", "right", "wheel", "back", "forward"]).annotate({ + "identifier": "BetaClickButtonType" +}) +export type BetaDoubleClickAction = { + readonly "type": "double_click" + readonly "x": number + readonly "y": number + readonly "keys": ReadonlyArray | null +} +export const BetaDoubleClickAction = Schema.Struct({ + "type": Schema.Literal("double_click").annotate({ + "description": "Specifies the event type. For a double click action, this property is always set to `double_click`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate where the double click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate where the double click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while double-clicking." }), + Schema.Null + ]) +}).annotate({ "title": "DoubleClick", "description": "A double click action.", "identifier": "BetaDoubleClickAction" }) +export type BetaCoordParam = { readonly "x": number; readonly "y": number } +export const BetaCoordParam = Schema.Struct({ + "x": Schema.Number.annotate({ "description": "The x-coordinate." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Coordinate", + "description": "An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.", + "identifier": "BetaCoordParam" +}) +export type BetaKeyPressAction = { readonly "type": "keypress"; readonly "keys": ReadonlyArray } +export const BetaKeyPressAction = Schema.Struct({ + "type": Schema.Literal("keypress").annotate({ + "description": "Specifies the event type. For a keypress action, this property is always set to `keypress`." + }), + "keys": Schema.Array( + Schema.String.annotate({ "description": "One of the keys the model is requesting to be pressed." }) + ).annotate({ + "description": + "The combination of keys the model is requesting to be pressed. This is an array of strings, each representing a key." + }) +}).annotate({ + "title": "KeyPress", + "description": "A collection of keypresses the model would like to perform.", + "identifier": "BetaKeyPressAction" +}) +export type BetaMoveParam = { + readonly "type": "move" + readonly "x": number + readonly "y": number + readonly "keys"?: ReadonlyArray | null +} +export const BetaMoveParam = Schema.Struct({ + "type": Schema.Literal("move").annotate({ + "description": "Specifies the event type. For a move action, this property is always set to `move`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate to move to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate to move to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while moving the mouse." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Move", "description": "A mouse move action.", "identifier": "BetaMoveParam" }) +export type BetaScreenshotParam = { readonly "type": "screenshot" } +export const BetaScreenshotParam = Schema.Struct({ + "type": Schema.Literal("screenshot").annotate({ + "description": "Specifies the event type. For a screenshot action, this property is always set to `screenshot`." + }) +}).annotate({ "title": "Screenshot", "description": "A screenshot action.", "identifier": "BetaScreenshotParam" }) +export type BetaScrollParam = { + readonly "type": "scroll" + readonly "x": number + readonly "y": number + readonly "scroll_x": number + readonly "scroll_y": number + readonly "keys"?: ReadonlyArray | null +} +export const BetaScrollParam = Schema.Struct({ + "type": Schema.Literal("scroll").annotate({ + "description": "Specifies the event type. For a scroll action, this property is always set to `scroll`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate where the scroll occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate where the scroll occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "scroll_x": Schema.Number.annotate({ "description": "The horizontal scroll distance." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "scroll_y": Schema.Number.annotate({ "description": "The vertical scroll distance." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while scrolling." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Scroll", "description": "A scroll action.", "identifier": "BetaScrollParam" }) +export type BetaTypeParam = { readonly "type": "type"; readonly "text": string } +export const BetaTypeParam = Schema.Struct({ + "type": Schema.Literal("type").annotate({ + "description": "Specifies the event type. For a type action, this property is always set to `type`." + }), + "text": Schema.String.annotate({ "description": "The text to type." }) +}).annotate({ "title": "Type", "description": "An action to type in text.", "identifier": "BetaTypeParam" }) +export type BetaWaitParam = { readonly "type": "wait" } +export const BetaWaitParam = Schema.Struct({ + "type": Schema.Literal("wait").annotate({ + "description": "Specifies the event type. For a wait action, this property is always set to `wait`." + }) +}).annotate({ "title": "Wait", "description": "A wait action.", "identifier": "BetaWaitParam" }) +export type BetaComputerCallSafetyCheckParam = { + readonly "id": string + readonly "code"?: string | null + readonly "message"?: string | null +} +export const BetaComputerCallSafetyCheckParam = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the pending safety check." }), + "code": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The type of the pending safety check." }), Schema.Null]) + ), + "message": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Details about the pending safety check." }), Schema.Null]) + ) +}).annotate({ + "description": "A pending safety check for the computer call.", + "identifier": "BetaComputerCallSafetyCheckParam" +}) +export type Beta_AgentTagParam = { readonly "agent_name": string } +export const Beta_AgentTagParam = Schema.Struct({ + "agent_name": Schema.String.annotate({ "description": "The canonical name of the agent that produced this item." }) +}).annotate({ "description": "The agent that produced this item.", "identifier": "Beta_AgentTagParam" }) +export type BetaComputerScreenshotImage = { + readonly "type": "computer_screenshot" + readonly "image_url"?: string + readonly "file_id"?: string +} +export const BetaComputerScreenshotImage = Schema.Struct({ + "type": Schema.Literal("computer_screenshot").annotate({ + "description": + "Specifies the event type. For a computer screenshot, this property is \nalways set to `computer_screenshot`.\n" + }), + "image_url": Schema.optionalKey( + Schema.String.annotate({ "description": "The URL of the screenshot image.", "format": "uri" }) + ), + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of an uploaded file that contains the screenshot." }) + ) +}).annotate({ + "description": "A computer screenshot image used with the computer use tool.\n", + "identifier": "BetaComputerScreenshotImage" +}) +export type BetaFunctionCallItemStatus = "in_progress" | "completed" | "incomplete" +export const BetaFunctionCallItemStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "BetaFunctionCallItemStatus" +}) +export type BetaWebSearchCallStatus = "in_progress" | "searching" | "completed" | "failed" | "incomplete" +export const BetaWebSearchCallStatus = Schema.Literals([ + "in_progress", + "searching", + "completed", + "failed", + "incomplete" +]).annotate({ "identifier": "BetaWebSearchCallStatus" }) +export type BetaWebSearchActionSearch = { + readonly "type": "search" + readonly "query"?: string + readonly "queries"?: ReadonlyArray + readonly "sources"?: ReadonlyArray<{ readonly "type": "url"; readonly "url": string }> +} +export const BetaWebSearchActionSearch = Schema.Struct({ + "type": Schema.Literal("search").annotate({ "description": "The action type.\n" }), + "query": Schema.optionalKey(Schema.String.annotate({ "description": "The search query.\n" })), + "queries": Schema.optionalKey( + Schema.Array(Schema.String.annotate({ "description": "A search query.\n" })).annotate({ + "title": "Search queries", + "description": "The search queries.\n" + }) + ), + "sources": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "type": Schema.Literal("url").annotate({ "description": "The type of source. Always `url`.\n" }), + "url": Schema.String.annotate({ "description": "The URL of the source.\n", "format": "uri" }) + }).annotate({ "title": "Web search source", "description": "A source used in the search.\n" }) + ).annotate({ "title": "Web search sources", "description": "The sources used in the search.\n" }) + ) +}).annotate({ + "title": "Search action", + "description": "Action type \"search\" - Performs a web search query.\n", + "identifier": "BetaWebSearchActionSearch" +}) +export type BetaWebSearchActionOpenPage = { readonly "type": "open_page"; readonly "url"?: string | null } +export const BetaWebSearchActionOpenPage = Schema.Struct({ + "type": Schema.Literal("open_page").annotate({ "description": "The action type.\n" }), + "url": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "format": "uri" }), Schema.Null]).annotate({ + "description": "The URL opened by the model.\n" + }) + ) +}).annotate({ + "title": "Open page action", + "description": "Action type \"open_page\" - Opens a specific URL from search results.\n", + "identifier": "BetaWebSearchActionOpenPage" +}) +export type BetaWebSearchActionFind = { + readonly "type": "find_in_page" + readonly "url": string + readonly "pattern": string +} +export const BetaWebSearchActionFind = Schema.Struct({ + "type": Schema.Literal("find_in_page").annotate({ "description": "The action type.\n" }), + "url": Schema.String.annotate({ "description": "The URL of the page searched for the pattern.\n", "format": "uri" }), + "pattern": Schema.String.annotate({ "description": "The pattern or text to search for within the page.\n" }) +}).annotate({ + "title": "Find action", + "description": "Action type \"find_in_page\": Searches for a pattern within a loaded page.\n", + "identifier": "BetaWebSearchActionFind" +}) +export type BetaDirectToolCallCaller = { readonly "type": "direct" } +export const BetaDirectToolCallCaller = Schema.Struct({ "type": Schema.Literal("direct") }).annotate({ + "identifier": "BetaDirectToolCallCaller" +}) +export type BetaProgramToolCallCaller = { readonly "type": "program"; readonly "caller_id": string } +export const BetaProgramToolCallCaller = Schema.Struct({ + "type": Schema.Literal("program"), + "caller_id": Schema.String.annotate({ + "description": "The call ID of the program item that produced this tool call." + }) +}).annotate({ "identifier": "BetaProgramToolCallCaller" }) +export type BetaPromptCacheBreakpointParam = { readonly "mode": "explicit" } +export const BetaPromptCacheBreakpointParam = Schema.Struct({ + "mode": Schema.Literal("explicit").annotate({ "description": "The breakpoint mode. Always `explicit`." }) +}).annotate({ + "title": "Prompt cache breakpoint", + "description": + "Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.", + "identifier": "BetaPromptCacheBreakpointParam" +}) +export type BetaDetailEnum = "low" | "high" | "auto" | "original" +export const BetaDetailEnum = Schema.Literals(["low", "high", "auto", "original"]).annotate({ + "identifier": "BetaDetailEnum" +}) +export type BetaFileDetailEnum = "auto" | "low" | "high" +export const BetaFileDetailEnum = Schema.Literals(["auto", "low", "high"]).annotate({ + "identifier": "BetaFileDetailEnum" +}) +export type BetaDirectToolCallCallerParam = { readonly "type": "direct" } +export const BetaDirectToolCallCallerParam = Schema.Struct({ + "type": Schema.Literal("direct").annotate({ "description": "The caller type. Always `direct`." }) +}).annotate({ "identifier": "BetaDirectToolCallCallerParam" }) +export type BetaProgramToolCallCallerParam = { readonly "type": "program"; readonly "caller_id": string } +export const BetaProgramToolCallCallerParam = Schema.Struct({ + "type": Schema.Literal("program").annotate({ "description": "The caller type. Always `program`." }), + "caller_id": Schema.String.annotate({ + "description": "The call ID of the program item that produced this tool call." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ) +}).annotate({ "identifier": "BetaProgramToolCallCallerParam" }) +export type BetaEncryptedContentParam = { readonly "type": "encrypted_content"; readonly "encrypted_content": string } +export const BetaEncryptedContentParam = Schema.Struct({ + "type": Schema.Literal("encrypted_content").annotate({ + "description": "The type of the input item. Always `encrypted_content`." + }), + "encrypted_content": Schema.String.annotate({ "description": "Opaque encrypted content." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ) +}).annotate({ + "title": "Encrypted content", + "description": "Opaque encrypted content that Responses API decrypts inside trusted model execution.", + "identifier": "BetaEncryptedContentParam" +}) +export type BetaMultiAgentAction1 = + | "spawn_agent" + | "interrupt_agent" + | "list_agents" + | "send_message" + | "followup_task" + | "wait_agent" +export const BetaMultiAgentAction1 = Schema.Literals([ + "spawn_agent", + "interrupt_agent", + "list_agents", + "send_message", + "followup_task", + "wait_agent" +]).annotate({ "identifier": "BetaMultiAgentAction1" }) +export type BetaFileCitationParam = { + readonly "type": "file_citation" + readonly "index": number + readonly "file_id": string + readonly "filename": string +} +export const BetaFileCitationParam = Schema.Struct({ + "type": Schema.Literal("file_citation").annotate({ "description": "The citation type. Always `file_citation`." }), + "index": Schema.Number.annotate({ "description": "The index of the file in the list of files." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })), + "file_id": Schema.String.annotate({ "description": "The ID of the file." }), + "filename": Schema.String.annotate({ "description": "The filename of the file cited." }) +}).annotate({ "identifier": "BetaFileCitationParam" }) +export type BetaUrlCitationParam = { + readonly "type": "url_citation" + readonly "start_index": number + readonly "end_index": number + readonly "url": string + readonly "title": string +} +export const BetaUrlCitationParam = Schema.Struct({ + "type": Schema.Literal("url_citation").annotate({ "description": "The citation type. Always `url_citation`." }), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "url": Schema.String.annotate({ "description": "The URL of the cited resource.", "format": "uri" }), + "title": Schema.String.annotate({ "description": "The title of the cited resource." }) +}).annotate({ "identifier": "BetaUrlCitationParam" }) +export type BetaContainerFileCitationParam = { + readonly "type": "container_file_citation" + readonly "start_index": number + readonly "end_index": number + readonly "container_id": string + readonly "file_id": string + readonly "filename": string +} +export const BetaContainerFileCitationParam = Schema.Struct({ + "type": Schema.Literal("container_file_citation").annotate({ + "description": "The citation type. Always `container_file_citation`." + }), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + "container_id": Schema.String.annotate({ "description": "The ID of the container." }), + "file_id": Schema.String.annotate({ "description": "The ID of the container file." }), + "filename": Schema.String.annotate({ "description": "The filename of the container file cited." }) +}).annotate({ "identifier": "BetaContainerFileCitationParam" }) +export type BetaSummaryTextContent = { readonly "type": "summary_text"; readonly "text": string } +export const BetaSummaryTextContent = Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ "description": "The type of the object. Always `summary_text`." }), + "text": Schema.String.annotate({ "description": "A summary of the reasoning output from the model so far." }) +}).annotate({ + "title": "Summary text", + "description": "A summary text from the model.", + "identifier": "BetaSummaryTextContent" +}) +export type BetaReasoningTextContent = { readonly "type": "reasoning_text"; readonly "text": string } +export const BetaReasoningTextContent = Schema.Struct({ + "type": Schema.Literal("reasoning_text").annotate({ + "description": "The type of the reasoning text. Always `reasoning_text`." + }), + "text": Schema.String.annotate({ "description": "The reasoning text from the model." }) +}).annotate({ + "title": "Reasoning text", + "description": "Reasoning text from the model.", + "identifier": "BetaReasoningTextContent" +}) +export type BetaImageBackground = "transparent" | "opaque" | "auto" +export const BetaImageBackground = Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "identifier": "BetaImageBackground" +}) +export type BetaImageOutputFormat = "png" | "webp" | "jpeg" +export const BetaImageOutputFormat = Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "identifier": "BetaImageOutputFormat" +}) +export type BetaCodeInterpreterOutputLogs = { readonly "type": "logs"; readonly "logs": string } +export const BetaCodeInterpreterOutputLogs = Schema.Struct({ + "type": Schema.Literal("logs").annotate({ "description": "The type of the output. Always `logs`." }), + "logs": Schema.String.annotate({ "description": "The logs output from the code interpreter." }) +}).annotate({ + "title": "Code interpreter output logs", + "description": "The logs output from the code interpreter.", + "identifier": "BetaCodeInterpreterOutputLogs" +}) +export type BetaCodeInterpreterOutputImage = { readonly "type": "image"; readonly "url": string } +export const BetaCodeInterpreterOutputImage = Schema.Struct({ + "type": Schema.Literal("image").annotate({ "description": "The type of the output. Always `image`." }), + "url": Schema.String.annotate({ + "description": "The URL of the image output from the code interpreter.", + "format": "uri" + }) +}).annotate({ + "title": "Code interpreter output image", + "description": "The image output from the code interpreter.", + "identifier": "BetaCodeInterpreterOutputImage" +}) +export type BetaLocalShellExecAction = { + readonly "type": "exec" + readonly "command": ReadonlyArray + readonly "timeout_ms"?: number | null + readonly "working_directory"?: string | null + readonly "env": { readonly [x: string]: string } + readonly "user"?: string | null +} +export const BetaLocalShellExecAction = Schema.Struct({ + "type": Schema.Literal("exec").annotate({ "description": "The type of the local shell action. Always `exec`." }), + "command": Schema.Array(Schema.String).annotate({ "description": "The command to run." }), + "timeout_ms": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "Optional timeout in milliseconds for the command." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "working_directory": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Optional working directory to run the command in." }), + Schema.Null + ]) + ), + "env": Schema.Record(Schema.String, Schema.String).annotate({ + "description": "Environment variables to set for the command." + }), + "user": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional user to run the command as." }), Schema.Null]) + ) +}).annotate({ + "title": "Local shell exec action", + "description": "Execute a shell command on the server.", + "identifier": "BetaLocalShellExecAction" +}) +export type BetaFunctionShellActionParam = { + readonly "commands": ReadonlyArray + readonly "timeout_ms"?: number | null + readonly "max_output_length"?: number | null +} +export const BetaFunctionShellActionParam = Schema.Struct({ + "commands": Schema.Array(Schema.String).annotate({ + "description": "Ordered shell commands for the execution environment to run." + }), + "timeout_ms": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Maximum wall-clock time in milliseconds to allow the shell commands to run." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "max_output_length": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Maximum number of UTF-8 characters to capture from combined stdout and stderr output." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) +}).annotate({ + "title": "Shell action", + "description": "Commands and limits describing how to run the shell tool call.", + "identifier": "BetaFunctionShellActionParam" +}) +export type BetaFunctionShellCallItemStatus = "in_progress" | "completed" | "incomplete" +export const BetaFunctionShellCallItemStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "title": "Shell call status", + "description": "Status values reported for shell tool calls.", + "identifier": "BetaFunctionShellCallItemStatus" +}) +export type BetaFunctionShellCallOutputTimeoutOutcomeParam = { readonly "type": "timeout" } +export const BetaFunctionShellCallOutputTimeoutOutcomeParam = Schema.Struct({ + "type": Schema.Literal("timeout").annotate({ "description": "The outcome type. Always `timeout`." }) +}).annotate({ + "title": "Shell call timeout outcome", + "description": "Indicates that the shell call exceeded its configured time limit.", + "identifier": "BetaFunctionShellCallOutputTimeoutOutcomeParam" +}) +export type BetaFunctionShellCallOutputExitOutcomeParam = { readonly "type": "exit"; readonly "exit_code": number } +export const BetaFunctionShellCallOutputExitOutcomeParam = Schema.Struct({ + "type": Schema.Literal("exit").annotate({ "description": "The outcome type. Always `exit`." }), + "exit_code": Schema.Number.annotate({ "description": "The exit code returned by the shell process." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Shell call exit outcome", + "description": "Indicates that the shell commands finished and returned an exit code.", + "identifier": "BetaFunctionShellCallOutputExitOutcomeParam" +}) +export type BetaApplyPatchCallStatusParam = "in_progress" | "completed" +export const BetaApplyPatchCallStatusParam = Schema.Literals(["in_progress", "completed"]).annotate({ + "title": "Apply patch call status", + "description": "Status values reported for apply_patch tool calls.", + "identifier": "BetaApplyPatchCallStatusParam" +}) +export type BetaApplyPatchCreateFileOperationParam = { + readonly "type": "create_file" + readonly "path": string + readonly "diff": string +} +export const BetaApplyPatchCreateFileOperationParam = Schema.Struct({ + "type": Schema.Literal("create_file").annotate({ "description": "The operation type. Always `create_file`." }), + "path": Schema.String.annotate({ "description": "Path of the file to create relative to the workspace root." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "diff": Schema.String.annotate({ "description": "Unified diff content to apply when creating the file." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ) +}).annotate({ + "title": "Apply patch create file operation", + "description": "Instruction for creating a new file via the apply_patch tool.", + "identifier": "BetaApplyPatchCreateFileOperationParam" +}) +export type BetaApplyPatchDeleteFileOperationParam = { readonly "type": "delete_file"; readonly "path": string } +export const BetaApplyPatchDeleteFileOperationParam = Schema.Struct({ + "type": Schema.Literal("delete_file").annotate({ "description": "The operation type. Always `delete_file`." }), + "path": Schema.String.annotate({ "description": "Path of the file to delete relative to the workspace root." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) +}).annotate({ + "title": "Apply patch delete file operation", + "description": "Instruction for deleting an existing file via the apply_patch tool.", + "identifier": "BetaApplyPatchDeleteFileOperationParam" +}) +export type BetaApplyPatchUpdateFileOperationParam = { + readonly "type": "update_file" + readonly "path": string + readonly "diff": string +} +export const BetaApplyPatchUpdateFileOperationParam = Schema.Struct({ + "type": Schema.Literal("update_file").annotate({ "description": "The operation type. Always `update_file`." }), + "path": Schema.String.annotate({ "description": "Path of the file to update relative to the workspace root." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + "diff": Schema.String.annotate({ "description": "Unified diff content to apply to the existing file." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ) +}).annotate({ + "title": "Apply patch update file operation", + "description": "Instruction for updating an existing file via the apply_patch tool.", + "identifier": "BetaApplyPatchUpdateFileOperationParam" +}) +export type BetaApplyPatchCallOutputStatusParam = "completed" | "failed" +export const BetaApplyPatchCallOutputStatusParam = Schema.Literals(["completed", "failed"]).annotate({ + "title": "Apply patch call output status", + "description": "Outcome values reported for apply_patch tool call outputs.", + "identifier": "BetaApplyPatchCallOutputStatusParam" +}) +export type BetaMCPListToolsTool = { + readonly "name": string + readonly "description"?: string | null + readonly "input_schema": { readonly [x: string]: Schema.Json } + readonly "annotations"?: { readonly [x: string]: Schema.Json } | null +} +export const BetaMCPListToolsTool = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the tool.\n" }), + "description": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The description of the tool.\n" }), Schema.Null]) + ), + "input_schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The JSON schema describing the tool's input.\n" + }), + "annotations": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Additional annotations about the tool.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP list tools tool", + "description": "A tool available on an MCP server.\n", + "identifier": "BetaMCPListToolsTool" +}) +export type BetaMCPProtocolError = { + readonly "type": "mcp_protocol_error" + readonly "code": number + readonly "message": string +} +export const BetaMCPProtocolError = Schema.Struct({ + "type": Schema.Literal("mcp_protocol_error"), + "code": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "message": Schema.String +}).annotate({ "identifier": "BetaMCPProtocolError" }) +export type BetaMCPToolExecutionError = { readonly "type": "mcp_tool_execution_error"; readonly "content": Schema.Json } +export const BetaMCPToolExecutionError = Schema.Struct({ + "type": Schema.Literal("mcp_tool_execution_error"), + "content": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ "identifier": "BetaMCPToolExecutionError" }) +export type BetaHTTPError = { readonly "type": "http_error"; readonly "code": number; readonly "message": string } +export const BetaHTTPError = Schema.Struct({ + "type": Schema.Literal("http_error"), + "code": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "message": Schema.String +}).annotate({ "identifier": "BetaHTTPError" }) +export type BetaMCPToolCallStatus = "in_progress" | "completed" | "incomplete" | "calling" | "failed" +export const BetaMCPToolCallStatus = Schema.Literals(["in_progress", "completed", "incomplete", "calling", "failed"]) + .annotate({ "identifier": "BetaMCPToolCallStatus" }) +export type BetaProgramOutputItemStatus = "completed" | "incomplete" +export const BetaProgramOutputItemStatus = Schema.Literals(["completed", "incomplete"]).annotate({ + "identifier": "BetaProgramOutputItemStatus" +}) +export type BetaIncludeEnum = + | "file_search_call.results" + | "web_search_call.results" + | "web_search_call.action.sources" + | "message.input_image.image_url" + | "computer_call_output.output.image_url" + | "code_interpreter_call.outputs" + | "reasoning.encrypted_content" + | "message.output_text.logprobs" +export const BetaIncludeEnum = Schema.Literals([ + "file_search_call.results", + "web_search_call.results", + "web_search_call.action.sources", + "message.input_image.image_url", + "computer_call_output.output.image_url", + "code_interpreter_call.outputs", + "reasoning.encrypted_content", + "message.output_text.logprobs" +]).annotate({ + "description": + "Specify additional output data to include in the model response. Currently supported values are:\n- `web_search_call.results`: Include the search results of the web search tool call.\n- `web_search_call.action.sources`: Include the sources of the web search tool call.\n- `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n- `file_search_call.results`: Include the search results of the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `message.output_text.logprobs`: Include logprobs with assistant messages.\n- `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program).", + "identifier": "BetaIncludeEnum" +}) +export type BetaModerationMode = "score" | "block" +export const BetaModerationMode = Schema.Literals(["score", "block"]).annotate({ "identifier": "BetaModerationMode" }) +export type BetaResponseStreamOptions = { readonly "include_obfuscation"?: boolean } | null +export const BetaResponseStreamOptions = Schema.Union([ + Schema.Struct({ + "include_obfuscation": Schema.optionalKey(Schema.Boolean.annotate({ + "description": + "When true, stream obfuscation will be enabled. Stream obfuscation adds\nrandom characters to an `obfuscation` field on streaming delta events to\nnormalize payload sizes as a mitigation to certain side-channel attacks.\nThese obfuscation fields are included by default, but add a small amount\nof overhead to the data stream. You can set `include_obfuscation` to\nfalse to optimize for bandwidth if you trust the network links between\nyour application and the OpenAI API.\n" + })) + }).annotate({ "description": "Options for streaming responses. Only set this when you set `stream: true`.\n" }), + Schema.Null +]).annotate({ "identifier": "BetaResponseStreamOptions" }) +export type BetaConversationParam_2 = { readonly "id": string } +export const BetaConversationParam_2 = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the conversation." }) +}).annotate({ + "title": "Conversation object", + "description": "The conversation that this response belongs to.", + "identifier": "BetaConversationParam-2" +}) +export type BetaContextManagementParam = { readonly "type": string; readonly "compact_threshold"?: number | null } +export const BetaContextManagementParam = Schema.Struct({ + "type": Schema.String.annotate({ + "description": "The context management entry type. Currently only 'compaction' is supported." + }), + "compact_threshold": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Token threshold at which compaction should be triggered for this entry." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1000).annotate({ "expected": "a value greater than or equal to 1000" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaContextManagementParam" }) +export type BetaMultiAgentParam = { readonly "enabled": boolean; readonly "max_concurrent_subagents"?: number } +export const BetaMultiAgentParam = Schema.Struct({ + "enabled": Schema.Boolean.annotate({ + "description": "Whether to enable server-hosted multi-agent execution for this response." + }), + "max_concurrent_subagents": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "`max_concurrent_subagents` sets the maximum number of subagents that can be active simultaneously across the entire agent tree. It includes all descendants—children, grandchildren, and deeper subagents—but excludes the root agent.\nThe API does not impose a fixed upper bound on this setting. The default is `3`, which is recommended for most workloads. Multi-agent runs also have no fixed limit on tree depth or the total number of subagents created during a run." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ) + ) +}).annotate({ + "description": "Configuration for server-hosted multi-agent execution.", + "identifier": "BetaMultiAgentParam" +}) +export type BetaResponseErrorCode = + | "server_error" + | "rate_limit_exceeded" + | "invalid_prompt" + | "data_residency_mismatch" + | "bio_policy" + | "misalignment_policy_violation" + | "vector_store_timeout" + | "invalid_image" + | "invalid_image_format" + | "invalid_base64_image" + | "invalid_image_url" + | "image_too_large" + | "image_too_small" + | "image_parse_error" + | "image_content_policy_violation" + | "invalid_image_mode" + | "image_file_too_large" + | "unsupported_image_media_type" + | "empty_image_file" + | "failed_to_download_image" + | "image_file_not_found" +export const BetaResponseErrorCode = Schema.Literals([ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "data_residency_mismatch", + "bio_policy", + "misalignment_policy_violation", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found" +]).annotate({ "description": "The error code for the response.\n", "identifier": "BetaResponseErrorCode" }) +export type Beta_MisalignmentErrorType = + | string + | "potentially_unintended_data_transfer" + | "potentially_unintended_data_access" + | "potentially_unintended_destructive_activity" + | "other" +export const Beta_MisalignmentErrorType = Schema.Union([ + Schema.String, + Schema.Literals([ + "potentially_unintended_data_transfer", + "potentially_unintended_data_access", + "potentially_unintended_destructive_activity", + "other" + ]) +]).annotate({ "identifier": "Beta_MisalignmentErrorType" }) +export type Beta_MisalignmentSteer = { readonly "message": string } +export const Beta_MisalignmentSteer = Schema.Struct({ + "message": Schema.String.annotate({ "description": "The public continuation instruction." }) +}).annotate({ "identifier": "Beta_MisalignmentSteer" }) +export type BetaTextContent = { readonly "type": "text"; readonly "text": string } +export const BetaTextContent = Schema.Struct({ "type": Schema.Literal("text"), "text": Schema.String }).annotate({ + "title": "Text Content", + "description": "A text content.", + "identifier": "BetaTextContent" +}) +export type BetaEncryptedContent = { readonly "type": "encrypted_content"; readonly "encrypted_content": string } +export const BetaEncryptedContent = Schema.Struct({ + "type": Schema.Literal("encrypted_content").annotate({ + "description": "The type of the input item. Always `encrypted_content`." + }), + "encrypted_content": Schema.String.annotate({ "description": "Opaque encrypted content." }) +}).annotate({ + "title": "Encrypted content", + "description": "Opaque encrypted content that Responses API decrypts inside trusted model execution.", + "identifier": "BetaEncryptedContent" +}) +export type BetaMultiAgentAction = + | "spawn_agent" + | "interrupt_agent" + | "list_agents" + | "send_message" + | "followup_task" + | "wait_agent" +export const BetaMultiAgentAction = Schema.Literals([ + "spawn_agent", + "interrupt_agent", + "list_agents", + "send_message", + "followup_task", + "wait_agent" +]).annotate({ "identifier": "BetaMultiAgentAction" }) +export type BetaProgramOutputStatus = "completed" | "incomplete" +export const BetaProgramOutputStatus = Schema.Literals(["completed", "incomplete"]).annotate({ + "identifier": "BetaProgramOutputStatus" +}) +export type BetaFunctionCallStatus = "in_progress" | "completed" | "incomplete" +export const BetaFunctionCallStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "BetaFunctionCallStatus" +}) +export type BetaFunctionCallOutputStatusEnum = "in_progress" | "completed" | "incomplete" +export const BetaFunctionCallOutputStatusEnum = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "BetaFunctionCallOutputStatusEnum" +}) +export type BetaMessageRole = + | "unknown" + | "user" + | "assistant" + | "system" + | "critic" + | "discriminator" + | "developer" + | "tool" +export const BetaMessageRole = Schema.Literals([ + "unknown", + "user", + "assistant", + "system", + "critic", + "discriminator", + "developer", + "tool" +]).annotate({ "identifier": "BetaMessageRole" }) +export type BetaFunctionShellAction = { + readonly "commands": ReadonlyArray + readonly "timeout_ms": number | null + readonly "max_output_length": number | null +} +export const BetaFunctionShellAction = Schema.Struct({ + "commands": Schema.Array(Schema.String.annotate({ "description": "A list of commands to run." })), + "timeout_ms": Schema.Union([ + Schema.Number.annotate({ "description": "Optional timeout in milliseconds for the commands." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]), + "max_output_length": Schema.Union([ + Schema.Number.annotate({ "description": "Optional maximum number of characters to return from each command." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) +}).annotate({ + "title": "Shell exec action", + "description": "Execute a shell command.", + "identifier": "BetaFunctionShellAction" +}) +export type BetaFunctionShellCallStatus = "in_progress" | "completed" | "incomplete" +export const BetaFunctionShellCallStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "BetaFunctionShellCallStatus" +}) +export type BetaLocalEnvironmentResource = { readonly "type": "local" } +export const BetaLocalEnvironmentResource = Schema.Struct({ + "type": Schema.Literal("local").annotate({ "description": "The environment type. Always `local`." }) +}).annotate({ + "title": "Local Environment", + "description": "Represents the use of a local environment to perform shell actions.", + "identifier": "BetaLocalEnvironmentResource" +}) +export type BetaContainerReferenceResource = { readonly "type": "container_reference"; readonly "container_id": string } +export const BetaContainerReferenceResource = Schema.Struct({ + "type": Schema.Literal("container_reference").annotate({ + "description": "The environment type. Always `container_reference`." + }), + "container_id": Schema.String +}).annotate({ + "title": "Container Reference", + "description": "Represents a container created with /v1/containers.", + "identifier": "BetaContainerReferenceResource" +}) +export type BetaFunctionShellCallOutputStatusEnum = "in_progress" | "completed" | "incomplete" +export const BetaFunctionShellCallOutputStatusEnum = Schema.Literals(["in_progress", "completed", "incomplete"]) + .annotate({ "identifier": "BetaFunctionShellCallOutputStatusEnum" }) +export type BetaFunctionShellCallOutputTimeoutOutcome = { readonly "type": "timeout" } +export const BetaFunctionShellCallOutputTimeoutOutcome = Schema.Struct({ + "type": Schema.Literal("timeout").annotate({ "description": "The outcome type. Always `timeout`." }) +}).annotate({ + "title": "Shell call timeout outcome", + "description": "Indicates that the shell call exceeded its configured time limit.", + "identifier": "BetaFunctionShellCallOutputTimeoutOutcome" +}) +export type BetaFunctionShellCallOutputExitOutcome = { readonly "type": "exit"; readonly "exit_code": number } +export const BetaFunctionShellCallOutputExitOutcome = Schema.Struct({ + "type": Schema.Literal("exit").annotate({ "description": "The outcome type. Always `exit`." }), + "exit_code": Schema.Number.annotate({ "description": "Exit code from the shell process." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Shell call exit outcome", + "description": "Indicates that the shell commands finished and returned an exit code.", + "identifier": "BetaFunctionShellCallOutputExitOutcome" +}) +export type BetaApplyPatchCallStatus = "in_progress" | "completed" +export const BetaApplyPatchCallStatus = Schema.Literals(["in_progress", "completed"]).annotate({ + "identifier": "BetaApplyPatchCallStatus" +}) +export type BetaApplyPatchCreateFileOperation = { + readonly "type": "create_file" + readonly "path": string + readonly "diff": string +} +export const BetaApplyPatchCreateFileOperation = Schema.Struct({ + "type": Schema.Literal("create_file").annotate({ "description": "Create a new file with the provided diff." }), + "path": Schema.String.annotate({ "description": "Path of the file to create." }), + "diff": Schema.String.annotate({ "description": "Diff to apply." }) +}).annotate({ + "title": "Apply patch create file operation", + "description": "Instruction describing how to create a file via the apply_patch tool.", + "identifier": "BetaApplyPatchCreateFileOperation" +}) +export type BetaApplyPatchDeleteFileOperation = { readonly "type": "delete_file"; readonly "path": string } +export const BetaApplyPatchDeleteFileOperation = Schema.Struct({ + "type": Schema.Literal("delete_file").annotate({ "description": "Delete the specified file." }), + "path": Schema.String.annotate({ "description": "Path of the file to delete." }) +}).annotate({ + "title": "Apply patch delete file operation", + "description": "Instruction describing how to delete a file via the apply_patch tool.", + "identifier": "BetaApplyPatchDeleteFileOperation" +}) +export type BetaApplyPatchUpdateFileOperation = { + readonly "type": "update_file" + readonly "path": string + readonly "diff": string +} +export const BetaApplyPatchUpdateFileOperation = Schema.Struct({ + "type": Schema.Literal("update_file").annotate({ "description": "Update an existing file with the provided diff." }), + "path": Schema.String.annotate({ "description": "Path of the file to update." }), + "diff": Schema.String.annotate({ "description": "Diff to apply." }) +}).annotate({ + "title": "Apply patch update file operation", + "description": "Instruction describing how to update a file via the apply_patch tool.", + "identifier": "BetaApplyPatchUpdateFileOperation" +}) +export type BetaApplyPatchCallOutputStatus = "completed" | "failed" +export const BetaApplyPatchCallOutputStatus = Schema.Literals(["completed", "failed"]).annotate({ + "identifier": "BetaApplyPatchCallOutputStatus" +}) +export type BetaResponseUsage = { + readonly "input_tokens": number + readonly "input_tokens_details": { readonly "cached_tokens": number; readonly "cache_write_tokens": number } + readonly "output_tokens": number + readonly "output_tokens_details": { readonly "reasoning_tokens": number } + readonly "total_tokens": number +} +export const BetaResponseUsage = Schema.Struct({ + "input_tokens": Schema.Number.annotate({ "description": "The number of input tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "input_tokens_details": Schema.Struct({ + "cached_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that were retrieved from the cache.\n[More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "cache_write_tokens": Schema.Number.annotate({ + "description": "The number of input tokens that were written to the cache." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ "description": "A detailed breakdown of the input tokens." }), + "output_tokens": Schema.Number.annotate({ "description": "The number of output tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_tokens_details": Schema.Struct({ + "reasoning_tokens": Schema.Number.annotate({ "description": "The number of reasoning tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "A detailed breakdown of the output tokens." }), + "total_tokens": Schema.Number.annotate({ "description": "The total number of tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": + "Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n", + "identifier": "BetaResponseUsage" +}) +export type BetaCacheMissReasonTypeEnum = + | "model_changed" + | "prompt_cache_key_changed" + | "tools_changed" + | "text_format_changed" + | "reasoning_effort_changed" + | "verbosity_changed" + | "context_compacted" + | "input_changed" + | "service_tier_changed" +export const BetaCacheMissReasonTypeEnum = Schema.Literals([ + "model_changed", + "prompt_cache_key_changed", + "tools_changed", + "text_format_changed", + "reasoning_effort_changed", + "verbosity_changed", + "context_compacted", + "input_changed", + "service_tier_changed" +]).annotate({ "identifier": "BetaCacheMissReasonTypeEnum" }) +export type BetaPromptCacheHitDiagnosticsBody = { readonly "type": "cache_hit" } +export const BetaPromptCacheHitDiagnosticsBody = Schema.Struct({ "type": Schema.Literal("cache_hit") }).annotate({ + "identifier": "BetaPromptCacheHitDiagnosticsBody" +}) +export type BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody = { + readonly "type": "comparison_response_not_found" +} +export const BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody = Schema.Struct({ + "type": Schema.Literal("comparison_response_not_found") +}).annotate({ "identifier": "BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody" }) +export type BetaPromptCacheUnavailableDiagnosticsBody = { readonly "type": "unavailable" } +export const BetaPromptCacheUnavailableDiagnosticsBody = Schema.Struct({ "type": Schema.Literal("unavailable") }) + .annotate({ "identifier": "BetaPromptCacheUnavailableDiagnosticsBody" }) +export type BetaModerationInputType = "text" | "image" +export const BetaModerationInputType = Schema.Literals(["text", "image"]).annotate({ + "identifier": "BetaModerationInputType" +}) +export type BetaModerationErrorBody = { readonly "type": "error"; readonly "code": string; readonly "message": string } +export const BetaModerationErrorBody = Schema.Struct({ + "type": Schema.Literal("error").annotate({ + "description": "The object type, which was always `error` for moderation failures." + }), + "code": Schema.String.annotate({ "description": "The error code." }), + "message": Schema.String.annotate({ "description": "The error message." }) +}).annotate({ + "title": "Moderation error", + "description": "An error produced while attempting moderation for the response input or output.", + "identifier": "BetaModerationErrorBody" +}) +export type BetaResponseConversation = { readonly "id": string } +export const BetaResponseConversation = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The unique ID of the conversation that this response was associated with." + }) +}).annotate({ + "title": "Conversation", + "description": + "The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.", + "identifier": "BetaResponseConversation" +}) +export type BetaShellCallOutputDelta = { readonly "stdout"?: string; readonly "stderr"?: string } +export const BetaShellCallOutputDelta = Schema.Struct({ + "stdout": Schema.optionalKey(Schema.String.annotate({ "description": "The stdout delta that was emitted." })), + "stderr": Schema.optionalKey(Schema.String.annotate({ "description": "The stderr delta that was emitted." })) +}).annotate({ + "title": "Shell call output delta", + "description": "A delta of stdout/stderr emitted while a shell call was running.", + "identifier": "BetaShellCallOutputDelta" +}) +export type BetaResponseLogProb = { + readonly "token": string + readonly "logprob": number + readonly "top_logprobs"?: ReadonlyArray<{ readonly "token"?: string; readonly "logprob"?: number }> +} +export const BetaResponseLogProb = Schema.Struct({ + "token": Schema.String.annotate({ "description": "A possible text token." }), + "logprob": Schema.Number.annotate({ "description": "The log probability of this token.\n" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "top_logprobs": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "token": Schema.optionalKey(Schema.String.annotate({ "description": "A possible text token." })), + "logprob": Schema.optionalKey( + Schema.Number.annotate({ "description": "The log probability of this token." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ) + })).annotate({ "description": "The log probabilities of up to 20 of the most likely tokens.\n" }) + ) +}).annotate({ + "description": + "A logprob is the logarithmic probability that the model assigns to producing \na particular token at a given position in the sequence. Less-negative (higher) \nlogprob values indicate greater model confidence in that token choice.\n", + "identifier": "BetaResponseLogProb" +}) +export type BetaPromptCacheRetentionEnum = "in_memory" | "24h" +export const BetaPromptCacheRetentionEnum = Schema.Literals(["in_memory", "24h"]).annotate({ + "identifier": "BetaPromptCacheRetentionEnum" +}) +export type BetaServiceTierEnum = "auto" | "default" | "fast" | "flex" | "priority" +export const BetaServiceTierEnum = Schema.Literals(["auto", "default", "fast", "flex", "priority"]).annotate({ + "identifier": "BetaServiceTierEnum" +}) +export type BetaMessageStatus = "in_progress" | "completed" | "incomplete" +export const BetaMessageStatus = Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "identifier": "BetaMessageStatus" +}) +export type BetaMessagePhase_2 = "commentary" | "final_answer" +export const BetaMessagePhase_2 = Schema.Literals(["commentary", "final_answer"]).annotate({ + "identifier": "BetaMessagePhase-2" +}) +export type BetaTruncationEnum = "auto" | "disabled" +export const BetaTruncationEnum = Schema.Literals(["auto", "disabled"]).annotate({ "identifier": "BetaTruncationEnum" }) +export type BetaPersonalityEnum = string | "friendly" | "pragmatic" +export const BetaPersonalityEnum = Schema.Union([Schema.String, Schema.Literals(["friendly", "pragmatic"])]).annotate({ + "identifier": "BetaPersonalityEnum" +}) +export type BetaTokenCountsResource = { readonly "object": "response.input_tokens"; readonly "input_tokens": number } +export const BetaTokenCountsResource = Schema.Struct({ + "object": Schema.Literal("response.input_tokens"), + "input_tokens": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ "title": "Token counts", "identifier": "BetaTokenCountsResource" }) +export type FileSearchRankingOptions = { readonly "ranker"?: FileSearchRanker; readonly "score_threshold": number } +export const FileSearchRankingOptions = Schema.Struct({ + "ranker": Schema.optionalKey(FileSearchRanker), + "score_threshold": Schema.Number.annotate({ + "description": + "The score threshold for the file search. All values must be a floating point number between 0 and 1." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })) +}).annotate({ + "title": "File search tool call ranking options", + "description": + "The ranking options for the file search. If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.\n\nSee the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.\n", + "identifier": "FileSearchRankingOptions" +}) +export type RunStepDetailsToolCallsFileSearchRankingOptionsObject = { + readonly "ranker": FileSearchRanker + readonly "score_threshold": number +} +export const RunStepDetailsToolCallsFileSearchRankingOptionsObject = Schema.Struct({ + "ranker": FileSearchRanker, + "score_threshold": Schema.Number.annotate({ + "description": + "The score threshold for the file search. All values must be a floating point number between 0 and 1." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })) +}).annotate({ + "title": "File search tool call ranking options", + "description": "The ranking options for the file search.", + "identifier": "RunStepDetailsToolCallsFileSearchRankingOptionsObject" +}) +export type FunctionObject = { + readonly "description"?: string + readonly "name": string + readonly "parameters"?: FunctionParameters + readonly "strict"?: boolean | null +} +export const FunctionObject = Schema.Struct({ + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A description of what the function does, used by the model to choose when and how to call the function." + }) + ), + "name": Schema.String.annotate({ + "description": + "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }), + "parameters": Schema.optionalKey(FunctionParameters), + "strict": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](https://developers.openai.com/api/docs/guides/function-calling)." + }), + Schema.Null + ])) +}).annotate({ "identifier": "FunctionObject" }) +export type ChatCompletionFunctions = { + readonly "description"?: string + readonly "name": string + readonly "parameters"?: FunctionParameters +} +export const ChatCompletionFunctions = Schema.Struct({ + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A description of what the function does, used by the model to choose when and how to call the function." + }) + ), + "name": Schema.String.annotate({ + "description": + "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }), + "parameters": Schema.optionalKey(FunctionParameters) +}).annotate({ "identifier": "ChatCompletionFunctions" }) +export type EvalLogsDataSourceConfig = { + readonly "type": "logs" + readonly "metadata"?: Metadata + readonly "schema": { readonly [x: string]: Schema.Json } +} +export const EvalLogsDataSourceConfig = Schema.Struct({ + "type": Schema.Literal("logs").annotate({ "description": "The type of data source. Always `logs`." }), + "metadata": Schema.optionalKey(Metadata), + "schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n" + }) +}).annotate({ + "title": "LogsDataSourceConfig", + "description": + "A LogsDataSourceConfig which specifies the metadata property of your logs query.\nThis is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.\nThe schema returned by this data source config is used to defined what variables are available in your evals.\n`item` and `sample` are both defined when using this data source config.\n", + "identifier": "EvalLogsDataSourceConfig" +}) +export type EvalStoredCompletionsDataSourceConfig = { + readonly "type": "stored_completions" + readonly "metadata"?: Metadata + readonly "schema": { readonly [x: string]: Schema.Json } +} +export const EvalStoredCompletionsDataSourceConfig = Schema.Struct({ + "type": Schema.Literal("stored_completions").annotate({ + "description": "The type of data source. Always `stored_completions`." + }), + "metadata": Schema.optionalKey(Metadata), + "schema": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n" + }) +}).annotate({ + "title": "StoredCompletionsDataSourceConfig", + "description": "Deprecated in favor of LogsDataSourceConfig.\n", + "identifier": "EvalStoredCompletionsDataSourceConfig" +}) +export type EvalStoredCompletionsSource = { + readonly "type": "stored_completions" + readonly "metadata"?: Metadata + readonly "model"?: string | null + readonly "created_after"?: number | null + readonly "created_before"?: number | null + readonly "limit"?: number | null +} +export const EvalStoredCompletionsSource = Schema.Struct({ + "type": Schema.Literal("stored_completions").annotate({ + "description": "The type of source. Always `stored_completions`." + }), + "metadata": Schema.optionalKey(Metadata), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "An optional model to filter by (e.g., 'gpt-6-astra')." }), + Schema.Null + ]) + ), + "created_after": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "An optional Unix timestamp to filter items created after this time." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "created_before": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "An optional Unix timestamp to filter items created before this time." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "limit": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "An optional maximum number of items to return." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "StoredCompletionsRunDataSource", + "description": "A StoredCompletionsRunDataSource configuration describing a set of filters\n", + "identifier": "EvalStoredCompletionsSource" +}) +export type ThreadObject = { + readonly "id": string + readonly "object": "thread" + readonly "created_at": number + readonly "tool_resources": { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { readonly "vector_store_ids"?: ReadonlyArray } + } | null + readonly "metadata": Metadata +} +export const ThreadObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("thread").annotate({ "description": "The object type, which is always `thread`." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the thread was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "tool_resources": Schema.Union([ + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + })) + }).annotate({ + "description": + "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }), + Schema.Null + ]), + "metadata": Metadata +}).annotate({ + "title": "Thread", + "description": + "Represents a thread that contains [messages](https://developers.openai.com/api/docs/assistants/migration).", + "identifier": "ThreadObject" +}) +export type ModifyThreadRequest = { + readonly "tool_resources"?: { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { readonly "vector_store_ids"?: ReadonlyArray } + } | null + readonly "metadata"?: Metadata +} +export const ModifyThreadRequest = Schema.Struct({ + "tool_resources": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + })) + }).annotate({ + "description": + "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }), + Schema.Null + ])), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ "identifier": "ModifyThreadRequest" }) +export type ModifyMessageRequest = { readonly "metadata"?: Metadata } +export const ModifyMessageRequest = Schema.Struct({ "metadata": Schema.optionalKey(Metadata) }).annotate({ + "identifier": "ModifyMessageRequest" +}) +export type ModifyRunRequest = { readonly "metadata"?: Metadata } +export const ModifyRunRequest = Schema.Struct({ "metadata": Schema.optionalKey(Metadata) }).annotate({ + "identifier": "ModifyRunRequest" +}) +export type UpdateConversationBody = { readonly "metadata": Metadata } +export const UpdateConversationBody = Schema.Struct({ + "metadata": Schema.suspend((): Schema.Codec => Metadata).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.\n Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters." + }) +}).annotate({ "identifier": "UpdateConversationBody" }) +export type ResponseFormatJsonSchema = { + readonly "type": "json_schema" + readonly "json_schema": { + readonly "description"?: string + readonly "name": string + readonly "schema"?: ResponseFormatJsonSchemaSchema + readonly "strict"?: boolean | null + } +} +export const ResponseFormatJsonSchema = Schema.Struct({ + "type": Schema.Literal("json_schema").annotate({ + "description": "The type of response format being defined. Always `json_schema`." + }), + "json_schema": Schema.Struct({ + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + }) + ), + "name": Schema.String.annotate({ + "description": + "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + }), + "schema": Schema.optionalKey(ResponseFormatJsonSchemaSchema), + "strict": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](https://developers.openai.com/api/docs/guides/structured-outputs).\n" + }), + Schema.Null + ])) + }).annotate({ + "title": "JSON schema", + "description": "Structured Outputs configuration options, including a JSON Schema.\n" + }) +}).annotate({ + "title": "JSON schema", + "description": + "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).\n", + "identifier": "ResponseFormatJsonSchema" +}) +export type TextResponseFormatJsonSchema = { + readonly "type": "json_schema" + readonly "description"?: string + readonly "name": string + readonly "schema": ResponseFormatJsonSchemaSchema + readonly "strict"?: boolean | null +} +export const TextResponseFormatJsonSchema = Schema.Struct({ + "type": Schema.Literal("json_schema").annotate({ + "description": "The type of response format being defined. Always `json_schema`." + }), + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + }) + ), + "name": Schema.String.annotate({ + "description": + "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + }), + "schema": ResponseFormatJsonSchemaSchema, + "strict": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](https://developers.openai.com/api/docs/guides/structured-outputs).\n" + }), + Schema.Null + ])) +}).annotate({ + "title": "JSON schema", + "description": + "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).\n", + "identifier": "TextResponseFormatJsonSchema" +}) +export type MisalignmentErrorDetailsResource = { + readonly "review_target"?: string | null + readonly "error_type"?: _MisalignmentErrorType + readonly "detailed_explanation"?: string + readonly "steer"?: _MisalignmentSteer +} +export const MisalignmentErrorDetailsResource = Schema.Struct({ + "review_target": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "An opaque target for explicitly continuing this review, or null when unavailable." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(96).annotate({ "expected": "a string with at most 96 code points" }) + ).check( + Schema.isPattern(new RegExp("^[A-Za-z0-9._~:-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^[A-Za-z0-9._~:-]+$" + }) + ), + Schema.Null + ]) + ), + "error_type": Schema.optionalKey( + Schema.suspend((): Schema.Codec<_MisalignmentErrorType> => _MisalignmentErrorType).annotate({ + "description": "An optional classification; clients must accept additional values." + }) + ), + "detailed_explanation": Schema.optionalKey( + Schema.String.annotate({ "description": "The public explanation for this block." }) + ), + "steer": Schema.optionalKey( + Schema.suspend((): Schema.Codec<_MisalignmentSteer> => _MisalignmentSteer).annotate({ + "description": "An optional public continuation instruction." + }) + ) +}).annotate({ "identifier": "MisalignmentErrorDetailsResource" }) +export type ResponseConfigurationUpdate = { + readonly "id": string + readonly "type": "configuration_update" + readonly "reasoning"?: { readonly "effort"?: ReasoningEffort } +} +export const ResponseConfigurationUpdate = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the configuration update item." }), + "type": Schema.Literal("configuration_update").annotate({ + "description": "The item type. Always `configuration_update`." + }), + "reasoning": Schema.optionalKey( + Schema.Struct({ + "effort": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ReasoningEffort).annotate({ + "description": + "The reasoning effort used for subsequent responses until another\nconfiguration update replaces it.\n" + }) + ) + }).annotate({ "description": "The reasoning configuration applied by this update." }) + ) +}).annotate({ + "description": + "A configuration update that applies to subsequent responses until it is\nreplaced by another configuration update.\n", + "identifier": "ResponseConfigurationUpdate" +}) +export type EvalResponsesSource = { + readonly "type": "responses" + readonly "metadata"?: { readonly [x: string]: Schema.Json } | null + readonly "model"?: string | null + readonly "instructions_search"?: string | null + readonly "created_after"?: number | null + readonly "created_before"?: number | null + readonly "reasoning_effort"?: ReasoningEffort | null + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "users"?: ReadonlyArray | null + readonly "tools"?: ReadonlyArray | null +} +export const EvalResponsesSource = Schema.Struct({ + "type": Schema.Literal("responses").annotate({ "description": "The type of run data source. Always `responses`." }), + "metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Metadata filter for the responses. This is a query parameter used to select responses." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The name of the model to find responses for. This is a query parameter used to select responses." + }), + Schema.Null + ]) + ), + "instructions_search": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Optional string to search the 'instructions' field. This is a query parameter used to select responses." + }), + Schema.Null + ]) + ), + "created_after": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "Only include items created after this timestamp (inclusive). This is a query parameter used to select responses." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + Schema.Null + ]) + ), + "created_before": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "Only include items created before this timestamp (inclusive). This is a query parameter used to select responses." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + Schema.Null + ]) + ), + "reasoning_effort": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ReasoningEffort).annotate({ + "description": "Optional reasoning effort parameter. This is a query parameter used to select responses." + }), + Schema.Null + ]) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Sampling temperature. This is a query parameter used to select responses." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "Nucleus sampling parameter. This is a query parameter used to select responses." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "users": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ + "description": "List of user identifiers. This is a query parameter used to select responses." + }), + Schema.Null + ]) + ), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ + "description": "List of tool names. This is a query parameter used to select responses." + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "EvalResponsesSource", + "description": "A EvalResponsesSource object describing a run data source configuration.\n", + "identifier": "EvalResponsesSource" +}) +export type CreateSpeechRequest = { + readonly "model": string | "tts-1" | "tts-1-hd" | "gpt-4o-mini-tts" | "gpt-4o-mini-tts-2025-12-15" + readonly "input": string + readonly "instructions"?: string + readonly "voice": VoiceIdsShared | "fable" | "onyx" | "nova" | { readonly "id": string } + readonly "response_format"?: "mp3" | "opus" | "aac" | "flac" | "wav" | "pcm" + readonly "speed"?: number + readonly "stream_format"?: "sse" | "audio" +} +export const CreateSpeechRequest = Schema.Struct({ + "model": Schema.Union([ + Schema.String, + Schema.Literals(["tts-1", "tts-1-hd", "gpt-4o-mini-tts", "gpt-4o-mini-tts-2025-12-15"]) + ]).annotate({ + "description": + "One of the available [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.\n" + }), + "input": Schema.String.annotate({ + "description": "The text to generate audio for. The maximum length is 4096 characters." + }).check(Schema.isMaxCodePoints(4096).annotate({ "expected": "a string with at most 4096 code points" })), + "instructions": Schema.optionalKey( + Schema.String.annotate({ + "description": + "Control the voice of your generated audio with additional instructions. Does not work with `tts-1` or `tts-1-hd`." + }).check(Schema.isMaxCodePoints(4096).annotate({ "expected": "a string with at most 4096 code points" })) + ), + "voice": Schema.Union([ + Schema.Union([VoiceIdsShared, Schema.Literals(["fable", "onyx", "nova"])]), + Schema.Struct({ "id": Schema.String.annotate({ "description": "The custom voice ID, e.g. `voice_1234`." }) }) + .annotate({ "description": "Custom voice reference." }) + ]).annotate({ + "title": "Voice", + "description": + "The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice object with an `id`, for example `{ \"id\": \"voice_1234\" }`. Previews of the voices are available in the [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options). Custom voices must be created from audio samples." + }), + "response_format": Schema.optionalKey( + Schema.Literals(["mp3", "opus", "aac", "flac", "wav", "pcm"]).annotate({ + "description": "The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`." + }) + ), + "speed": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0.25).annotate({ "expected": "a value greater than or equal to 0.25" }) + ).check(Schema.isLessThanOrEqualTo(4).annotate({ "expected": "a value less than or equal to 4" })) + ), + "stream_format": Schema.optionalKey( + Schema.Literals(["sse", "audio"]).annotate({ + "description": + "The format to stream the audio in. Supported formats are `sse` and `audio`. `sse` is not supported for `tts-1` or `tts-1-hd`." + }) + ) +}).annotate({ "identifier": "CreateSpeechRequest" }) +export type VoiceIdsOrCustomVoice = VoiceIdsShared | { readonly "id": string } +export const VoiceIdsOrCustomVoice = Schema.Union([ + VoiceIdsShared, + Schema.Struct({ "id": Schema.String.annotate({ "description": "The custom voice ID, e.g. `voice_1234`." }) }) + .annotate({ "description": "Custom voice reference." }) +]).annotate({ + "title": "Voice", + "description": "A built-in voice name or a custom voice reference.\n", + "identifier": "VoiceIdsOrCustomVoice" +}) +export type CreateSpeechResponseStreamEvent = SpeechAudioDeltaEvent | SpeechAudioDoneEvent +export const CreateSpeechResponseStreamEvent = Schema.Union([SpeechAudioDeltaEvent, SpeechAudioDoneEvent]).annotate({ + "identifier": "CreateSpeechResponseStreamEvent" +}) +export type CreateTranslationRequestMultipart = { + readonly "file": __ClientMultipartFile + readonly "model": string | "whisper-1" + readonly "prompt"?: string + readonly "response_format"?: "json" | "text" | "srt" | "verbose_json" | "vtt" + readonly "temperature"?: number +} +export const CreateTranslationRequestMultipart = Schema.Struct({ + "file": __ClientMultipartFile, + "model": Schema.Union([Schema.String, Schema.Literal("whisper-1")]).annotate({ + "description": + "ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is currently available.\n" + }), + "prompt": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should be in English.\n" + }) + ), + "response_format": Schema.optionalKey( + Schema.Literals(["json", "text", "srt", "verbose_json", "vtt"]).annotate({ + "description": + "The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`.\n" + }) + ), + "temperature": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ) +}).annotate({ "identifier": "CreateTranslationRequestMultipart" }) +export type CreateVoiceConsentRequestMultipart = { + readonly "name": string + readonly "recording": __ClientMultipartFile + readonly "language": string +} +export const CreateVoiceConsentRequestMultipart = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The label to use for this consent recording." }), + "recording": __ClientMultipartFile, + "language": Schema.String.annotate({ + "description": "The BCP 47 language tag for the consent phrase (for example, `en-US`)." + }) +}).annotate({ "identifier": "CreateVoiceConsentRequestMultipart" }) +export type CreateVoiceFromConsentRequestMultipart = { + readonly "type"?: "audio_sample" + readonly "name": string + readonly "audio_sample": __ClientMultipartFile + readonly "consent": string +} +export const CreateVoiceFromConsentRequestMultipart = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("audio_sample").annotate({ + "description": "The voice creation method. Defaults to `audio_sample` when omitted." + }) + ), + "name": Schema.String.annotate({ "description": "The name of the new voice." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })), + "audio_sample": __ClientMultipartFile, + "consent": Schema.String.annotate({ "description": "The consent recording ID (for example, `cons_1234`)." }) +}).annotate({ + "title": "Consent", + "description": "Creates a voice from a consent recording and an audio sample. Requires multipart/form-data.", + "identifier": "CreateVoiceFromConsentRequestMultipart" +}) +export type CreateContainerFileBodyMultipart = { readonly "file_id"?: string; readonly "file"?: __ClientMultipartFile } +export const CreateContainerFileBodyMultipart = Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "Name of the file to create." })), + "file": Schema.optionalKey(__ClientMultipartFile) +}).annotate({ "identifier": "CreateContainerFileBodyMultipart" }) +export type CreateImageVariationRequestMultipart = { + readonly "image": __ClientMultipartFile + readonly "model"?: string | "dall-e-2" | null + readonly "n"?: number | null + readonly "response_format"?: "url" | "b64_json" | null + readonly "size"?: "256x256" | "512x512" | "1024x1024" | null + readonly "user"?: string +} +export const CreateImageVariationRequestMultipart = Schema.Struct({ + "image": __ClientMultipartFile, + "model": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Literal("dall-e-2"), Schema.Null]).annotate({ + "description": + "The legacy model used by the retired image variations endpoint. This endpoint no longer accepts requests." + }) + ), + "n": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(10).annotate({ "expected": "a value less than or equal to 10" })), + Schema.Null + ]).annotate({ "description": "The number of images to generate. Must be between 1 and 10." }) + ), + "response_format": Schema.optionalKey( + Schema.Union([Schema.Literals(["url", "b64_json"]), Schema.Null]).annotate({ + "description": + "The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated." + }) + ), + "size": Schema.optionalKey( + Schema.Union([Schema.Literals(["256x256", "512x512", "1024x1024"]), Schema.Null]).annotate({ + "description": "The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`." + }) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ) +}).annotate({ + "description": "Legacy request for the retired image variations endpoint.", + "identifier": "CreateImageVariationRequestMultipart" +}) +export type AddUploadPartRequestMultipart = { readonly "data": __ClientMultipartFile } +export const AddUploadPartRequestMultipart = Schema.Struct({ "data": __ClientMultipartFile }).annotate({ + "identifier": "AddUploadPartRequestMultipart" +}) +export type CreateContentProvenanceBodyMultipart = { readonly "file": __ClientMultipartFile } +export const CreateContentProvenanceBodyMultipart = Schema.Struct({ "file": __ClientMultipartFile }).annotate({ + "identifier": "CreateContentProvenanceBodyMultipart" +}) +export type CreateVideoCharacterBodyMultipart = { readonly "video": __ClientMultipartFile; readonly "name": string } +export const CreateVideoCharacterBodyMultipart = Schema.Struct({ + "video": __ClientMultipartFile, + "name": Schema.String.annotate({ "description": "Display name for this API character." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(80).annotate({ "expected": "a string with at most 80 code points" })) +}).annotate({ + "title": "Create character request", + "description": "Parameters for creating a character from an uploaded video.", + "identifier": "CreateVideoCharacterBodyMultipart" +}) +export type CreateSkillBodyMultipart = { + readonly "files": ReadonlyArray<__ClientMultipartFile> | __ClientMultipartFile +} +export const CreateSkillBodyMultipart = Schema.Struct({ + "files": Schema.Union([ + Schema.Array(__ClientMultipartFile).annotate({ + "description": "Skill files to upload (directory upload) or a single zip file." + }).check(Schema.isMaxLength(500).annotate({ "expected": "a value with a length of at most 500" })), + __ClientMultipartFile + ], { mode: "oneOf" }) +}).annotate({ + "title": "Create skill request", + "description": "Uploads a skill either as a directory (multipart `files[]`) or as a single zip file.", + "identifier": "CreateSkillBodyMultipart" +}) +export type CreateSkillVersionBodyMultipart = { + readonly "files": ReadonlyArray<__ClientMultipartFile> | __ClientMultipartFile + readonly "default"?: boolean +} +export const CreateSkillVersionBodyMultipart = Schema.Struct({ + "files": Schema.Union([ + Schema.Array(__ClientMultipartFile).annotate({ + "description": "Skill files to upload (directory upload) or a single zip file." + }).check(Schema.isMaxLength(500).annotate({ "expected": "a value with a length of at most 500" })), + __ClientMultipartFile + ], { mode: "oneOf" }), + "default": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether to set this version as the default." }) + ) +}).annotate({ + "title": "Create skill version request", + "description": "Uploads a new immutable version of a skill.", + "identifier": "CreateSkillVersionBodyMultipart" +}) +export type CreateTranscriptionRequestMultipart = { + readonly "file": __ClientMultipartFile + readonly "model": + | string + | "whisper-1" + | "gpt-transcribe" + | "gpt-4o-transcribe" + | "gpt-4o-mini-transcribe" + | "gpt-4o-mini-transcribe-2025-12-15" + | "gpt-4o-transcribe-diarize" + readonly "language"?: string + readonly "languages"?: ReadonlyArray + readonly "keywords"?: ReadonlyArray + readonly "prompt"?: string + readonly "response_format"?: AudioResponseFormat + readonly "temperature"?: number + readonly "include"?: ReadonlyArray + readonly "timestamp_granularities"?: ReadonlyArray<"word" | "segment"> + readonly "stream"?: boolean | null + readonly "chunking_strategy"?: "auto" | VadConfig | null + readonly "known_speaker_names"?: ReadonlyArray + readonly "known_speaker_references"?: ReadonlyArray +} +export const CreateTranscriptionRequestMultipart = Schema.Struct({ + "file": __ClientMultipartFile, + "model": Schema.Union([ + Schema.String, + Schema.Literals([ + "whisper-1", + "gpt-transcribe", + "gpt-4o-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe-diarize" + ]) + ]).annotate({ + "description": + "ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` (which is powered by our open source Whisper V2 model), and `gpt-4o-transcribe-diarize`.\n" + }), + "language": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.\n" + }) + ), + "languages": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Possible languages of the input audio, in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. Supported by `gpt-transcribe`.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ), + "keywords": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "Words or phrases to guide transcription of the input audio. Supported by `gpt-transcribe`.\n" + }) + ), + "prompt": Schema.optionalKey(Schema.String.annotate({ + "description": + "An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should match the audio language. This field is not supported when using `gpt-4o-transcribe-diarize`.\n" + })), + "response_format": Schema.optionalKey(AudioResponseFormat), + "temperature": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "include": Schema.optionalKey( + Schema.Array(TranscriptionInclude).annotate({ + "description": + "Additional information to include in the transcription response.\n`logprobs` will return the log probabilities of the tokens in the\nresponse to understand the model's confidence in the transcription.\n`logprobs` only works with response_format set to `json` and only with\nthe models `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is not supported when using `gpt-4o-transcribe-diarize`.\n" + }) + ), + "timestamp_granularities": Schema.optionalKey( + Schema.Array(Schema.Literals(["word", "segment"])).annotate({ + "description": + "The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.\nThis option is not available for `gpt-4o-transcribe-diarize`.\n" + }) + ), + "stream": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)\nfor more information.\n\nNote: Streaming is not supported for the `whisper-1` model and will be ignored.\n" + }), + Schema.Null + ])), + "chunking_strategy": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Literal("auto").annotate({ + "description": "Automatically set chunking parameters based on the audio. Must be set to `\"auto\"`.\n" + }), + VadConfig + ]).annotate({ + "description": + "Controls how the audio is cut into chunks. When set to `\"auto\"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 seconds. " + }), + Schema.Null + ]) + ), + "known_speaker_names": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for example `customer` or `agent`). Up to 4 speakers are supported.\n" + }).check(Schema.isMaxLength(4).annotate({ "expected": "a value with a length of at most 4" })) + ), + "known_speaker_references": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Optional list of audio samples (as [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) that contain known speaker references matching `known_speaker_names[]`. Each sample must be between 2 and 10 seconds, and can use any of the same input audio formats supported by `file`.\n" + }).check(Schema.isMaxLength(4).annotate({ "expected": "a value with a length of at most 4" })) + ) +}).annotate({ "identifier": "CreateTranscriptionRequestMultipart" }) +export type TranscriptTextDoneEvent = { + readonly "type": "transcript.text.done" + readonly "text": string + readonly "languages"?: ReadonlyArray + readonly "logprobs"?: ReadonlyArray< + { readonly "token"?: string; readonly "logprob"?: number; readonly "bytes"?: ReadonlyArray } + > + readonly "usage"?: TranscriptTextUsageTokens +} +export const TranscriptTextDoneEvent = Schema.Struct({ + "type": Schema.Literal("transcript.text.done").annotate({ + "description": "The type of the event. Always `transcript.text.done`.\n" + }), + "text": Schema.String.annotate({ "description": "The text that was transcribed.\n" }), + "languages": Schema.optionalKey( + Schema.Array(TranscriptionLanguage).annotate({ + "description": + "The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.\n" + }) + ), + "logprobs": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "token": Schema.optionalKey( + Schema.String.annotate({ "description": "The token that was used to generate the log probability.\n" }) + ), + "logprob": Schema.optionalKey( + Schema.Number.annotate({ "description": "The log probability of the token.\n" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ), + "bytes": Schema.optionalKey( + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).annotate({ + "description": "The bytes that were used to generate the log probability.\n" + }) + ) + })).annotate({ + "description": + "The log probabilities of the individual tokens in the transcription. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.\n" + }) + ), + "usage": Schema.optionalKey(TranscriptTextUsageTokens) +}).annotate({ + "description": + "Emitted when the transcription is complete. Contains the complete transcription text. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.", + "identifier": "TranscriptTextDoneEvent" +}) +export type CreateTranscriptionResponseJson = { + readonly "text": string + readonly "languages"?: ReadonlyArray + readonly "logprobs"?: ReadonlyArray< + { readonly "token"?: string; readonly "logprob"?: number; readonly "bytes"?: ReadonlyArray } + > + readonly "usage"?: TranscriptTextUsageTokens | TranscriptTextUsageDuration +} +export const CreateTranscriptionResponseJson = Schema.Struct({ + "text": Schema.String.annotate({ "description": "The transcribed text." }), + "languages": Schema.optionalKey( + Schema.Array(TranscriptionLanguage).annotate({ + "description": + "The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.\n" + }) + ), + "logprobs": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "token": Schema.optionalKey(Schema.String.annotate({ "description": "The token in the transcription." })), + "logprob": Schema.optionalKey( + Schema.Number.annotate({ "description": "The log probability of the token." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ), + "bytes": Schema.optionalKey(Schema.Array(Schema.Finite).annotate({ "description": "The bytes of the token." })) + })).annotate({ + "description": + "The log probabilities of the tokens in the transcription. Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` if `logprobs` is added to the `include` array.\n" + }) + ), + "usage": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => TranscriptTextUsageTokens).annotate({ + "title": "Token Usage" + }), + Schema.suspend((): Schema.Codec => TranscriptTextUsageDuration).annotate({ + "title": "Duration Usage" + }) + ], { mode: "oneOf" }).annotate({ "description": "Token usage statistics for the request." }) + ) +}).annotate({ + "description": "Represents a transcription response returned by model, based on the provided input.", + "identifier": "CreateTranscriptionResponseJson" +}) +export type CreateTranscriptionResponseDiarizedJson = { + readonly "task": "transcribe" + readonly "duration": number + readonly "text": string + readonly "segments": ReadonlyArray + readonly "usage"?: TranscriptTextUsageTokens | TranscriptTextUsageDuration +} +export const CreateTranscriptionResponseDiarizedJson = Schema.Struct({ + "task": Schema.Literal("transcribe").annotate({ + "description": "The type of task that was run. Always `transcribe`." + }), + "duration": Schema.Number.annotate({ "description": "Duration of the input audio in seconds.", "format": "double" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "text": Schema.String.annotate({ "description": "The concatenated transcript text for the entire audio input." }), + "segments": Schema.Array(TranscriptionDiarizedSegment).annotate({ + "description": "Segments of the transcript annotated with timestamps and speaker labels." + }), + "usage": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => TranscriptTextUsageTokens).annotate({ + "title": "Token Usage" + }), + Schema.suspend((): Schema.Codec => TranscriptTextUsageDuration).annotate({ + "title": "Duration Usage" + }) + ], { mode: "oneOf" }).annotate({ "description": "Token or duration usage statistics for the request." }) + ) +}).annotate({ + "description": + "Represents a diarized transcription response returned by the model, including the combined transcript and speaker-segment annotations.\n", + "identifier": "CreateTranscriptionResponseDiarizedJson" +}) +export type CreateTranscriptionResponseVerboseJson = { + readonly "language": string + readonly "duration": number + readonly "text": string + readonly "words"?: ReadonlyArray + readonly "segments"?: ReadonlyArray + readonly "usage"?: TranscriptTextUsageDuration +} +export const CreateTranscriptionResponseVerboseJson = Schema.Struct({ + "language": Schema.String.annotate({ "description": "The language of the input audio." }), + "duration": Schema.Number.annotate({ "description": "The duration of the input audio.", "format": "double" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "text": Schema.String.annotate({ "description": "The transcribed text." }), + "words": Schema.optionalKey( + Schema.Array(TranscriptionWord).annotate({ "description": "Extracted words and their corresponding timestamps." }) + ), + "segments": Schema.optionalKey( + Schema.Array(TranscriptionSegment).annotate({ + "description": "Segments of the transcribed text and their corresponding details." + }) + ), + "usage": Schema.optionalKey(TranscriptTextUsageDuration) +}).annotate({ + "description": "Represents a verbose json transcription response returned by model, based on the provided input.", + "identifier": "CreateTranscriptionResponseVerboseJson" +}) +export type CreateTranslationResponseVerboseJson = { + readonly "language": string + readonly "duration": number + readonly "text": string + readonly "segments"?: ReadonlyArray +} +export const CreateTranslationResponseVerboseJson = Schema.Struct({ + "language": Schema.String.annotate({ "description": "The language of the output translation (always `english`)." }), + "duration": Schema.Number.annotate({ "description": "The duration of the input audio.", "format": "double" }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "text": Schema.String.annotate({ "description": "The translated text." }), + "segments": Schema.optionalKey( + Schema.Array(TranscriptionSegment).annotate({ + "description": "Segments of the translated text and their corresponding details." + }) + ) +}).annotate({ "identifier": "CreateTranslationResponseVerboseJson" }) +export type VoiceConsentListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const VoiceConsentListResource = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(VoiceConsentResource), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "VoiceConsentListResource" }) +export type Batch = { + readonly "id": string + readonly "object": "batch" + readonly "endpoint": string + readonly "model"?: string + readonly "errors"?: + | { readonly "object"?: string; readonly "data"?: ReadonlyArray } & { + readonly [x: string]: Schema.Json + } + | null + readonly "input_file_id": string + readonly "completion_window": string + readonly "status": + | "validating" + | "failed" + | "in_progress" + | "finalizing" + | "completed" + | "expired" + | "cancelling" + | "cancelled" + readonly "output_file_id"?: string | null + readonly "error_file_id"?: string | null + readonly "created_at": number + readonly "in_progress_at"?: number | null + readonly "expires_at"?: number | null + readonly "finalizing_at"?: number | null + readonly "completed_at"?: number | null + readonly "failed_at"?: number | null + readonly "expired_at"?: number | null + readonly "cancelling_at"?: number | null + readonly "cancelled_at"?: number | null + readonly "request_counts"?: BatchRequestCounts + readonly "usage"?: { + readonly "input_tokens": number + readonly "input_tokens_details": { readonly "cached_tokens": number } + readonly "output_tokens": number + readonly "output_tokens_details": { readonly "reasoning_tokens": number } + readonly "total_tokens": number + } + readonly "metadata"?: Metadata +} +export const Batch = Schema.Struct({ + "id": Schema.String, + "object": Schema.Literal("batch").annotate({ "description": "The object type, which is always `batch`." }), + "endpoint": Schema.String.annotate({ "description": "The OpenAI API endpoint used by the batch." }), + "model": Schema.optionalKey(Schema.String.annotate({ + "description": + "Model ID used to process the batch, like `gpt-6-astra`. OpenAI\noffers a wide range of models with different capabilities, performance\ncharacteristics, and price points. Refer to the [model\nguide](https://developers.openai.com/api/docs/models) to browse and compare available models.\n" + })), + "errors": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "object": Schema.optionalKey( + Schema.String.annotate({ "description": "The object type, which is always `list`." }) + ), + "data": Schema.optionalKey(Schema.Array(BatchError)) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]) + ), + "input_file_id": Schema.String.annotate({ "description": "The ID of the input file for the batch." }), + "completion_window": Schema.String.annotate({ + "description": "The time frame within which the batch should be processed." + }), + "status": Schema.Literals([ + "validating", + "failed", + "in_progress", + "finalizing", + "completed", + "expired", + "cancelling", + "cancelled" + ]).annotate({ "description": "The current status of the batch." }), + "output_file_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The ID of the file containing the outputs of successfully executed requests." + }) + ), + "error_file_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The ID of the file containing the outputs of requests with errors." + }) + ), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the batch was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "in_progress_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch started processing.", + "format": "unixtime" + }) + ), + "expires_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch will expire.", + "format": "unixtime" + }) + ), + "finalizing_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch started finalizing.", + "format": "unixtime" + }) + ), + "completed_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch was completed.", + "format": "unixtime" + }) + ), + "failed_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch failed.", + "format": "unixtime" + }) + ), + "expired_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch expired.", + "format": "unixtime" + }) + ), + "cancelling_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch started cancelling.", + "format": "unixtime" + }) + ), + "cancelled_at": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": "The Unix timestamp (in seconds) for when the batch was cancelled.", + "format": "unixtime" + }) + ), + "request_counts": Schema.optionalKey(BatchRequestCounts), + "usage": Schema.optionalKey( + Schema.Struct({ + "input_tokens": Schema.Number.annotate({ "description": "The number of input tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "input_tokens_details": Schema.Struct({ + "cached_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that were retrieved from the cache. [More on\nprompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ "description": "A detailed breakdown of the input tokens." }), + "output_tokens": Schema.Number.annotate({ "description": "The number of output tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_tokens_details": Schema.Struct({ + "reasoning_tokens": Schema.Number.annotate({ "description": "The number of reasoning tokens." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "A detailed breakdown of the output tokens." }), + "total_tokens": Schema.Number.annotate({ "description": "The total number of tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ + "description": + "Represents token usage details including input tokens, output tokens, a\nbreakdown of output tokens, and the total tokens used. Only populated on\nbatches created after September 7, 2025.\n" + }) + ), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ "identifier": "Batch" }) +export type CreateBatchRequest = { + readonly "input_file_id": string + readonly "endpoint": + | "/v1/responses" + | "/v1/chat/completions" + | "/v1/embeddings" + | "/v1/completions" + | "/v1/moderations" + | "/v1/images/generations" + | "/v1/images/edits" + | "/v1/videos" + readonly "completion_window": "24h" + readonly "metadata"?: Metadata + readonly "output_expires_after"?: BatchFileExpirationAfter +} +export const CreateBatchRequest = Schema.Struct({ + "input_file_id": Schema.String.annotate({ + "description": + "The ID of an uploaded file that contains requests for the new batch.\n\nSee [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.\n\nYour input file must be formatted as a [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size.\n" + }), + "endpoint": Schema.Literals([ + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", + "/v1/videos" + ]).annotate({ + "description": + "The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and `/v1/videos` are supported. Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding inputs across all requests in the batch." + }), + "completion_window": Schema.Literal("24h").annotate({ + "description": "The time frame within which the batch should be processed. Currently only `24h` is supported." + }), + "metadata": Schema.optionalKey(Metadata), + "output_expires_after": Schema.optionalKey(BatchFileExpirationAfter) +}).annotate({ "identifier": "CreateBatchRequest" }) +export type ChatCompletionMessageToolCalls = ReadonlyArray< + ChatCompletionMessageToolCall | ChatCompletionMessageCustomToolCall +> +export const ChatCompletionMessageToolCalls = Schema.Array( + Schema.Union([ChatCompletionMessageToolCall, ChatCompletionMessageCustomToolCall], { mode: "oneOf" }) +).annotate({ + "description": "The tool calls generated by the model, such as function calls.", + "identifier": "ChatCompletionMessageToolCalls" +}) +export type CreateCompletionResponse = { + readonly "id": string + readonly "choices": ReadonlyArray< + { + readonly "finish_reason": "stop" | "length" | "content_filter" | null + readonly "index": number + readonly "logprobs": { + readonly "text_offset"?: ReadonlyArray + readonly "token_logprobs"?: ReadonlyArray + readonly "tokens"?: ReadonlyArray + readonly "top_logprobs"?: ReadonlyArray<{ readonly [x: string]: number }> + } | null + readonly "text": string + } + > + readonly "created": number + readonly "model": string + readonly "system_fingerprint"?: string + readonly "object": "text_completion" + readonly "usage"?: CompletionUsage +} +export const CreateCompletionResponse = Schema.Struct({ + "id": Schema.String.annotate({ "description": "A unique identifier for the completion." }), + "choices": Schema.Array(Schema.Struct({ + "finish_reason": Schema.Union([Schema.Literals(["stop", "length", "content_filter"]), Schema.Null]).annotate({ + "description": + "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\nor `content_filter` if content was omitted due to a flag from our content filters. The value is null while a streamed completion is unfinished.\n" + }), + "index": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "logprobs": Schema.Union([ + Schema.Struct({ + "text_offset": Schema.optionalKey( + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) + ), + "token_logprobs": Schema.optionalKey(Schema.Array(Schema.Finite)), + "tokens": Schema.optionalKey(Schema.Array(Schema.String)), + "top_logprobs": Schema.optionalKey(Schema.Array(Schema.Record(Schema.String, Schema.Finite))) + }), + Schema.Null + ]), + "text": Schema.String + })).annotate({ "description": "The list of completion choices the model generated for the input prompt." }), + "created": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the completion was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "model": Schema.String.annotate({ "description": "The model used for completion." }), + "system_fingerprint": Schema.optionalKey( + Schema.String.annotate({ + "description": + "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + }) + ), + "object": Schema.Literal("text_completion").annotate({ + "description": "The object type, which is always \"text_completion\"" + }), + "usage": Schema.optionalKey(CompletionUsage) +}).annotate({ + "description": + "Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint).\n", + "identifier": "CreateCompletionResponse" +}) +export type ModerationResultBody = { + readonly "type": "moderation_result" + readonly "model": string + readonly "flagged": boolean + readonly "categories": { readonly [x: string]: boolean } + readonly "category_scores": { readonly [x: string]: number } + readonly "category_applied_input_types": { readonly [x: string]: ReadonlyArray } +} +export const ModerationResultBody = Schema.Struct({ + "type": Schema.Literal("moderation_result").annotate({ + "description": "The object type, which was always `moderation_result` for successful moderation results." + }), + "model": Schema.String.annotate({ "description": "The moderation model that produced this result." }), + "flagged": Schema.Boolean.annotate({ + "description": "A boolean indicating whether the content was flagged by any category." + }), + "categories": Schema.Record(Schema.String, Schema.Boolean).annotate({ + "description": + "A dictionary of moderation categories to booleans, True if the input is flagged under this category." + }), + "category_scores": Schema.Record(Schema.String, Schema.Finite).annotate({ + "description": "A dictionary of moderation categories to scores." + }), + "category_applied_input_types": Schema.Record(Schema.String, Schema.Array(ModerationInputType)).annotate({ + "description": "Which modalities of input are reflected by the score for each category." + }) +}).annotate({ + "title": "Moderation result", + "description": "A moderation result produced for the response input or output.", + "identifier": "ModerationResultBody" +}) +export type PromptCacheOptionsParam = { readonly "ttl"?: PromptCacheTTLEnum; readonly "mode"?: PromptCacheModeEnum } +export const PromptCacheOptionsParam = Schema.Struct({ + "ttl": Schema.optionalKey( + Schema.suspend((): Schema.Codec => PromptCacheTTLEnum).annotate({ + "description": + "The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer." + }) + ), + "mode": Schema.optionalKey( + Schema.suspend((): Schema.Codec => PromptCacheModeEnum).annotate({ + "description": + "Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching." + }) + ) +}).annotate({ + "title": "Prompt cache options", + "description": + "Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.", + "identifier": "PromptCacheOptionsParam" +}) +export type PromptCacheOptions = { + readonly "ttl": PromptCacheTTLEnum + readonly "mode": PromptCacheModeEnum + readonly "comparison_response_id"?: string | null +} +export const PromptCacheOptions = Schema.Struct({ + "ttl": Schema.suspend((): Schema.Codec => PromptCacheTTLEnum).annotate({ + "description": "The minimum lifetime applied to each cache breakpoint." + }), + "mode": Schema.suspend((): Schema.Codec => PromptCacheModeEnum).annotate({ + "description": "Whether implicit prompt-cache breakpoints were enabled." + }), + "comparison_response_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The response ID supplied as the prompt cache diagnostics comparison." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Prompt cache options", + "description": + "The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.", + "identifier": "PromptCacheOptions" +}) +export type ChatCompletionRequestMessageContentPartText = { + readonly "type": "text" + readonly "text": string + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam +} +export const ChatCompletionRequestMessageContentPartText = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "The type of the content part." }), + "text": Schema.String.annotate({ "description": "The text content." }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointParam) +}).annotate({ + "title": "Text content part", + "description": "Learn about [text inputs](https://developers.openai.com/api/docs/guides/text).\n", + "identifier": "ChatCompletionRequestMessageContentPartText" +}) +export type ChatCompletionRequestMessageContentPartImage = { + readonly "type": "image_url" + readonly "image_url": { readonly "url": string; readonly "detail"?: "auto" | "low" | "high" | "original" } + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam +} +export const ChatCompletionRequestMessageContentPartImage = Schema.Struct({ + "type": Schema.Literal("image_url").annotate({ "description": "The type of the content part." }), + "image_url": Schema.Struct({ + "url": Schema.String.annotate({ + "description": "Either a URL of the image or the base64 encoded image data.", + "format": "uri" + }), + "detail": Schema.optionalKey( + Schema.Literals(["auto", "low", "high", "original"]).annotate({ + "description": + "Specifies the detail level of the image. Learn more in the [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level)." + }) + ) + }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointParam) +}).annotate({ + "title": "Image content part", + "description": "Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).\n", + "identifier": "ChatCompletionRequestMessageContentPartImage" +}) +export type ChatCompletionRequestMessageContentPartAudio = { + readonly "type": "input_audio" + readonly "input_audio": { readonly "data": string; readonly "format": "wav" | "mp3" } + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam +} +export const ChatCompletionRequestMessageContentPartAudio = Schema.Struct({ + "type": Schema.Literal("input_audio").annotate({ + "description": "The type of the content part. Always `input_audio`." + }), + "input_audio": Schema.Struct({ + "data": Schema.String.annotate({ "description": "Base64 encoded audio data." }), + "format": Schema.Literals(["wav", "mp3"]).annotate({ + "description": "The format of the encoded audio data. Currently supports \"wav\" and \"mp3\".\n" + }) + }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointParam) +}).annotate({ + "title": "Audio content part", + "description": "Learn about [audio inputs](https://developers.openai.com/api/docs/guides/audio).\n", + "identifier": "ChatCompletionRequestMessageContentPartAudio" +}) +export type ChatCompletionRequestMessageContentPartFile = { + readonly "type": "file" + readonly "file": { readonly "filename"?: string; readonly "file_data"?: string; readonly "file_id"?: string } + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam +} +export const ChatCompletionRequestMessageContentPartFile = Schema.Struct({ + "type": Schema.Literal("file").annotate({ "description": "The type of the content part. Always `file`." }), + "file": Schema.Struct({ + "filename": Schema.optionalKey( + Schema.String.annotate({ + "description": "The name of the file, used when passing the file to the model as a\nstring.\n" + }) + ), + "file_data": Schema.optionalKey( + Schema.String.annotate({ + "description": "The base64 encoded file data, used when passing the file to the model\nas a string.\n" + }) + ), + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of an uploaded file to use as input.\n" }) + ) + }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointParam) +}).annotate({ + "title": "File content part", + "description": "Learn about [file inputs](https://developers.openai.com/api/docs/guides/text) for text generation.\n", + "identifier": "ChatCompletionRequestMessageContentPartFile" +}) +export type InputTextContentParam = { + readonly "type": "input_text" + readonly "text": string + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam | null +} +export const InputTextContentParam = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the input item. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text input to the model." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "prompt_cache_breakpoint": Schema.optionalKey(Schema.Union([PromptCacheBreakpointParam, Schema.Null])) +}).annotate({ + "title": "Input text", + "description": "A text input to the model.", + "identifier": "InputTextContentParam" +}) +export type ModelIdsResponses = + | ModelIdsShared + | "o1-pro" + | "o1-pro-2025-03-19" + | "o3-pro" + | "o3-pro-2025-06-10" + | "o3-deep-research" + | "o3-deep-research-2025-06-26" + | "o4-mini-deep-research" + | "o4-mini-deep-research-2025-06-26" + | "computer-use-preview" + | "computer-use-preview-2025-03-11" + | "gpt-5.5-pro" + | "gpt-5.5-pro-2026-04-23" + | "gpt-5-codex" + | "gpt-5-pro" + | "gpt-5-pro-2025-10-06" + | "gpt-5.1-codex-max" + | "gpt-daybreak-blue-latest" + | "gpt-daybreak-red-latest" + | "gpt-5.6-cyber" + | "gpt-rosalind-research" +export const ModelIdsResponses = Schema.Union([ + ModelIdsShared, + Schema.Literals([ + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research" + ]).annotate({ "title": "ResponsesOnlyModel" }) +]).annotate({ "identifier": "ModelIdsResponses" }) +export type ModerationConfigParam = { readonly "mode": ModerationMode } +export const ModerationConfigParam = Schema.Struct({ "mode": ModerationMode }).annotate({ + "description": "The moderation policy for the response input.", + "identifier": "ModerationConfigParam" +}) +export type CreateCompletionRequest = { + readonly "model": string | "gpt-3.5-turbo-instruct" | "davinci-002" | "babbage-002" + readonly "prompt": + | string + | ReadonlyArray + | ReadonlyArray + | ReadonlyArray> + | null + readonly "best_of"?: number | null + readonly "echo"?: boolean | null + readonly "frequency_penalty"?: number | null + readonly "logit_bias"?: { readonly [x: string]: number } | null + readonly "logprobs"?: number | null + readonly "max_tokens"?: number | null + readonly "n"?: number | null + readonly "presence_penalty"?: number | null + readonly "seed"?: number | null + readonly "stop"?: StopConfiguration + readonly "stream"?: boolean | null + readonly "stream_options"?: ChatCompletionStreamOptions + readonly "suffix"?: string | null + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "user"?: string +} +export const CreateCompletionRequest = Schema.Struct({ + "model": Schema.Union([Schema.String, Schema.Literals(["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"])]) + .annotate({ + "description": + "ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.\n" + }), + "prompt": Schema.Union([ + Schema.String, + Schema.Array(Schema.String), + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Array( + Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) + ).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + Schema.Null + ], { mode: "oneOf" }).annotate({ + "description": + "The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.\n\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document.\n" + }), + "best_of": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" })), + Schema.Null + ]).annotate({ + "description": + "Generates `best_of` completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\n\nWhen used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + }) + ), + "echo": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": "Echo back the prompt in addition to the completion\n" + }) + ), + "frequency_penalty": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(-2).annotate({ "expected": "a value greater than or equal to -2" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n\n[See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)\n" + }) + ), + "logit_bias": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + Schema.Null + ]).annotate({ + "description": + "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\n\nAs an example, you can pass `{\"50256\": -100}` to prevent the <|endoftext|> token from being generated.\n" + }) + ), + "logprobs": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(5).annotate({ "expected": "a value less than or equal to 5" })), + Schema.Null + ]).annotate({ + "description": + "Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response.\n\nThe maximum value for `logprobs` is 5.\n" + }) + ), + "max_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ), + Schema.Null + ]).annotate({ + "description": + "The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion.\n\nThe token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.\n" + }) + ), + "n": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(128).annotate({ "expected": "a value less than or equal to 128" })), + Schema.Null + ]).annotate({ + "description": + "How many completions to generate for each prompt.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + }) + ), + "presence_penalty": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(-2).annotate({ "expected": "a value greater than or equal to -2" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n\n[See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)\n" + }) + ), + "seed": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": + "If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\n\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n", + "format": "int64" + }) + ), + "stop": Schema.optionalKey(StopConfiguration), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).\n" + }) + ), + "stream_options": Schema.optionalKey(ChatCompletionStreamOptions), + "suffix": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "The suffix that comes after a completion of inserted text.\n\nThis parameter is only supported for `gpt-3.5-turbo-instruct`.\n" + }) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or `top_p` but not both.\n" + }) + ), + "top_p": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ]).annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ) +}).annotate({ "identifier": "CreateCompletionRequest" }) +export type ChatCompletionAllowedToolsChoice = { + readonly "type": "allowed_tools" + readonly "allowed_tools": ChatCompletionAllowedTools +} +export const ChatCompletionAllowedToolsChoice = Schema.Struct({ + "type": Schema.Literal("allowed_tools").annotate({ + "description": "Allowed tool configuration type. Always `allowed_tools`." + }), + "allowed_tools": ChatCompletionAllowedTools +}).annotate({ + "title": "Allowed tools", + "description": "Constrains the tools available to the model to a pre-defined set.\n", + "identifier": "ChatCompletionAllowedToolsChoice" +}) +export type ChatCompletionStreamResponseDelta = { + readonly "audio"?: { + readonly "id"?: string + readonly "expires_at"?: number + readonly "data"?: string + readonly "transcript"?: string + } + readonly "content"?: string | null + readonly "function_call"?: { readonly "arguments"?: string; readonly "name"?: string } + readonly "tool_calls"?: ReadonlyArray + readonly "role"?: "developer" | "system" | "user" | "assistant" | "tool" + readonly "refusal"?: string | null +} +export const ChatCompletionStreamResponseDelta = Schema.Struct({ + "audio": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "Unique identifier for this audio response." })), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when this audio response will no\nlonger be accessible on the server for use in multi-turn conversations.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "data": Schema.optionalKey( + Schema.String.annotate({ + "description": "Base64 encoded audio bytes generated by the model, in the format\nspecified in the request.\n" + }) + ), + "transcript": Schema.optionalKey( + Schema.String.annotate({ "description": "Transcript text from this audio chunk." }) + ) + }).annotate({ + "description": + "A partial audio response. Audio chunks may carry an ID, base64 data, or\ntranscript text; the final audio update contains only its expiry timestamp.\n" + }) + ), + "content": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The contents of the chunk message." }), Schema.Null]) + ), + "function_call": Schema.optionalKey( + Schema.Struct({ + "arguments": Schema.optionalKey(Schema.String.annotate({ + "description": + "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the function to call." })) + }).annotate({ + "description": + "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model." + }) + ), + "tool_calls": Schema.optionalKey(Schema.Array(ChatCompletionMessageToolCallChunk)), + "role": Schema.optionalKey( + Schema.Literals(["developer", "system", "user", "assistant", "tool"]).annotate({ + "description": "The role of the author of this message." + }) + ), + "refusal": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The refusal message generated by the model." }), + Schema.Null + ]) + ) +}).annotate({ + "description": + "A chat completion delta generated by streamed model responses.\nStreamed audio can arrive in partial updates with an ID, base64 data, or\ntranscript text. The final audio update contains only its expiry timestamp.\n", + "identifier": "ChatCompletionStreamResponseDelta" +}) +export type ContainerListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const ContainerListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be 'list'." }), + "data": Schema.Array(ContainerResource).annotate({ "description": "A list of containers." }), + "first_id": Schema.String.annotate({ "description": "The ID of the first container in the list." }), + "last_id": Schema.String.annotate({ "description": "The ID of the last container in the list." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more containers available." }) +}).annotate({ "identifier": "ContainerListResource" }) +export type InlineSkillParam = { + readonly "type": "inline" + readonly "name": string + readonly "description": string + readonly "source": InlineSkillSourceParam +} +export const InlineSkillParam = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "Defines an inline skill for this request." }), + "name": Schema.String.annotate({ "description": "The name of the skill." }), + "description": Schema.String.annotate({ "description": "The description of the skill." }), + "source": Schema.suspend((): Schema.Codec => InlineSkillSourceParam).annotate({ + "description": "Inline skill payload" + }) +}).annotate({ "identifier": "InlineSkillParam" }) +export type ContainerNetworkPolicyAllowlistParam = { + readonly "type": "allowlist" + readonly "allowed_domains": ReadonlyArray + readonly "domain_secrets"?: ReadonlyArray +} +export const ContainerNetworkPolicyAllowlistParam = Schema.Struct({ + "type": Schema.Literal("allowlist").annotate({ + "description": "Allow outbound network access only to specified domains. Always `allowlist`." + }), + "allowed_domains": Schema.Array(Schema.String).annotate({ + "description": "A list of allowed domains when type is `allowlist`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "domain_secrets": Schema.optionalKey( + Schema.Array(ContainerNetworkPolicyDomainSecretParam).annotate({ + "description": "Optional domain-scoped secrets for allowlisted domains." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ) +}).annotate({ "identifier": "ContainerNetworkPolicyAllowlistParam" }) +export type ContainerFileListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const ContainerFileListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be 'list'." }), + "data": Schema.Array(ContainerFileResource).annotate({ "description": "A list of container files." }), + "first_id": Schema.String.annotate({ "description": "The ID of the first file in the list." }), + "last_id": Schema.String.annotate({ "description": "The ID of the last file in the list." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more files available." }) +}).annotate({ "identifier": "ContainerFileListResource" }) +export type InputTextContent = { + readonly "type": "input_text" + readonly "text": string + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointConfig +} +export const InputTextContent = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the input item. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text input to the model." }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointConfig) +}).annotate({ "title": "Input text", "description": "A text input to the model.", "identifier": "InputTextContent" }) +export type Annotation = FileCitationBody | UrlCitationBody | ContainerFileCitationBody | FilePath +export const Annotation = Schema.Union([FileCitationBody, UrlCitationBody, ContainerFileCitationBody, FilePath], { + mode: "oneOf" +}).annotate({ "description": "An annotation that applies to a span of output text.", "identifier": "Annotation" }) +export type LogProb = { + readonly "token": string + readonly "logprob": number + readonly "bytes": ReadonlyArray + readonly "top_logprobs": ReadonlyArray +} +export const LogProb = Schema.Struct({ + "token": Schema.String, + "logprob": Schema.Finite, + "bytes": Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "top_logprobs": Schema.Array(TopLogProb) +}).annotate({ "title": "Log probability", "description": "The log probability of a token.", "identifier": "LogProb" }) +export type ReasoningItem = { + readonly "type": "reasoning" + readonly "id": string + readonly "encrypted_content"?: string | null + readonly "summary": ReadonlyArray + readonly "content"?: ReadonlyArray + readonly "status"?: "in_progress" | "completed" | "incomplete" +} +export const ReasoningItem = Schema.Struct({ + "type": Schema.Literal("reasoning").annotate({ "description": "The type of the object. Always `reasoning`.\n" }), + "id": Schema.String.annotate({ "description": "The unique identifier of the reasoning content.\n" }), + "encrypted_content": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The encrypted content of the reasoning item. This is populated by default\nfor reasoning items returned by `POST /v1/responses` and WebSocket\n`response.create` requests.\n\nWhen streaming, use the completed reasoning item and its\n`encrypted_content` from the `response.output_item.done` event in\nsubsequent requests. The `encrypted_content` in\n`response.output_item.added` may be incomplete. This is especially\nimportant when `store` is `false` or when using Zero Data Retention.\n" + }), + Schema.Null + ])), + "summary": Schema.Array(SummaryTextContent).annotate({ "description": "Reasoning summary content.\n" }), + "content": Schema.optionalKey( + Schema.Array(ReasoningTextContent).annotate({ "description": "Reasoning text content.\n" }) + ), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ) +}).annotate({ + "title": "Reasoning", + "description": + "A description of the chain of thought used by a reasoning model while generating\na response. Be sure to include these items in your `input` to the Responses API\nfor subsequent turns of a conversation if you are manually\n[managing context](https://developers.openai.com/api/docs/guides/conversation-state).\n", + "identifier": "ReasoningItem" +}) +export type InputImageContent = { + readonly "type": "input_image" + readonly "image_url"?: string | null + readonly "file_id"?: string | null + readonly "detail": ImageDetail + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointConfig +} +export const InputImageContent = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ + "description": "The type of the input item. Always `input_image`." + }), + "image_url": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.", + "format": "uri" + }), + Schema.Null + ]) + ), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "detail": Schema.suspend((): Schema.Codec => ImageDetail).annotate({ + "description": + "The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`." + }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointConfig) +}).annotate({ + "title": "Input image", + "description": + "An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).", + "identifier": "InputImageContent" +}) +export type ComputerScreenshotContent = { + readonly "type": "computer_screenshot" + readonly "image_url": string | null + readonly "file_id": string | null + readonly "detail": ImageDetail + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointConfig +} +export const ComputerScreenshotContent = Schema.Struct({ + "type": Schema.Literal("computer_screenshot").annotate({ + "description": + "Specifies the event type. For a computer screenshot, this property is always set to `computer_screenshot`." + }), + "image_url": Schema.Union([ + Schema.String.annotate({ "description": "The URL of the screenshot image.", "format": "uri" }), + Schema.Null + ]), + "file_id": Schema.Union([ + Schema.String.annotate({ "description": "The identifier of an uploaded file that contains the screenshot." }), + Schema.Null + ]), + "detail": Schema.suspend((): Schema.Codec => ImageDetail).annotate({ + "description": + "The detail level of the screenshot image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`." + }), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointConfig) +}).annotate({ + "title": "Computer screenshot", + "description": "A screenshot of a computer.", + "identifier": "ComputerScreenshotContent" +}) +export type InputFileContent = { + readonly "type": "input_file" + readonly "file_id"?: string | null + readonly "filename"?: string + readonly "file_data"?: string + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointConfig + readonly "file_url"?: string + readonly "detail"?: FileInputDetail +} +export const InputFileContent = Schema.Struct({ + "type": Schema.Literal("input_file").annotate({ "description": "The type of the input item. Always `input_file`." }), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "filename": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the file to be sent to the model." }) + ), + "file_data": Schema.optionalKey( + Schema.String.annotate({ "description": "The content of the file to be sent to the model.\n" }) + ), + "prompt_cache_breakpoint": Schema.optionalKey(PromptCacheBreakpointConfig), + "file_url": Schema.optionalKey( + Schema.String.annotate({ "description": "The URL of the file to be sent to the model.", "format": "uri" }) + ), + "detail": Schema.optionalKey( + Schema.suspend((): Schema.Codec => FileInputDetail).annotate({ + "description": + "The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`." + }) + ) +}).annotate({ "title": "Input file", "description": "A file input to the model.", "identifier": "InputFileContent" }) +export type ToolCallCaller = DirectToolCallCaller | ProgramToolCallCaller +export const ToolCallCaller = Schema.Union([DirectToolCallCaller, ProgramToolCallCaller], { mode: "oneOf" }).annotate({ + "description": "The execution context that produced this tool call.", + "identifier": "ToolCallCaller" +}) +export type ToolCallCallerParam = DirectToolCallCallerParam | ProgramToolCallCallerParam +export const ToolCallCallerParam = Schema.Union([DirectToolCallCallerParam, ProgramToolCallCallerParam], { + mode: "oneOf" +}).annotate({ + "description": "The execution context that produced this tool call.", + "identifier": "ToolCallCallerParam" +}) +export type FileSearchToolCall = { + readonly "id": string + readonly "type": "file_search_call" + readonly "status": "in_progress" | "searching" | "completed" | "incomplete" | "failed" + readonly "queries": ReadonlyArray + readonly "results"?: + | ReadonlyArray< + { + readonly "file_id"?: string + readonly "text"?: string + readonly "filename"?: string + readonly "attributes"?: VectorStoreFileAttributes + readonly "score"?: number + } + > + | null +} +export const FileSearchToolCall = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the file search tool call.\n" }), + "type": Schema.Literal("file_search_call").annotate({ + "description": "The type of the file search tool call. Always `file_search_call`.\n" + }), + "status": Schema.Literals(["in_progress", "searching", "completed", "incomplete", "failed"]).annotate({ + "description": + "The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n" + }), + "queries": Schema.Array(Schema.String).annotate({ "description": "The queries used to search for files.\n" }), + "results": Schema.optionalKey(Schema.Union([ + Schema.Array(Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the file.\n" })), + "text": Schema.optionalKey( + Schema.String.annotate({ "description": "The text that was retrieved from the file.\n" }) + ), + "filename": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the file.\n" })), + "attributes": Schema.optionalKey(VectorStoreFileAttributes), + "score": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The relevance score of the file - a value between 0 and 1.\n", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ) + })).annotate({ "description": "The results of the file search tool call.\n" }), + Schema.Null + ])) +}).annotate({ + "title": "File search tool call", + "description": + "The results of a file search tool call. See the\n[file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.\n", + "identifier": "FileSearchToolCall" +}) +export type UpdateVectorStoreFileAttributesRequest = { readonly "attributes": VectorStoreFileAttributes } +export const UpdateVectorStoreFileAttributesRequest = Schema.Struct({ "attributes": VectorStoreFileAttributes }) + .annotate({ "identifier": "UpdateVectorStoreFileAttributesRequest" }) +export type WebSearchToolCall = { + readonly "id": string + readonly "type": "web_search_call" + readonly "status": WebSearchCallStatus + readonly "action"?: WebSearchActionSearch | WebSearchActionOpenPage | WebSearchActionFind +} +export const WebSearchToolCall = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the web search tool call.\n" }), + "type": Schema.Literal("web_search_call").annotate({ + "description": "The type of the web search tool call. Always `web_search_call`.\n" + }), + "status": Schema.suspend((): Schema.Codec => WebSearchCallStatus).annotate({ + "description": "The status of the web search tool call.\n" + }), + "action": Schema.optionalKey( + Schema.Union([WebSearchActionSearch, WebSearchActionOpenPage, WebSearchActionFind], { mode: "oneOf" }).annotate({ + "description": + "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find_in_page).\n" + }) + ) +}).annotate({ + "title": "Web search tool call", + "description": + "The results of a web search tool call. See the\n[web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.\n", + "identifier": "WebSearchToolCall" +}) +export type ImageGenToolCall = { + readonly "type": "image_generation_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "generating" | "failed" + readonly "result": string | null + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" | null + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | null + readonly "action"?: ImageGenActionEnum | null + readonly "background"?: ImageBackground | null + readonly "output_format"?: ImageOutputFormat | null + readonly "revised_prompt"?: string | null +} +export const ImageGenToolCall = Schema.Struct({ + "type": Schema.Literal("image_generation_call").annotate({ + "description": "The type of the image generation call. Always `image_generation_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the image generation call." }), + "status": Schema.Literals(["in_progress", "completed", "generating", "failed"]).annotate({ + "description": "The status of the image generation call." + }), + "result": Schema.Union([ + Schema.String.annotate({ "description": "The generated image encoded in base64." }), + Schema.Null + ]), + "size": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + Schema.Null + ]) + ), + "quality": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": + "The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`." + }), + Schema.Null + ]) + ), + "action": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ImageGenActionEnum).annotate({ + "description": "The action used for image generation." + }), + Schema.Null + ]) + ), + "background": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ImageBackground).annotate({ + "description": "The background setting used for generation." + }), + Schema.Null + ]) + ), + "output_format": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ImageOutputFormat).annotate({ + "description": "The output format used for generation." + }), + Schema.Null + ]) + ), + "revised_prompt": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The prompt that was used after any model prompt rewriting." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Image generation call", + "description": "An image generation request made by the model.", + "identifier": "ImageGenToolCall" +}) +export type ClickParam = { + readonly "type": "click" + readonly "button": ClickButtonType + readonly "x": number + readonly "y": number + readonly "keys"?: ReadonlyArray | null +} +export const ClickParam = Schema.Struct({ + "type": Schema.Literal("click").annotate({ + "description": "Specifies the event type. For a click action, this property is always `click`." + }), + "button": Schema.suspend((): Schema.Codec => ClickButtonType).annotate({ + "description": + "Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate where the click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate where the click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while clicking." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Click", "description": "A click action.", "identifier": "ClickParam" }) +export type DragParam = { + readonly "type": "drag" + readonly "path": ReadonlyArray + readonly "keys"?: ReadonlyArray | null +} +export const DragParam = Schema.Struct({ + "type": Schema.Literal("drag").annotate({ + "description": "Specifies the event type. For a drag action, this property is always set to `drag`." + }), + "path": Schema.Array(CoordParam).annotate({ + "description": + "An array of coordinates representing the path of the drag action. Coordinates will appear as an array of objects, eg\n```\n[\n { x: 100, y: 200 },\n { x: 200, y: 300 }\n]\n```" + }), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while dragging the mouse." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Drag", "description": "A drag action.", "identifier": "DragParam" }) +export type ComputerToolCallOutputResource = { + readonly "type": "computer_call_output" + readonly "id": string + readonly "call_id": string + readonly "acknowledged_safety_checks"?: ReadonlyArray + readonly "output": ComputerScreenshotImage + readonly "status": "completed" | "incomplete" + readonly "created_by"?: string +} +export const ComputerToolCallOutputResource = Schema.Struct({ + "type": Schema.Literal("computer_call_output").annotate({ + "description": "The type of the computer tool call output. Always `computer_call_output`.\n" + }), + "id": Schema.String.annotate({ "description": "The ID of the computer tool call output.\n" }), + "call_id": Schema.String.annotate({ "description": "The ID of the computer tool call that produced the output.\n" }), + "acknowledged_safety_checks": Schema.optionalKey( + Schema.Array(ComputerCallSafetyCheckParam).annotate({ + "description": "The safety checks reported by the API that have been acknowledged by the\ndeveloper.\n" + }) + ), + "output": ComputerScreenshotImage, + "status": Schema.Literals(["completed", "incomplete"]).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "identifier": "ComputerToolCallOutputResource" }) +export type ToolSearchCall = { + readonly "type": "tool_search_call" + readonly "id": string + readonly "call_id": string | null + readonly "execution": ToolSearchExecutionType + readonly "arguments": Schema.Json + readonly "status": FunctionCallStatus + readonly "created_by"?: string +} +export const ToolSearchCall = Schema.Struct({ + "type": Schema.Literal("tool_search_call").annotate({ + "description": "The type of the item. Always `tool_search_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool search call item." }), + "call_id": Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }), + Schema.Null + ]), + "execution": Schema.suspend((): Schema.Codec => ToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }), + "arguments": Schema.Json.annotate({ + "expected": "JSON value", + "description": "Arguments used for the tool search call." + }), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatus).annotate({ + "description": "The status of the tool search call item that was recorded." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ "identifier": "ToolSearchCall" }) +export type FunctionTool = { + readonly "type": "function" + readonly "name": string + readonly "async"?: boolean + readonly "description"?: string | null + readonly "parameters": { readonly [x: string]: Schema.Json } | null + readonly "output_schema"?: { readonly [x: string]: Schema.Json } | null + readonly "strict": boolean | null + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const FunctionTool = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of the function tool. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function to call." }), + "async": Schema.optionalKey(Schema.Boolean), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "A description of the function. Used by the model to determine whether or not to call the function." + }), + Schema.Null + ]) + ), + "parameters": Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON schema object describing the parameters of the function." + }), + Schema.Null + ]), + "output_schema": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON schema object describing the JSON value encoded in string outputs for this function." + }), + Schema.Null + ]) + ), + "strict": Schema.Union([ + Schema.Boolean.annotate({ + "description": "Whether strict parameter validation is enforced for this function tool." + }), + Schema.Null + ]), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this function is deferred and loaded via tool search." }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Function", + "description": + "Defines a function in your own code the model can choose to call. Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).", + "identifier": "FunctionTool" +}) +export type ApplyPatchToolParam = { + readonly "type": "apply_patch" + readonly "allowed_callers"?: ReadonlyArray | null +} +export const ApplyPatchToolParam = Schema.Struct({ + "type": Schema.Literal("apply_patch").annotate({ "description": "The type of the tool. Always `apply_patch`." }), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Apply patch tool", + "description": "Allows the assistant to create, delete, or update files using unified diffs.", + "identifier": "ApplyPatchToolParam" +}) +export type RankingOptions = { + readonly "ranker"?: RankerVersionType + readonly "score_threshold"?: number + readonly "hybrid_search"?: HybridSearchOptions +} +export const RankingOptions = Schema.Struct({ + "ranker": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RankerVersionType).annotate({ + "description": "The ranker to use for the file search." + }) + ), + "score_threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The score threshold for the file search, a number between 0 and 1. Numbers closer to 1 will attempt to return only the most relevant results, but may return fewer results." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "hybrid_search": Schema.optionalKey( + Schema.suspend((): Schema.Codec => HybridSearchOptions).annotate({ + "description": + "Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled." + }) + ) +}).annotate({ "identifier": "RankingOptions" }) +export type Filters = ComparisonFilter | CompoundFilter +export const Filters = Schema.Union([ComparisonFilter, CompoundFilter]).annotate({ "identifier": "Filters" }) +export type VectorStoreSearchRequest = { + readonly "query": string | ReadonlyArray + readonly "rewrite_query"?: boolean + readonly "max_num_results"?: number + readonly "filters"?: ComparisonFilter | CompoundFilter + readonly "ranking_options"?: { + readonly "ranker"?: "none" | "auto" | "default-2024-11-15" + readonly "score_threshold"?: number + } +} +export const VectorStoreSearchRequest = Schema.Struct({ + "query": Schema.Union([ + Schema.String, + Schema.Array(Schema.String.annotate({ "description": "A list of queries to search for." })) + ], { mode: "oneOf" }).annotate({ "description": "A query string for a search" }), + "rewrite_query": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether to rewrite the natural language query for vector search." }) + ), + "max_num_results": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum number of results to return. This number should be between 1 and 50 inclusive." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ), + "filters": Schema.optionalKey( + Schema.Union([ComparisonFilter, CompoundFilter], { mode: "oneOf" }).annotate({ + "description": "A filter to apply based on file attributes." + }) + ), + "ranking_options": Schema.optionalKey( + Schema.Struct({ + "ranker": Schema.optionalKey( + Schema.Literals(["none", "auto", "default-2024-11-15"]).annotate({ + "description": "Enable re-ranking; set to `none` to disable, which can help reduce latency." + }) + ), + "score_threshold": Schema.optionalKey( + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })) + ) + }).annotate({ "description": "Ranking options for search." }) + ) +}).annotate({ "identifier": "VectorStoreSearchRequest" }) +export type ComputerUsePreviewTool = { + readonly "type": "computer_use_preview" + readonly "environment": ComputerEnvironment + readonly "display_width": number + readonly "display_height": number +} +export const ComputerUsePreviewTool = Schema.Struct({ + "type": Schema.Literal("computer_use_preview").annotate({ + "description": "The type of the computer use tool. Always `computer_use_preview`." + }), + "environment": Schema.suspend((): Schema.Codec => ComputerEnvironment).annotate({ + "description": "The type of computer environment to control." + }), + "display_width": Schema.Number.annotate({ "description": "The width of the computer display." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "display_height": Schema.Number.annotate({ "description": "The height of the computer display." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Computer use preview", + "description": + "A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).", + "identifier": "ComputerUsePreviewTool" +}) +export type WebSearchTool = { + readonly "type": "web_search" | "web_search_2025_08_26" + readonly "external_web_access"?: boolean + readonly "filters"?: { readonly "allowed_domains"?: ReadonlyArray | null } | null + readonly "user_location"?: WebSearchApproximateLocation + readonly "search_context_size"?: "low" | "medium" | "high" +} +export const WebSearchTool = Schema.Struct({ + "type": Schema.Literals(["web_search", "web_search_2025_08_26"]).annotate({ + "description": "The type of the web search tool. One of `web_search` or `web_search_2025_08_26`." + }), + "external_web_access": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Allow live internet access for web search. Defaults to true when omitted. When false, the web search tool runs in offline/cache-only mode and will not fetch new external content." + }) + ), + "filters": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "allowed_domains": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String.annotate({ "description": "Allowed domain for the search." })).annotate({ + "title": "Allowed domains for the search.", + "description": + "Allowed domains for the search. If not provided, all domains are allowed.\nSubdomains of the provided domains are allowed as well.\n\nExample: `[\"pubmed.ncbi.nlm.nih.gov\"]`\n" + }), + Schema.Null + ]) + ) + }).annotate({ "description": "Filters for the search.\n" }), + Schema.Null + ])), + "user_location": Schema.optionalKey( + Schema.suspend((): Schema.Codec => WebSearchApproximateLocation).annotate({ + "description": + "The approximate location of the user. If omitted or null, defaults to the\nUnited States. To avoid this fallback, pass `{\"type\": \"approximate\"}` without\nlocation fields. To localize results, provide the relevant location fields.\n" + }) + ), + "search_context_size": Schema.optionalKey( + Schema.Literals(["low", "medium", "high"]).annotate({ + "description": + "High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default." + }) + ) +}).annotate({ + "title": "Web search", + "description": + "Search the Internet for sources related to the prompt. Learn more about the\n[web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).\n", + "identifier": "WebSearchTool" +}) +export type MCPTool = { + readonly "type": "mcp" + readonly "server_label": string + readonly "server_url"?: string + readonly "connector_id"?: + | "connector_dropbox" + | "connector_gmail" + | "connector_googlecalendar" + | "connector_googledrive" + | "connector_microsoftteams" + | "connector_outlookcalendar" + | "connector_outlookemail" + | "connector_sharepoint" + readonly "tunnel_id"?: string + readonly "authorization"?: string + readonly "server_description"?: string + readonly "headers"?: { readonly [x: string]: string } | null + readonly "allowed_tools"?: ReadonlyArray | MCPToolFilter | null + readonly "allowed_callers"?: ReadonlyArray | null + readonly "require_approval"?: + | { readonly "always"?: MCPToolFilter; readonly "never"?: MCPToolFilter } + | "always" + | "never" + | null + readonly "defer_loading"?: boolean +} +export const MCPTool = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The type of the MCP tool. Always `mcp`." }), + "server_label": Schema.String.annotate({ + "description": "A label for this MCP server, used to identify it in tool calls.\n" + }), + "server_url": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The URL for the MCP server. One of `server_url`, `connector_id`, or\n`tunnel_id` must be provided.\n", + "format": "uri" + }) + ), + "connector_id": Schema.optionalKey( + Schema.Literals([ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint" + ]).annotate({ + "description": + "Identifier for service connectors, like those available in ChatGPT. One of\n`server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more\nabout service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).\n\nThis field is deprecated for models released after September 1, 2026.\nUse `server_url` to connect to a remote MCP server, or `tunnel_id` to\nconnect through a Secure MCP Tunnel.\n\nCurrently supported `connector_id` values are:\n\n- Dropbox: `connector_dropbox`\n- Gmail: `connector_gmail`\n- Google Calendar: `connector_googlecalendar`\n- Google Drive: `connector_googledrive`\n- Microsoft Teams: `connector_microsoftteams`\n- Outlook Calendar: `connector_outlookcalendar`\n- Outlook Email: `connector_outlookemail`\n- SharePoint: `connector_sharepoint`\n" + }) + ), + "tunnel_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The Secure MCP Tunnel ID to use instead of a direct server URL. One of\n`server_url`, `connector_id`, or `tunnel_id` must be provided.\n" + }).check( + Schema.isPattern(new RegExp("^tunnel_[a-z0-9]{32}$", "u")).annotate({ + "expected": "a string matching the RegExp ^tunnel_[a-z0-9]{32}$" + }) + ) + ), + "authorization": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An OAuth access token that can be used with a remote MCP server, either\nwith a custom MCP server URL or a service connector. Your application\nmust handle the OAuth authorization flow and provide the token here.\n" + }) + ), + "server_description": Schema.optionalKey( + Schema.String.annotate({ "description": "Optional description of the MCP server, used to provide more context.\n" }) + ), + "headers": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.String).annotate({ + "description": "Optional HTTP headers to send to the MCP server. Use for authentication\nor other purposes.\n" + }), + Schema.Null + ]) + ), + "allowed_tools": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Array(Schema.String).annotate({ + "title": "MCP allowed tools", + "description": "A string array of allowed tool names" + }), + MCPToolFilter + ], { mode: "oneOf" }).annotate({ "description": "List of allowed tool names or a filter object.\n" }), + Schema.Null + ]) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ), + "require_approval": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Struct({ "always": Schema.optionalKey(MCPToolFilter), "never": Schema.optionalKey(MCPToolFilter) }) + .annotate({ + "title": "MCP tool approval filter", + "description": + "Specify which of the MCP server's tools require approval. Can be\n`always`, `never`, or a filter object associated with tools\nthat require approval.\n" + }), + Schema.Literals(["always", "never"]).annotate({ + "title": "MCP tool approval setting", + "description": + "Specify a single approval policy for all tools. One of `always` or\n`never`. When set to `always`, all tools will require approval. When\nset to `never`, all tools will not require approval.\n" + }) + ], { mode: "oneOf" }).annotate({ "description": "Specify which of the MCP server's tools require approval." }), + Schema.Null + ]) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this MCP tool is deferred and discovered via tool search.\n" }) + ) +}).annotate({ + "title": "MCP tool", + "description": + "Give the model access to additional tools via remote Model Context Protocol\n(MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).\n", + "identifier": "MCPTool" +}) +export type ImageGenTool = { + readonly "type": "image_generation" + readonly "model"?: + | string + | "gpt-image-1" + | "gpt-image-1-mini" + | "gpt-image-1.5" + | "gpt-image-2" + | "gpt-image-2-2026-04-21" + | "gpt-image-2.5-sunburst" + | "gpt-image-2.5-sunburst-2026-09-08" + | "gpt-image-2.5-flare" + | "gpt-image-2.5-flare-2026-09-08" + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" | "auto" + readonly "output_format"?: "png" | "webp" | "jpeg" + readonly "output_compression"?: number + readonly "moderation"?: "auto" | "low" + readonly "background"?: "transparent" | "opaque" | "auto" + readonly "input_fidelity"?: InputFidelity | null + readonly "input_image_mask"?: { readonly "image_url"?: string; readonly "file_id"?: string } + readonly "partial_images"?: number + readonly "action"?: ImageGenActionEnum +} +export const ImageGenTool = Schema.Struct({ + "type": Schema.Literal("image_generation").annotate({ + "description": "The type of the image generation tool. Always `image_generation`.\n" + }), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-1.5", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08" + ]).annotate({ + "description": + "The image generation model to use. One of `gpt-image-1`,\n`gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,\n`gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,\n`gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,\n`gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:\n`gpt-image-1`.\n" + }) + ]) + ), + "quality": Schema.optionalKey( + Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": + "The quality of the generated image. The GPT image models support `low`,\n`medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`,\nincluding their `2026-09-08` snapshots, also support `xhigh` and `max`.\nDefault: `auto`.\n" + }) + ), + "size": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024", "auto"])]).annotate({ + "description": + "The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing." + }) + ), + "output_format": Schema.optionalKey( + Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format of the generated image. One of `png`, `webp`, or\n`jpeg`. Default: `png`.\n" + }) + ), + "output_compression": Schema.optionalKey( + Schema.Number.annotate({ "description": "Compression level for the output image. Default: 100.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })).check( + Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" }) + ) + ), + "moderation": Schema.optionalKey( + Schema.Literals(["auto", "low"]).annotate({ + "description": "Moderation level for the generated image. Default: `auto`.\n" + }) + ), + "background": Schema.optionalKey( + Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "description": + "Set the background of the generated image. One of `transparent`, `opaque`,\nor `auto`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including\ntheir `2026-09-08` snapshots, support `opaque` and `transparent`\nbackgrounds. Transparent backgrounds are available for supported GPT Image\nmodels. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in\npreview. When using `transparent`, set the output format to `png` or `webp`.\nDefault: `auto`.\n" + }) + ), + "input_fidelity": Schema.optionalKey(Schema.Union([InputFidelity, Schema.Null])), + "input_image_mask": Schema.optionalKey( + Schema.Struct({ + "image_url": Schema.optionalKey(Schema.String.annotate({ "description": "Base64-encoded mask image.\n" })), + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "File ID for the mask image.\n" })) + }).annotate({ + "description": + "Optional mask for inpainting. Contains `image_url`\n(string, optional) and `file_id` (string, optional).\n" + }) + ), + "partial_images": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Number of partial images to generate in streaming mode, from 0 (default value) to 3.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(3).annotate({ "expected": "a value less than or equal to 3" })) + ), + "action": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ImageGenActionEnum).annotate({ + "description": "Whether to generate a new image or edit an existing image. Default: `auto`.\n" + }) + ) +}).annotate({ + "title": "Image generation tool", + "description": "A tool that generates images using the GPT image models.\n", + "identifier": "ImageGenTool" +}) +export type LocalEnvironmentParam = { readonly "type": "local"; readonly "skills"?: ReadonlyArray } +export const LocalEnvironmentParam = Schema.Struct({ + "type": Schema.Literal("local").annotate({ "description": "Use a local computer environment." }), + "skills": Schema.optionalKey( + Schema.Array(LocalSkillParam).annotate({ "description": "An optional list of skills." }).check( + Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" }) + ) + ) +}).annotate({ "identifier": "LocalEnvironmentParam" }) +export type CustomGrammarFormatParam = { + readonly "type": "grammar" + readonly "syntax": GrammarSyntax1 + readonly "definition": string +} +export const CustomGrammarFormatParam = Schema.Struct({ + "type": Schema.Literal("grammar").annotate({ "description": "Grammar format. Always `grammar`." }), + "syntax": Schema.suspend((): Schema.Codec => GrammarSyntax1).annotate({ + "description": "The syntax of the grammar definition. One of `lark` or `regex`." + }), + "definition": Schema.String.annotate({ "description": "The grammar definition." }) +}).annotate({ + "title": "Grammar format", + "description": "A grammar defined by the user.", + "identifier": "CustomGrammarFormatParam" +}) +export type FunctionToolParam = { + readonly "name": string + readonly "description"?: string | null + readonly "parameters"?: EmptyModelParam | null + readonly "strict"?: boolean | null + readonly "type": "function" + readonly "async"?: boolean + readonly "output_schema"?: { readonly [x: string]: Schema.Json } | null + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const FunctionToolParam = Schema.Struct({ + "name": Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })).check( + Schema.isPattern(new RegExp("^[a-zA-Z0-9_-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^[a-zA-Z0-9_-]+$" + }) + ), + "description": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "parameters": Schema.optionalKey(Schema.Union([EmptyModelParam, Schema.Null])), + "strict": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enforce strict parameter validation. If omitted, Responses attempts to use strict validation when the schema is compatible, and falls back to non-strict validation otherwise." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("function"), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the tool response can be returned asynchronously versus immediately returned on next response creation." + }) + ), + "output_schema": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "A JSON Schema describing the JSON value encoded in string outputs for this function tool. This does not describe content-array outputs." + }), + Schema.Null + ]) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this function should be deferred and discovered via tool search." + }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "FunctionToolParam" }) +export type ToolSearchToolParam = { + readonly "type": "tool_search" + readonly "execution"?: ToolSearchExecutionType + readonly "description"?: string | null + readonly "parameters"?: EmptyModelParam | null +} +export const ToolSearchToolParam = Schema.Struct({ + "type": Schema.Literal("tool_search").annotate({ "description": "The type of the tool. Always `tool_search`." }), + "execution": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ToolSearchExecutionType).annotate({ + "description": "Whether tool search is executed by the server or by the client." + }) + ), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "Description shown to the model for a client-executed tool search tool." + }), + Schema.Null + ]) + ), + "parameters": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => EmptyModelParam).annotate({ + "description": "Parameter schema for a client-executed tool search tool." + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Tool search tool", + "description": "Hosted or BYOT tool search configuration for deferred tools.", + "identifier": "ToolSearchToolParam" +}) +export type ToolSearchOutputFunctionToolParam = { + readonly "name": string + readonly "description"?: string | null + readonly "parameters"?: EmptyModelParam | null + readonly "strict"?: boolean | null + readonly "type": "function" + readonly "async"?: boolean + readonly "output_schema"?: { readonly [x: string]: Schema.Json } | null + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const ToolSearchOutputFunctionToolParam = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the loaded function tool." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })).check( + Schema.isPattern(new RegExp("^(?![\\s\\S]*[\\r\\n])[a-zA-Z0-9_.-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^(?![\\s\\S]*[\\r\\n])[a-zA-Z0-9_.-]+$" + }) + ), + "description": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "parameters": Schema.optionalKey(Schema.Union([EmptyModelParam, Schema.Null])), + "strict": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enforce strict parameter validation. If omitted, Responses attempts to use strict validation when the schema is compatible, and falls back to non-strict validation otherwise." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("function"), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the tool response can be returned asynchronously versus immediately returned on next response creation." + }) + ), + "output_schema": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "A JSON Schema describing the JSON value encoded in string outputs for this function tool. This does not describe content-array outputs." + }), + Schema.Null + ]) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this function should be deferred and discovered via tool search." + }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "ToolSearchOutputFunctionToolParam" }) +export type WebSearchPreviewTool = { + readonly "type": "web_search_preview" | "web_search_preview_2025_03_11" + readonly "user_location"?: ApproximateLocation | null + readonly "search_context_size"?: SearchContextSize + readonly "search_content_types"?: ReadonlyArray +} +export const WebSearchPreviewTool = Schema.Struct({ + "type": Schema.Literals(["web_search_preview", "web_search_preview_2025_03_11"]).annotate({ + "description": "The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`." + }), + "user_location": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ApproximateLocation).annotate({ + "description": + "The approximate location of the user. If omitted or null, defaults to the United States. To avoid this fallback, pass `{\"type\": \"approximate\"}` without location fields. To localize results, provide the relevant location fields." + }), + Schema.Null + ]) + ), + "search_context_size": Schema.optionalKey( + Schema.suspend((): Schema.Codec => SearchContextSize).annotate({ + "description": + "High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default." + }) + ), + "search_content_types": Schema.optionalKey(Schema.Array(SearchContentType)) +}).annotate({ + "title": "Web search preview", + "description": + "This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).", + "identifier": "WebSearchPreviewTool" +}) +export type ProgramOutput = { + readonly "type": "program_output" + readonly "id": string + readonly "call_id": string + readonly "result": string + readonly "status": ProgramOutputStatus +} +export const ProgramOutput = Schema.Struct({ + "type": Schema.Literal("program_output").annotate({ + "description": "The type of the item. Always `program_output`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the program output item." }), + "call_id": Schema.String.annotate({ "description": "The call ID of the program item." }), + "result": Schema.String.annotate({ "description": "The result produced by the program item." }), + "status": Schema.suspend((): Schema.Codec => ProgramOutputStatus).annotate({ + "description": "The terminal status of the program output item." + }) +}).annotate({ "identifier": "ProgramOutput" }) +export type CodeInterpreterToolCall = { + readonly "type": "code_interpreter_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "incomplete" | "interpreting" | "failed" + readonly "container_id": string + readonly "code": string | null + readonly "outputs": ReadonlyArray | null +} +export const CodeInterpreterToolCall = Schema.Struct({ + "type": Schema.Literal("code_interpreter_call").annotate({ + "description": "The type of the code interpreter tool call. Always `code_interpreter_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the code interpreter tool call.\n" }), + "status": Schema.Literals(["in_progress", "completed", "incomplete", "interpreting", "failed"]).annotate({ + "description": + "The status of the code interpreter tool call. Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and `failed`.\n" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the container used to run the code.\n" }), + "code": Schema.Union([ + Schema.String.annotate({ "description": "The code to run, or null if not available.\n" }), + Schema.Null + ]), + "outputs": Schema.Union([ + Schema.Array(Schema.Union([CodeInterpreterOutputLogs, CodeInterpreterOutputImage], { mode: "oneOf" })).annotate({ + "description": + "The outputs generated by the code interpreter, such as logs or images.\nCan be null if no outputs are available.\n" + }), + Schema.Null + ]) +}).annotate({ + "title": "Code interpreter tool call", + "description": "A tool call to run code.\n", + "identifier": "CodeInterpreterToolCall" +}) +export type LocalShellToolCall = { + readonly "type": "local_shell_call" + readonly "id": string + readonly "call_id": string + readonly "action": LocalShellExecAction + readonly "status": "in_progress" | "completed" | "incomplete" +} +export const LocalShellToolCall = Schema.Struct({ + "type": Schema.Literal("local_shell_call").annotate({ + "description": "The type of the local shell call. Always `local_shell_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the local shell call.\n" }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "action": LocalShellExecAction, + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the local shell call.\n" + }) +}).annotate({ + "title": "Local shell call", + "description": "A tool call to run a command on the local shell.\n", + "identifier": "LocalShellToolCall" +}) +export type FunctionShellCallOutputContent = { + readonly "stdout": string + readonly "stderr": string + readonly "outcome": FunctionShellCallOutputTimeoutOutcome | FunctionShellCallOutputExitOutcome + readonly "created_by"?: string +} +export const FunctionShellCallOutputContent = Schema.Struct({ + "stdout": Schema.String.annotate({ "description": "The standard output that was captured." }), + "stderr": Schema.String.annotate({ "description": "The standard error output that was captured." }), + "outcome": Schema.Union([FunctionShellCallOutputTimeoutOutcome, FunctionShellCallOutputExitOutcome], { + mode: "oneOf" + }).annotate({ + "title": "Shell call outcome", + "description": + "Represents either an exit outcome (with an exit code) or a timeout outcome for a shell call output chunk." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ + "title": "Shell call output content", + "description": "The content of a shell tool call output that was emitted.", + "identifier": "FunctionShellCallOutputContent" +}) +export type MCPListTools = { + readonly "type": "mcp_list_tools" + readonly "id": string + readonly "server_label": string + readonly "tools": ReadonlyArray + readonly "error"?: string | null +} +export const MCPListTools = Schema.Struct({ + "type": Schema.Literal("mcp_list_tools").annotate({ + "description": "The type of the item. Always `mcp_list_tools`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the list.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server.\n" }), + "tools": Schema.Array(MCPListToolsTool).annotate({ "description": "The tools available on the server.\n" }), + "error": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Error message if the server could not list tools.\n" }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP list tools", + "description": "A list of tools available on an MCP server.\n", + "identifier": "MCPListTools" +}) +export type MCPToolCallError = MCPProtocolError | MCPToolExecutionError | HTTPError +export const MCPToolCallError = Schema.Union([MCPProtocolError, MCPToolExecutionError, HTTPError], { mode: "oneOf" }) + .annotate({ "identifier": "MCPToolCallError" }) +export type InputImageContentParamAutoParam = { + readonly "type": "input_image" + readonly "image_url"?: string | null + readonly "file_id"?: string | null + readonly "detail"?: DetailEnum | null + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam | null +} +export const InputImageContentParamAutoParam = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ + "description": "The type of the input item. Always `input_image`." + }), + "image_url": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.", + "format": "uri" + }).check(Schema.isMaxCodePoints(20971520).annotate({ "expected": "a string with at most 20971520 code points" })), + Schema.Null + ]) + ), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "detail": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => DetailEnum).annotate({ + "description": + "The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`." + }), + Schema.Null + ]) + ), + "prompt_cache_breakpoint": Schema.optionalKey(Schema.Union([PromptCacheBreakpointParam, Schema.Null])) +}).annotate({ + "title": "Input image", + "description": + "An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)", + "identifier": "InputImageContentParamAutoParam" +}) +export type InputFileContentParam = { + readonly "type": "input_file" + readonly "file_id"?: string | null + readonly "filename"?: string | null + readonly "file_data"?: string | null + readonly "file_url"?: string | null + readonly "detail"?: FileDetailEnum + readonly "prompt_cache_breakpoint"?: PromptCacheBreakpointParam | null +} +export const InputFileContentParam = Schema.Struct({ + "type": Schema.Literal("input_file").annotate({ "description": "The type of the input item. Always `input_file`." }), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "filename": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the file to be sent to the model." }), + Schema.Null + ]) + ), + "file_data": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The base64-encoded data of the file to be sent to the model." }).check( + Schema.isMaxCodePoints(73400320).annotate({ "expected": "a string with at most 73400320 code points" }) + ), + Schema.Null + ]) + ), + "file_url": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The URL of the file to be sent to the model.", "format": "uri" }), + Schema.Null + ]) + ), + "detail": Schema.optionalKey( + Schema.suspend((): Schema.Codec => FileDetailEnum).annotate({ + "description": + "The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`." + }) + ), + "prompt_cache_breakpoint": Schema.optionalKey(Schema.Union([PromptCacheBreakpointParam, Schema.Null])) +}).annotate({ + "title": "Input file", + "description": "A file input to the model.", + "identifier": "InputFileContentParam" +}) +export type FunctionShellCallOutputOutcomeParam = + | FunctionShellCallOutputTimeoutOutcomeParam + | FunctionShellCallOutputExitOutcomeParam +export const FunctionShellCallOutputOutcomeParam = Schema.Union([ + FunctionShellCallOutputTimeoutOutcomeParam, + FunctionShellCallOutputExitOutcomeParam +], { mode: "oneOf" }).annotate({ + "title": "Shell call outcome", + "description": "The exit or timeout outcome associated with this shell call.", + "identifier": "FunctionShellCallOutputOutcomeParam" +}) +export type ApplyPatchOperationParam = + | ApplyPatchCreateFileOperationParam + | ApplyPatchDeleteFileOperationParam + | ApplyPatchUpdateFileOperationParam +export const ApplyPatchOperationParam = Schema.Union([ + ApplyPatchCreateFileOperationParam, + ApplyPatchDeleteFileOperationParam, + ApplyPatchUpdateFileOperationParam +], { mode: "oneOf" }).annotate({ + "title": "Apply patch operation", + "description": "One of the create_file, delete_file, or update_file operations supplied to the apply_patch tool.", + "identifier": "ApplyPatchOperationParam" +}) +export type ProgramOutputItemParam = { + readonly "id": string + readonly "type": "program_output" + readonly "call_id": string + readonly "result": string + readonly "status": ProgramOutputItemStatus +} +export const ProgramOutputItemParam = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of this program output item." }), + "type": Schema.Literal("program_output").annotate({ "description": "The item type. Always `program_output`." }), + "call_id": Schema.String.annotate({ "description": "The call ID of the program item." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "result": Schema.String.annotate({ "description": "The result produced by the program item." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => ProgramOutputItemStatus).annotate({ + "description": "The terminal status of the program output." + }) +}).annotate({ "identifier": "ProgramOutputItemParam" }) +export type CreateEmbeddingResponse = { + readonly "data": ReadonlyArray + readonly "model": string + readonly "object": "list" + readonly "usage": { readonly "prompt_tokens": number; readonly "total_tokens": number } +} +export const CreateEmbeddingResponse = Schema.Struct({ + "data": Schema.Array(Embedding).annotate({ "description": "The list of embeddings generated by the model." }), + "model": Schema.String.annotate({ "description": "The name of the model used to generate the embedding." }), + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always \"list\"." }), + "usage": Schema.Struct({ + "prompt_tokens": Schema.Number.annotate({ "description": "The number of tokens used by the prompt." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "total_tokens": Schema.Number.annotate({ "description": "The total number of tokens used by the request." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "The usage information for the request." }) +}).annotate({ "identifier": "CreateEmbeddingResponse" }) +export type CreateEvalJsonlRunDataSource = { + readonly "type": "jsonl" + readonly "source": EvalJsonlFileContentSource | EvalJsonlFileIdSource +} +export const CreateEvalJsonlRunDataSource = Schema.Struct({ + "type": Schema.Literal("jsonl").annotate({ "description": "The type of data source. Always `jsonl`." }), + "source": Schema.Union([EvalJsonlFileContentSource, EvalJsonlFileIdSource], { mode: "oneOf" }).annotate({ + "description": "Determines what populates the `item` namespace in the data source." + }) +}).annotate({ + "title": "JsonlRunDataSource", + "description": "A JsonlRunDataSource object with that specifies a JSONL file that matches the eval \n", + "identifier": "CreateEvalJsonlRunDataSource" +}) +export type EvalRunOutputItem = { + readonly "object": "eval.run.output_item" + readonly "id": string + readonly "run_id": string + readonly "eval_id": string + readonly "created_at": number + readonly "status": string + readonly "datasource_item_id": number + readonly "datasource_item": { readonly [x: string]: Schema.Json } + readonly "results": ReadonlyArray + readonly "sample": { + readonly "input": ReadonlyArray<{ readonly "role": string; readonly "content": string }> + readonly "output": ReadonlyArray<{ readonly "role"?: string; readonly "content"?: string }> + readonly "finish_reason": string + readonly "model": string + readonly "usage": { + readonly "total_tokens": number + readonly "completion_tokens": number + readonly "prompt_tokens": number + readonly "cached_tokens": number + } + readonly "error": EvalApiError | null + readonly "temperature": number + readonly "max_completion_tokens": number + readonly "top_p": number + readonly "seed": number + } +} +export const EvalRunOutputItem = Schema.Struct({ + "object": Schema.Literal("eval.run.output_item").annotate({ + "description": "The type of the object. Always \"eval.run.output_item\"." + }), + "id": Schema.String.annotate({ "description": "Unique identifier for the evaluation run output item." }), + "run_id": Schema.String.annotate({ + "description": "The identifier of the evaluation run associated with this output item." + }), + "eval_id": Schema.String.annotate({ "description": "The identifier of the evaluation group." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the evaluation run was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "status": Schema.String.annotate({ "description": "The status of the evaluation run." }), + "datasource_item_id": Schema.Number.annotate({ "description": "The identifier for the data source item." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "datasource_item": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Details of the input data source item." + }), + "results": Schema.Array(EvalRunOutputItemResult).annotate({ + "description": "A list of grader results for this output item." + }), + "sample": Schema.Struct({ + "input": Schema.Array( + Schema.Struct({ + "role": Schema.String.annotate({ + "description": "The role of the message sender (e.g., system, user, developer)." + }), + "content": Schema.String.annotate({ "description": "The content of the message." }) + }).annotate({ "description": "An input message." }) + ).annotate({ "description": "An array of input messages." }), + "output": Schema.Array( + Schema.Struct({ + "role": Schema.optionalKey( + Schema.String.annotate({ + "description": "The role of the message (e.g. \"system\", \"assistant\", \"user\")." + }) + ), + "content": Schema.optionalKey(Schema.String.annotate({ "description": "The content of the message." })) + }) + ).annotate({ "description": "An array of output messages." }), + "finish_reason": Schema.String.annotate({ "description": "The reason why the sample generation was finished." }), + "model": Schema.String.annotate({ "description": "The model used for generating the sample." }), + "usage": Schema.Struct({ + "total_tokens": Schema.Number.annotate({ "description": "The total number of tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "completion_tokens": Schema.Number.annotate({ "description": "The number of completion tokens generated." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "prompt_tokens": Schema.Number.annotate({ "description": "The number of prompt tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "cached_tokens": Schema.Number.annotate({ "description": "The number of tokens retrieved from cache." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "Token usage details for the sample." }), + "error": Schema.Union([EvalApiError, Schema.Null]), + "temperature": Schema.Number.annotate({ "description": "The sampling temperature used." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "max_completion_tokens": Schema.Number.annotate({ + "description": "The maximum number of tokens allowed for completion." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "top_p": Schema.Number.annotate({ "description": "The top_p value used for sampling." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ), + "seed": Schema.Number.annotate({ "description": "The seed used for generating the sample." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "A sample containing the input and output of the evaluation run." }) +}).annotate({ + "title": "EvalRunOutputItem", + "description": "A schema representing an evaluation run output item.\n", + "identifier": "EvalRunOutputItem" +}) +export type ListFilesResponse = { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const ListFilesResponse = Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(OpenAIFile), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListFilesResponse" }) +export type Upload = { + readonly "id": string + readonly "created_at": number + readonly "filename": string + readonly "bytes": number + readonly "purpose": string + readonly "status": "pending" | "completed" | "cancelled" | "expired" + readonly "expires_at": number + readonly "object"?: "upload" + readonly "file"?: OpenAIFile | null +} +export const Upload = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The Upload unique identifier, which can be referenced in API endpoints." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the Upload was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "filename": Schema.String.annotate({ "description": "The name of the file to be uploaded." }), + "bytes": Schema.Number.annotate({ "description": "The intended number of bytes to be uploaded." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "purpose": Schema.String.annotate({ + "description": + "The intended purpose of the file. [Please refer here](https://developers.openai.com/api/reference/resources/files#%28resource%29%20files%20%3E%20%28model%29%20file_object%20%3E%20%28schema%29%20%3E%20%28property%29%20purpose) for acceptable values." + }), + "status": Schema.Literals(["pending", "completed", "cancelled", "expired"]).annotate({ + "description": "The status of the Upload." + }), + "expires_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the Upload will expire.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "object": Schema.optionalKey( + Schema.Literal("upload").annotate({ "description": "The object type, which is always \"upload\"." }) + ), + "file": Schema.optionalKey( + Schema.Union([OpenAIFile, Schema.Null]).annotate({ + "description": "The ready File object after the Upload is completed." + }) + ) +}).annotate({ + "title": "Upload", + "description": "The Upload object can accept byte chunks in the form of Parts.\n", + "identifier": "Upload" +}) +export type CreateFileRequestMultipart = { + readonly "file": __ClientMultipartFile + readonly "purpose": "assistants" | "batch" | "fine-tune" | "vision" | "user_data" | "evals" + readonly "expires_after"?: FileExpirationAfter +} +export const CreateFileRequestMultipart = Schema.Struct({ + "file": __ClientMultipartFile, + "purpose": Schema.Literals(["assistants", "batch", "fine-tune", "vision", "user_data", "evals"]).annotate({ + "description": + "The intended purpose of the uploaded file. One of:\n- `assistants`: Used in the Assistants API\n- `batch`: Used in the Batch API\n- `fine-tune`: Used for fine-tuning\n- `vision`: Images used for vision fine-tuning\n- `user_data`: Flexible file type for any purpose\n- `evals`: Used for eval data sets\n" + }), + "expires_after": Schema.optionalKey(FileExpirationAfter) +}).annotate({ "identifier": "CreateFileRequestMultipart" }) +export type CreateUploadRequest = { + readonly "filename": string + readonly "purpose": "assistants" | "batch" | "fine-tune" | "vision" + readonly "bytes": number + readonly "mime_type": string + readonly "expires_after"?: FileExpirationAfter +} +export const CreateUploadRequest = Schema.Struct({ + "filename": Schema.String.annotate({ "description": "The name of the file to upload.\n" }), + "purpose": Schema.Literals(["assistants", "batch", "fine-tune", "vision"]).annotate({ + "description": + "The intended purpose of the uploaded file.\n\nSee the [documentation on File\npurposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).\n" + }), + "bytes": Schema.Number.annotate({ "description": "The number of bytes in the file you are uploading.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "mime_type": Schema.String.annotate({ + "description": + "The MIME type of the file.\n\n\nThis must fall within the supported MIME types for your file purpose. See\nthe supported MIME types for assistants and vision.\n" + }), + "expires_after": Schema.optionalKey(FileExpirationAfter) +}).annotate({ "identifier": "CreateUploadRequest" }) +export type ListFineTuningCheckpointPermissionResponse = { + readonly "data": ReadonlyArray + readonly "object": "list" + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ListFineTuningCheckpointPermissionResponse = Schema.Struct({ + "data": Schema.Array(FineTuningCheckpointPermission), + "object": Schema.Literal("list"), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListFineTuningCheckpointPermissionResponse" }) +export type FineTuneSupervisedMethod = { readonly "hyperparameters"?: FineTuneSupervisedHyperparameters } +export const FineTuneSupervisedMethod = Schema.Struct({ + "hyperparameters": Schema.optionalKey(FineTuneSupervisedHyperparameters) +}).annotate({ + "description": "Configuration for the supervised fine-tuning method.", + "identifier": "FineTuneSupervisedMethod" +}) +export type FineTuneDPOMethod = { readonly "hyperparameters"?: FineTuneDPOHyperparameters } +export const FineTuneDPOMethod = Schema.Struct({ "hyperparameters": Schema.optionalKey(FineTuneDPOHyperparameters) }) + .annotate({ "description": "Configuration for the DPO fine-tuning method.", "identifier": "FineTuneDPOMethod" }) +export type ListFineTuningJobCheckpointsResponse = { + readonly "data": ReadonlyArray + readonly "object": "list" + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ListFineTuningJobCheckpointsResponse = Schema.Struct({ + "data": Schema.Array(FineTuningJobCheckpoint), + "object": Schema.Literal("list"), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListFineTuningJobCheckpointsResponse" }) +export type ListFineTuningJobEventsResponse = { + readonly "data": ReadonlyArray + readonly "object": "list" + readonly "has_more": boolean +} +export const ListFineTuningJobEventsResponse = Schema.Struct({ + "data": Schema.Array(FineTuningJobEvent), + "object": Schema.Literal("list"), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListFineTuningJobEventsResponse" }) +export type EditImageBodyJsonParam = { + readonly "model"?: + | string + | "gpt-image-1.5" + | "gpt-image-2" + | "gpt-image-2-2026-04-21" + | "gpt-image-2.5-sunburst" + | "gpt-image-2.5-sunburst-2026-09-08" + | "gpt-image-2.5-flare" + | "gpt-image-2.5-flare-2026-09-08" + | "gpt-image-1" + | "gpt-image-1-mini" + | "chatgpt-image-latest" + | null + readonly "images": ReadonlyArray + readonly "mask"?: ImageRefParam + readonly "prompt": string + readonly "n"?: number | null + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | null + readonly "input_fidelity"?: "high" | "low" | null + readonly "size"?: string | "auto" | "1024x1024" | "1536x1024" | "1024x1536" | null + readonly "user"?: string + readonly "output_format"?: "png" | "jpeg" | "webp" | null + readonly "output_compression"?: number | null + readonly "moderation"?: "low" | "auto" | null + readonly "background"?: "transparent" | "opaque" | "auto" | null + readonly "stream"?: boolean | null + readonly "partial_images"?: PartialImages +} +export const EditImageBodyJsonParam = Schema.Struct({ + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-image-1.5", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1", + "gpt-image-1-mini", + "chatgpt-image-latest" + ]), + Schema.Null + ]).annotate({ + "description": + "The GPT image model to use for image editing, including `gpt-image-2`, its dated snapshot `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`. Defaults to `gpt-image-2.5-sunburst`." + }) + ), + "images": Schema.Array(ImageRefParam).annotate({ + "description": "Input image references to edit.\nFor GPT image models, you can provide up to 16 images.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" }) + ), + "mask": Schema.optionalKey(ImageRefParam), + "prompt": Schema.String.annotate({ "description": "A text description of the desired image edit." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" })), + "n": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(10).annotate({ "expected": "a value less than or equal to 10" })), + Schema.Null + ]).annotate({ "description": "The number of edited images to generate." }) + ), + "quality": Schema.optionalKey( + Schema.Union([Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]), Schema.Null]).annotate({ + "description": + "Output quality for GPT image models. The GPT image models support `low`, `medium`,\nand `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their\n`2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.\n" + }) + ), + "input_fidelity": Schema.optionalKey( + Schema.Union([Schema.Literals(["high", "low"]), Schema.Null]).annotate({ + "description": + "Control how much effort the model will exert to match the style and features, especially facial features, of input images. Supports `high` and `low` on `gpt-image-1` and `gpt-image-1.5`; `gpt-image-1-mini` supports only `low`. For `gpt-image-2`, omit this parameter. Defaults to `low` on supported models." + }) + ), + "size": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Literals(["auto", "1024x1024", "1536x1024", "1024x1536"]), Schema.Null]) + .annotate({ + "description": + "The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing." + }) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A unique identifier representing your end-user, which can help OpenAI\nmonitor and detect abuse.\n" + }) + ), + "output_format": Schema.optionalKey( + Schema.Union([Schema.Literals(["png", "jpeg", "webp"]), Schema.Null]).annotate({ + "description": "Output image format. Supported for GPT image models." + }) + ), + "output_compression": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })), + Schema.Null + ]).annotate({ "description": "Compression level for `jpeg` or `webp` output." }) + ), + "moderation": Schema.optionalKey( + Schema.Union([Schema.Literals(["low", "auto"]), Schema.Null]).annotate({ + "description": "Moderation level for GPT image models." + }) + ), + "background": Schema.optionalKey( + Schema.Union([Schema.Literals(["transparent", "opaque", "auto"]), Schema.Null]).annotate({ + "description": + "Set the background of the generated image output. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`." + }) + ), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ "description": "Stream partial image results as events." }) + ), + "partial_images": Schema.optionalKey(PartialImages) +}).annotate({ + "description": + "JSON request body for image edits.\n\nUse `images` (array of `ImageRefParam`) instead of multipart `image` uploads.\nYou can reference images via external URLs, data URLs, or uploaded file IDs.\nJSON edits support GPT image models.\n", + "identifier": "EditImageBodyJsonParam" +}) +export type CreateImageEditRequestMultipart = { + readonly "image": __ClientMultipartFile | ReadonlyArray<__ClientMultipartFile> + readonly "prompt": string + readonly "mask"?: __ClientMultipartFile + readonly "background"?: "transparent" | "opaque" | "auto" | null + readonly "model": + | string + | "gpt-image-1.5" + | "gpt-image-2" + | "gpt-image-2-2026-04-21" + | "gpt-image-2.5-sunburst" + | "gpt-image-2.5-sunburst-2026-09-08" + | "gpt-image-2.5-flare" + | "gpt-image-2.5-flare-2026-09-08" + | "gpt-image-1" + | "gpt-image-1-mini" + | "chatgpt-image-latest" + | "dall-e-2" + readonly "n"?: number | null + readonly "size"?: string | "1024x1024" | "1536x1024" | "1024x1536" | "auto" | "256x256" | "512x512" | null + readonly "response_format"?: "url" | "b64_json" | null + readonly "output_format"?: "png" | "jpeg" | "webp" | null + readonly "output_compression"?: number | null + readonly "user"?: string + readonly "input_fidelity"?: InputFidelity | null + readonly "stream"?: boolean | null + readonly "partial_images"?: PartialImages + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | "standard" | null +} +export const CreateImageEditRequestMultipart = Schema.Struct({ + "image": Schema.Union([ + __ClientMultipartFile, + Schema.Array(__ClientMultipartFile).check( + Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" }) + ) + ]).annotate({ + "description": + "The image(s) to edit. Must be a supported image file or an array of images.\n\nFor the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,\n`gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,\n`gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and\n`gpt-image-2.5-flare-2026-09-08`), each image should be a `png`, `webp`, or `jpg`\nfile less than 50MB. You can provide up to 16 images. `chatgpt-image-latest`\nfollows the same input constraints as GPT image models.\n" + }), + "prompt": Schema.String.annotate({ + "description": + "A text description of the desired image(s). The maximum length is 32000 characters for the GPT image models." + }), + "mask": Schema.optionalKey(__ClientMultipartFile), + "background": Schema.optionalKey( + Schema.Union([Schema.Literals(["transparent", "opaque", "auto"]), Schema.Null]).annotate({ + "description": + "Set the background of the generated image(s). This parameter is only supported for\nthe GPT image models. Must be one of `transparent`, `opaque`, or `auto` (default\nvalue). When `auto` is used, the model will automatically determine the best\nbackground for the image.\n\n`gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`\nsnapshots, support `opaque` and `transparent` backgrounds. Transparent backgrounds\nare available for supported GPT Image models. For `gpt-image-2` and\n`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,\nset the output format to `png` or `webp`.\n" + }) + ), + "model": Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-image-1.5", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1", + "gpt-image-1-mini", + "chatgpt-image-latest", + "dall-e-2" + ]) + ]).annotate({ + "description": + "The GPT image model to use for image editing (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`)." + }), + "n": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(10).annotate({ "expected": "a value less than or equal to 10" })), + Schema.Null + ]).annotate({ "description": "The number of images to generate. Must be between 1 and 10." }) + ), + "size": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals(["1024x1024", "1536x1024", "1024x1536", "auto", "256x256", "512x512"]), + Schema.Null + ]).annotate({ + "description": + "The size of the generated images. Defaults to `auto`. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing." + }) + ), + "response_format": Schema.optionalKey( + Schema.Union([Schema.Literals(["url", "b64_json"]), Schema.Null]).annotate({ + "description": + "Legacy response format parameter for retired image models. Unsupported for GPT image models, which always return base64-encoded images." + }) + ), + "output_format": Schema.optionalKey( + Schema.Union([Schema.Literals(["png", "jpeg", "webp"]), Schema.Null]).annotate({ + "description": + "The format in which the generated images are returned. This parameter is\nonly supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`.\nThe default value is `png`.\n" + }) + ), + "output_compression": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": + "The compression level (0-100%) for the generated images. This parameter\nis only supported for the GPT image models with the `webp` or `jpeg` output\nformats, and defaults to 100.\n" + }) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ), + "input_fidelity": Schema.optionalKey(Schema.Union([InputFidelity, Schema.Null])), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "Edit the image in streaming mode. Defaults to `false`. See the\n[Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.\n" + }) + ), + "partial_images": Schema.optionalKey(PartialImages), + "quality": Schema.optionalKey( + Schema.Union([Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto", "standard"]), Schema.Null]) + .annotate({ + "description": + "The quality of the image that will be generated for GPT image models. The GPT image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.\n" + }) + ) +}).annotate({ "identifier": "CreateImageEditRequestMultipart" }) +export type CreateImageRequest = { + readonly "prompt": string + readonly "model": + | string + | "gpt-image-1.5" + | "gpt-image-2" + | "gpt-image-2-2026-04-21" + | "gpt-image-2.5-sunburst" + | "gpt-image-2.5-sunburst-2026-09-08" + | "gpt-image-2.5-flare" + | "gpt-image-2.5-flare-2026-09-08" + | "gpt-image-1" + | "gpt-image-1-mini" + | "chatgpt-image-latest" + | "dall-e-2" + | "dall-e-3" + readonly "n"?: number | null + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | "standard" | "hd" | null + readonly "response_format"?: "url" | "b64_json" | null + readonly "output_format"?: "png" | "jpeg" | "webp" | null + readonly "output_compression"?: number | null + readonly "stream"?: boolean | null + readonly "partial_images"?: PartialImages + readonly "size"?: + | string + | "auto" + | "1024x1024" + | "1536x1024" + | "1024x1536" + | "256x256" + | "512x512" + | "1792x1024" + | "1024x1792" + | null + readonly "moderation"?: "low" | "auto" | null + readonly "background"?: "transparent" | "opaque" | "auto" | null + readonly "style"?: "vivid" | "natural" | null + readonly "user"?: string +} +export const CreateImageRequest = Schema.Struct({ + "prompt": Schema.String.annotate({ + "description": "A text description of the desired image(s). The maximum length is 32000 characters." + }), + "model": Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-image-1.5", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08", + "gpt-image-1", + "gpt-image-1-mini", + "chatgpt-image-latest", + "dall-e-2", + "dall-e-3" + ]) + ]).annotate({ + "description": + "The GPT image model to use for image generation. Specify a model explicitly. Supported models include `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`." + }), + "n": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(10).annotate({ "expected": "a value less than or equal to 10" })), + Schema.Null + ]).annotate({ "description": "The number of images to generate. Must be between 1 and 10." }) + ), + "quality": Schema.optionalKey( + Schema.Union([Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto", "standard", "hd"]), Schema.Null]) + .annotate({ + "description": + "The quality of the image that will be generated.\n\n- `auto` (default value) will automatically select the best quality for the given\n model.\n- `high`, `medium` and `low` are supported for the GPT image models.\n- `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`\n snapshots, also support `xhigh` and `max`.\n" + }) + ), + "response_format": Schema.optionalKey( + Schema.Union([Schema.Literals(["url", "b64_json"]), Schema.Null]).annotate({ + "description": + "Legacy response format parameter for retired image models. Unsupported for GPT image models, which always return base64-encoded images." + }) + ), + "output_format": Schema.optionalKey( + Schema.Union([Schema.Literals(["png", "jpeg", "webp"]), Schema.Null]).annotate({ + "description": + "The format in which the generated images are returned. This parameter is only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`." + }) + ), + "output_compression": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": + "The compression level (0-100%) for the generated images. This parameter is only supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100." + }) + ), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "Generate the image in streaming mode. Defaults to `false`. See the\n[Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.\nThis parameter is only supported for the GPT image models.\n" + }) + ), + "partial_images": Schema.optionalKey(PartialImages), + "size": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals(["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"]), + Schema.Null + ]).annotate({ + "description": + "The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing." + }) + ), + "moderation": Schema.optionalKey( + Schema.Union([Schema.Literals(["low", "auto"]), Schema.Null]).annotate({ + "description": + "Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default value)." + }) + ), + "background": Schema.optionalKey( + Schema.Union([Schema.Literals(["transparent", "opaque", "auto"]), Schema.Null]).annotate({ + "description": + "Set the background of the generated image(s). This parameter is only supported for\nthe GPT image models. Must be one of `transparent`, `opaque`, or `auto` (default\nvalue). When `auto` is used, the model will automatically determine the best\nbackground for the image.\n\n`gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`\nsnapshots, support `opaque` and `transparent` backgrounds. Transparent backgrounds\nare available for supported GPT Image models. For `gpt-image-2` and\n`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,\nset the output format to `png` or `webp`.\n" + }) + ), + "style": Schema.optionalKey( + Schema.Union([Schema.Literals(["vivid", "natural"]), Schema.Null]).annotate({ + "description": + "Legacy style parameter for retired image models. Unsupported for GPT image models; describe the desired style in the prompt instead." + }) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ) +}).annotate({ "identifier": "CreateImageRequest" }) +export type ImageGenUsage = { + readonly "input_tokens": number + readonly "total_tokens": number + readonly "output_tokens": number + readonly "output_tokens_details"?: ImageGenOutputTokensDetails + readonly "input_tokens_details": ImageGenInputUsageDetails +} +export const ImageGenUsage = Schema.Struct({ + "input_tokens": Schema.Number.annotate({ + "description": "The number of tokens (images and text) in the input prompt." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "total_tokens": Schema.Number.annotate({ + "description": "The total number of tokens (images and text) used for the image generation." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_tokens": Schema.Number.annotate({ "description": "The number of output tokens generated by the model." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_tokens_details": Schema.optionalKey(ImageGenOutputTokensDetails), + "input_tokens_details": ImageGenInputUsageDetails +}).annotate({ + "title": "Image generation usage", + "description": "For `gpt-image-1` only, the token usage information for the image generation.", + "identifier": "ImageGenUsage" +}) +export type ImageEditCompletedEvent = { + readonly "type": "image_edit.completed" + readonly "b64_json": string + readonly "created_at": number + readonly "size": string | "1024x1024" | "1024x1536" | "1536x1024" | "auto" + readonly "quality": "low" | "medium" | "high" | "xhigh" | "max" | "auto" + readonly "background": "transparent" | "opaque" | "auto" + readonly "output_format": "png" | "webp" | "jpeg" + readonly "usage": ImagesUsage +} +export const ImageEditCompletedEvent = Schema.Struct({ + "type": Schema.Literal("image_edit.completed").annotate({ + "description": "The type of the event. Always `image_edit.completed`.\n" + }), + "b64_json": Schema.String.annotate({ + "description": "Base64-encoded final edited image data, suitable for rendering as an image.\n" + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp when the event was created.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "size": Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024", "auto"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + "quality": Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": "The quality setting for the edited image.\n" + }), + "background": Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "description": "The background setting for the edited image.\n" + }), + "output_format": Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format for the edited image.\n" + }), + "usage": ImagesUsage +}).annotate({ + "description": "Emitted when image editing has completed and the final image is available.\n", + "identifier": "ImageEditCompletedEvent" +}) +export type ImageGenCompletedEvent = { + readonly "type": "image_generation.completed" + readonly "b64_json": string + readonly "created_at": number + readonly "size": string | "1024x1024" | "1024x1536" | "1536x1024" | "auto" + readonly "quality": "low" | "medium" | "high" | "xhigh" | "max" | "auto" + readonly "background": "transparent" | "opaque" | "auto" + readonly "output_format": "png" | "webp" | "jpeg" + readonly "usage": ImagesUsage +} +export const ImageGenCompletedEvent = Schema.Struct({ + "type": Schema.Literal("image_generation.completed").annotate({ + "description": "The type of the event. Always `image_generation.completed`.\n" + }), + "b64_json": Schema.String.annotate({ + "description": "Base64-encoded image data, suitable for rendering as an image.\n" + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp when the event was created.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "size": Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024", "auto"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + "quality": Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": "The quality setting for the generated image.\n" + }), + "background": Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "description": "The background setting for the generated image.\n" + }), + "output_format": Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format for the generated image.\n" + }), + "usage": ImagesUsage +}).annotate({ + "description": "Emitted when image generation has completed and the final image is available.\n", + "identifier": "ImageGenCompletedEvent" +}) +export type LiveInitialSessionAudioOutputParam = { + readonly "voice"?: + | string + | "alloy" + | "ash" + | "ballad" + | "beacon" + | "bossa" + | "brise" + | "cedar" + | "cinder" + | "coral" + | "delta" + | "echo" + | "flitz" + | "gleam" + | "harema" + | "juni" + | "marin" + | "meridian" + | "nira" + | "noeul" + | "nuri" + | "quartz" + | "ripple" + | "sage" + | "shida" + | "shimmer" + | "sillage" + | "stone" + | "tempo" + | "verse" + | "vesper" + | "willow" + | LiveCustomVoiceParam +} +export const LiveInitialSessionAudioOutputParam = Schema.Struct({ + "voice": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.String, + Schema.Literals([ + "alloy", + "ash", + "ballad", + "beacon", + "bossa", + "brise", + "cedar", + "cinder", + "coral", + "delta", + "echo", + "flitz", + "gleam", + "harema", + "juni", + "marin", + "meridian", + "nira", + "noeul", + "nuri", + "quartz", + "ripple", + "sage", + "shida", + "shimmer", + "sillage", + "stone", + "tempo", + "verse", + "vesper", + "willow" + ]) + ]), + LiveCustomVoiceParam + ], { mode: "oneOf" }).annotate({ + "description": + "The voice used for Live speech, as a built-in voice name or a custom voice object containing its ID. Defaults to `marin` and cannot change after startup." + }) + ) +}).annotate({ + "description": "Settings for speech generated by the Live model. Choose the voice before starting the session.", + "identifier": "LiveInitialSessionAudioOutputParam" +}) +export type LiveDelegationReasoningInputParam = { + readonly "effort"?: LiveReasoningEffort | null + readonly "summary"?: LiveReasoningSummary | null +} +export const LiveDelegationReasoningInputParam = Schema.Struct({ + "effort": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveReasoningEffort).annotate({ + "description": + "How much reasoning effort the delegated Responses model should use. Supported values depend on the backend model." + }), + Schema.Null + ]) + ), + "summary": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveReasoningSummary).annotate({ + "description": "The reasoning summary to request from the delegated Responses model, when supported." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "Reasoning options for Responses requests made on behalf of the Live session.", + "identifier": "LiveDelegationReasoningInputParam" +}) +export type LiveDelegationTextInputParam = { readonly "verbosity"?: LiveTextVerbosity | null } +export const LiveDelegationTextInputParam = Schema.Struct({ + "verbosity": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveTextVerbosity).annotate({ + "description": + "The amount of detail in text generated by the Responses backend. This does not configure the Live model’s spoken delivery." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "Text generation options for the Live session’s Responses backend.", + "identifier": "LiveDelegationTextInputParam" +}) +export type LiveInlineSkillParam = { + readonly "type": "inline" + readonly "name": string + readonly "description": string + readonly "source": LiveInlineSkillSourceParam +} +export const LiveInlineSkillParam = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "Defines an inline skill for this request." }), + "name": Schema.String.annotate({ "description": "The name of the skill." }), + "description": Schema.String.annotate({ "description": "The description of the skill." }), + "source": Schema.suspend((): Schema.Codec => LiveInlineSkillSourceParam).annotate({ + "description": "Inline skill payload" + }) +}).annotate({ "identifier": "LiveInlineSkillParam" }) +export type LiveLocalEnvironmentParam = { + readonly "type": "local" + readonly "skills"?: ReadonlyArray | null +} +export const LiveLocalEnvironmentParam = Schema.Struct({ + "type": Schema.Literal("local").annotate({ "description": "Use a local computer environment." }), + "skills": Schema.optionalKey( + Schema.Union([ + Schema.Array(LiveLocalSkillParam).annotate({ "description": "An optional list of skills." }).check( + Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "LiveLocalEnvironmentParam" }) +export type LiveAllowedToolsChoiceParam = { + readonly "type": "allowed_tools" + readonly "tools": ReadonlyArray< + | LiveFunctionToolChoiceParam + | LiveMCPToolChoiceParam + | LiveSpecificFileSearchParam + | LiveSpecificWebSearchParam + | LiveSpecificWebSearchPreviewParam + | LiveSpecificImageGenParam + | LiveSpecificComputerParam + | LiveSpecificCodeInterpreterParam + | LiveSpecificProgrammaticToolCallingParam + | LiveSpecificFunctionShellParam + | LiveSpecificCustomToolParam + | LiveSpecificApplyPatchParam + > + readonly "mode"?: LiveToolChoiceValueEnum | null +} +export const LiveAllowedToolsChoiceParam = Schema.Struct({ + "type": Schema.Literal("allowed_tools").annotate({ "description": "The tool choice type. Always `allowed_tools`." }), + "tools": Schema.Array( + Schema.Union([ + LiveFunctionToolChoiceParam, + LiveMCPToolChoiceParam, + LiveSpecificFileSearchParam, + LiveSpecificWebSearchParam, + LiveSpecificWebSearchPreviewParam, + LiveSpecificImageGenParam, + LiveSpecificComputerParam, + LiveSpecificCodeInterpreterParam, + LiveSpecificProgrammaticToolCallingParam, + LiveSpecificFunctionShellParam, + LiveSpecificCustomToolParam, + LiveSpecificApplyPatchParam + ], { mode: "oneOf" }) + ).annotate({ "description": "The tools that the delegated Responses model may call." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxLength(128).annotate({ "expected": "a value with a length of at most 128" })), + "mode": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveToolChoiceValueEnum).annotate({ + "description": "How to select a tool from the allowed set." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "LiveAllowedToolsChoiceParam" }) +export type LiveInitialDeveloperMessageItemParam = { + readonly "id"?: string | null + readonly "type"?: "message" + readonly "status"?: LiveInitialMessageStatus | null + readonly "role": "developer" + readonly "content": ReadonlyArray +} +export const LiveInitialDeveloperMessageItemParam = Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation." + }), + Schema.Null + ]) + ), + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The history item type. Always `message`." }) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveInitialMessageStatus).annotate({ + "description": + "The supplied message’s status. Live uses its text as history and does not resume an incomplete message." + }), + Schema.Null + ]) + ), + "role": Schema.Literal("developer").annotate({ + "description": "The author of this history message. Always `developer`." + }), + "content": Schema.Array(LiveInitialInputTextContentPartParam).annotate({ + "description": "The message content. Supply exactly one text part for the initial Live conversation history." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" }) + ) +}).annotate({ + "description": "A developer message included in the initial text history of a Live session.", + "identifier": "LiveInitialDeveloperMessageItemParam" +}) +export type LiveInitialUserMessageItemParam = { + readonly "id"?: string | null + readonly "type"?: "message" + readonly "status"?: LiveInitialMessageStatus | null + readonly "role": "user" + readonly "content": ReadonlyArray +} +export const LiveInitialUserMessageItemParam = Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation." + }), + Schema.Null + ]) + ), + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The history item type. Always `message`." }) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveInitialMessageStatus).annotate({ + "description": + "The supplied message’s status. Live uses its text as history and does not resume an incomplete message." + }), + Schema.Null + ]) + ), + "role": Schema.Literal("user").annotate({ "description": "The author of this history message. Always `user`." }), + "content": Schema.Array(LiveInitialInputTextContentPartParam).annotate({ + "description": "The message content. Supply exactly one text part for the initial Live conversation history." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" }) + ) +}).annotate({ + "description": "A user message included in the initial text history of a Live session.", + "identifier": "LiveInitialUserMessageItemParam" +}) +export type LiveInitialAssistantMessageItemParam = { + readonly "id"?: string | null + readonly "type"?: "message" + readonly "status"?: LiveInitialMessageStatus | null + readonly "role": "assistant" + readonly "content": ReadonlyArray +} +export const LiveInitialAssistantMessageItemParam = Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation." + }), + Schema.Null + ]) + ), + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The history item type. Always `message`." }) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveInitialMessageStatus).annotate({ + "description": + "The supplied message’s status. Live uses its text as history and does not resume an incomplete message." + }), + Schema.Null + ]) + ), + "role": Schema.Literal("assistant").annotate({ + "description": "The author of this history message. Always `assistant`." + }), + "content": Schema.Array( + Schema.Union([LiveInitialTextContentPartParam, LiveInitialOutputTextContentPartParam], { mode: "oneOf" }) + ).annotate({ + "description": "The message content. Supply exactly one text part for the initial Live conversation history." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" }) + ) +}).annotate({ + "description": "An assistant message included in the initial text history of a Live session.", + "identifier": "LiveInitialAssistantMessageItemParam" +}) +export type LiveDataChannelConfigParam = { + readonly "allowed_client_events"?: "all" | ReadonlyArray + readonly "allowed_server_events"?: "all" | ReadonlyArray +} +export const LiveDataChannelConfigParam = Schema.Struct({ + "allowed_client_events": Schema.optionalKey( + Schema.Union([ + Schema.Literal("all"), + Schema.Array( + Schema.String.annotate({ + "description": + "A Live client event type allowed on the frontend data channel, such as `session.input_audio.mute`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ).check( + Schema.isPattern(new RegExp("^(?:error|info|[a-z][a-z0-9_-]*(?:\\.[a-z0-9_-]+)+)$", "u")).annotate({ + "expected": "a string matching the RegExp ^(?:error|info|[a-z][a-z0-9_-]*(?:\\.[a-z0-9_-]+)+)$" + }) + ) + ).check(Schema.isMaxLength(256).annotate({ "expected": "a value with a length of at most 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Client event types that the frontend data channel may send. Use 'all' to allow every client event; an empty array allows none. Omission preserves the existing allow-all behavior." + }) + ), + "allowed_server_events": Schema.optionalKey( + Schema.Union([ + Schema.Literal("all"), + Schema.Array(LiveAllowedServerEventParam).check( + Schema.isMaxLength(256).annotate({ "expected": "a value with a length of at most 256" }) + ) + ], { mode: "oneOf" }).annotate({ + "description": + "Server events that may be sent to the frontend data channel. Use 'all' to allow every server event; an empty array allows none. Omission preserves the existing allow-all behavior. Responses events use an object with type 'response.event' and a response_event selector." + }) + ) +}).annotate({ + "description": + "Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections.", + "identifier": "LiveDataChannelConfigParam" +}) +export type LiveCreateResponse = { + readonly "session": { readonly "id": string } + readonly "transport": LiveWebRTCTransport +} +export const LiveCreateResponse = Schema.Struct({ + "session": Schema.Struct({ + "id": Schema.String.annotate({ + "description": "Opaque session identifier. Preserve the returned value unchanged, including its prefix." + }) + }).annotate({ + "description": "The newly created Live session. Use its ID for session controls and sideband connections." + }), + "transport": Schema.suspend((): Schema.Codec => LiveWebRTCTransport).annotate({ + "description": "WebRTC transport with the SDP answer." + }) +}).annotate({ + "description": + "The created Live session identifier and WebRTC answer. Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands.", + "identifier": "LiveCreateResponse" +}) +export type LiveSIPTrunk = { + readonly "provider_url": string + readonly "auth": LiveSIPTrunkAuth + readonly "caller_number": string +} +export const LiveSIPTrunk = Schema.Struct({ + "provider_url": Schema.String.annotate({ + "description": + "Provider endpoint in the form `sips:host[:port][;transport=tcp]`. The default port is 5061. IPv6 addresses must be bracketed. TLS signaling is required; plaintext SIP is not supported. Do not include userinfo, a path, URI headers, or other URI parameters. Local hostnames and literal private, loopback, link-local, unspecified, multicast, and broadcast IP addresses are rejected." + }), + "auth": LiveSIPTrunkAuth, + "caller_number": Schema.String.annotate({ + "description": "Caller phone number in E.164 format to use in the SIP From header." + }).check( + Schema.isPattern(new RegExp("^\\+[1-9][0-9]{1,14}$", "u")).annotate({ + "expected": "a string matching the RegExp ^\\+[1-9][0-9]{1,14}$" + }) + ) +}).annotate({ + "description": + "SIP trunk configuration supplied for this call. Keep trunk credentials on your server. The creation response does not return this configuration.", + "identifier": "LiveSIPTrunk" +}) +export type LiveSessionCreateResponse = { + readonly "session": Session + readonly "transport": LiveWebRTCTransport | { readonly "type": "sip" } +} +export const LiveSessionCreateResponse = Schema.Struct({ + "session": Session, + "transport": Schema.Union([ + LiveWebRTCTransport, + Schema.Struct({ + "type": Schema.Literal("sip").annotate({ + "description": "The transport used for the Live session. Always `sip`." + }) + }).annotate({ "description": "The SIP transport. Trunk credentials and SDP are not returned." }) + ], { mode: "oneOf" }).annotate({ + "description": + "WebRTC transport with an SDP answer, or SIP transport without SDP or trunk credentials. For SIP, attach a sideband using session.id to receive transport.ringing, transport.answered, and transport.failed notifications." + }) +}).annotate({ + "description": + "The created Live session and its transport. WebRTC creation returns 201 Created with an SDP answer. Outbound SIP creation returns 201 Created after initialization, before the callee necessarily answers, without SDP or trunk credentials.", + "identifier": "LiveSessionCreateResponse" +}) +export type ListModelsResponse = { readonly "object": "list"; readonly "data": ReadonlyArray } +export const ListModelsResponse = Schema.Struct({ "object": Schema.Literal("list"), "data": Schema.Array(Model) }) + .annotate({ "identifier": "ListModelsResponse" }) +export type ApiKeyList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "first_id"?: string | null + readonly "last_id"?: string | null +} +export const ApiKeyList = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(AdminApiKey), + "has_more": Schema.Boolean, + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) +}).annotate({ "identifier": "ApiKeyList" }) +export type AuditLogActorSession = { readonly "user"?: AuditLogActorUser; readonly "ip_address"?: string } +export const AuditLogActorSession = Schema.Struct({ + "user": Schema.optionalKey(AuditLogActorUser), + "ip_address": Schema.optionalKey( + Schema.String.annotate({ "description": "The IP address from which the action was performed." }) + ) +}).annotate({ + "description": "The session in which the audit logged action was performed.", + "identifier": "AuditLogActorSession" +}) +export type AuditLogActorApiKey = { + readonly "id"?: string + readonly "type"?: "user" | "service_account" + readonly "user"?: AuditLogActorUser + readonly "service_account"?: AuditLogActorServiceAccount +} +export const AuditLogActorApiKey = Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The tracking id of the API key." })), + "type": Schema.optionalKey( + Schema.Literals(["user", "service_account"]).annotate({ + "description": "The type of API key. Can be either `user` or `service_account`." + }) + ), + "user": Schema.optionalKey(AuditLogActorUser), + "service_account": Schema.optionalKey(AuditLogActorServiceAccount) +}).annotate({ + "description": "The API Key used to perform the audit logged action.", + "identifier": "AuditLogActorApiKey" +}) +export type Provider = + | AwsExternalStorageProviderResponse + | AzureExternalStorageProviderResponse + | GcpExternalStorageProviderResponse + | OciExternalStorageProviderResponse +export const Provider = Schema.Union([ + AwsExternalStorageProviderResponse, + AzureExternalStorageProviderResponse, + GcpExternalStorageProviderResponse, + OciExternalStorageProviderResponse +], { mode: "oneOf" }).annotate({ "identifier": "provider" }) +export type ListCertificatesResponse = { + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean + readonly "object": "list" +} +export const ListCertificatesResponse = Schema.Struct({ + "data": Schema.Array(OrganizationCertificate), + "first_id": Schema.Union([Schema.String, Schema.Null]), + "last_id": Schema.Union([Schema.String, Schema.Null]), + "has_more": Schema.Boolean, + "object": Schema.Literal("list") +}).annotate({ "identifier": "ListCertificatesResponse" }) +export type OrganizationCertificateActivationResponse = { + readonly "object": "organization.certificate.activation" + readonly "data": ReadonlyArray +} +export const OrganizationCertificateActivationResponse = Schema.Struct({ + "object": Schema.Literal("organization.certificate.activation").annotate({ + "description": "The organization certificate activation result type." + }), + "data": Schema.Array(OrganizationCertificate) +}).annotate({ "identifier": "OrganizationCertificateActivationResponse" }) +export type OrganizationCertificateDeactivationResponse = { + readonly "object": "organization.certificate.deactivation" + readonly "data": ReadonlyArray +} +export const OrganizationCertificateDeactivationResponse = Schema.Struct({ + "object": Schema.Literal("organization.certificate.deactivation").annotate({ + "description": "The organization certificate deactivation result type." + }), + "data": Schema.Array(OrganizationCertificate) +}).annotate({ "identifier": "OrganizationCertificateDeactivationResponse" }) +export type UsageTimeBucket = { + readonly "object": "bucket" + readonly "start_time": number + readonly "end_time": number + readonly "results": ReadonlyArray< + | UsageCompletionsResult + | UsageEmbeddingsResult + | UsageModerationsResult + | UsageImagesResult + | UsageAudioSpeechesResult + | UsageAudioTranscriptionsResult + | UsageVectorStoresResult + | UsageCodeInterpreterSessionsResult + | UsageFileSearchCallsResult + | UsageWebSearchCallsResult + | CostsResult + > +} +export const UsageTimeBucket = Schema.Struct({ + "object": Schema.Literal("bucket"), + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "results": Schema.Array( + Schema.Union([ + UsageCompletionsResult, + UsageEmbeddingsResult, + UsageModerationsResult, + UsageImagesResult, + UsageAudioSpeechesResult, + UsageAudioTranscriptionsResult, + UsageVectorStoresResult, + UsageCodeInterpreterSessionsResult, + UsageFileSearchCallsResult, + UsageWebSearchCallsResult, + CostsResult + ], { mode: "oneOf" }) + ) +}).annotate({ "identifier": "UsageTimeBucket" }) +export type GroupListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next": string | null +} +export const GroupListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(GroupResponse).annotate({ "description": "Groups returned in the current page." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether additional groups are available when paginating." }), + "next": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Cursor to fetch the next page of results, or `null` if there are no more results." + }) +}).annotate({ "description": "Paginated list of organization groups.", "identifier": "GroupListResource" }) +export type RoleListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next": string | null +} +export const RoleListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(AssignedRoleDetails).annotate({ + "description": "Role assignments returned in the current page." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether additional assignments are available when paginating." + }), + "next": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Cursor to fetch the next page of results, or `null` when there are no more assignments." + }) +}).annotate({ "description": "Paginated list of roles assigned to a principal.", "identifier": "RoleListResource" }) +export type GroupRoleAssignment = { readonly "object": "group.role"; readonly "group": Group; readonly "role": Role } +export const GroupRoleAssignment = Schema.Struct({ + "object": Schema.Literal("group.role").annotate({ "description": "Always `group.role`." }), + "group": Group, + "role": Role +}).annotate({ "description": "Role assignment linking a group to a role.", "identifier": "GroupRoleAssignment" }) +export type PublicRoleListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next": string | null +} +export const PublicRoleListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(Role).annotate({ "description": "Roles returned in the current page." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether more roles are available when paginating." }), + "next": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Cursor to fetch the next page of results, or `null` when there are no additional roles." + }) +}).annotate({ + "description": "Paginated list of roles available on an organization or project.", + "identifier": "PublicRoleListResource" +}) +export type UserListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next": string | null +} +export const UserListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(GroupUser).annotate({ "description": "Users in the current page." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether more users are available when paginating." }), + "next": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Cursor to fetch the next page of results, or `null` when no further users are available." + }) +}).annotate({ + "description": "Paginated list of user objects returned when inspecting group membership.", + "identifier": "UserListResource" +}) +export type InviteListResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const InviteListResponse = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`" }), + "data": Schema.Array(Invite), + "first_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The first `invite_id` in the retrieved `list`" + }) + ), + "last_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The last `invite_id` in the retrieved `list`" + }) + ), + "has_more": Schema.Boolean.annotate({ + "description": "The `has_more` property is used for pagination to indicate there are additional results." + }) +}).annotate({ "identifier": "InviteListResponse" }) +export type Project = { + readonly "id": string + readonly "object": "organization.project" + readonly "name"?: string | null + readonly "created_at": number + readonly "archived_at"?: number | null + readonly "status"?: string | null + readonly "external_key_id"?: string | null + readonly "residency"?: PublicProjectResidency +} +export const Project = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "object": Schema.Literal("organization.project").annotate({ + "description": "The object type, which is always `organization.project`" + }), + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The name of the project. This appears in reporting." + }) + ), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the project was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "archived_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the project was archived or `null`.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "status": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "`active` or `archived`" }) + ), + "external_key_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The external key associated with the project." + }) + ), + "residency": Schema.optionalKey( + Schema.suspend((): Schema.Codec => PublicProjectResidency).annotate({ + "description": "The residency configuration for the project." + }) + ) +}).annotate({ "description": "Represents an individual project.", "identifier": "Project" }) +export type ProjectCreateRequest = { + readonly "name": string + readonly "geography"?: string | null + readonly "residency"?: PublicProjectResidency | null + readonly "external_key_id"?: string | null +} +export const ProjectCreateRequest = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The friendly name of the project, this name appears in reports." }), + "geography": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.\nDeprecated: use `residency` instead. Do not provide both `geography` and `residency`." + }) + ), + "residency": Schema.optionalKey( + Schema.Union([PublicProjectResidency, Schema.Null]).annotate({ + "description": + "Create the project with the specified residency configuration. Your organization must have access to the requested residency configuration in order to use it. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field." + }) + ), + "external_key_id": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "External key ID to associate with the project." + }) + ) +}).annotate({ "identifier": "ProjectCreateRequest" }) +export type ProjectApiKey = { + readonly "object": "organization.project.api_key" + readonly "redacted_value": string + readonly "name": string + readonly "created_at": number + readonly "expires_at"?: number | null + readonly "last_used_at": number | null + readonly "id": string + readonly "owner_project_access": "active" | "inactive" + readonly "owner": { + readonly "type"?: "user" | "service_account" + readonly "user"?: ProjectApiKeyOwnerUser + readonly "service_account"?: ProjectApiKeyOwnerServiceAccount + } +} +export const ProjectApiKey = Schema.Struct({ + "object": Schema.Literal("organization.project.api_key").annotate({ + "description": "The object type, which is always `organization.project.api_key`" + }), + "redacted_value": Schema.String.annotate({ "description": "The redacted value of the API key" }), + "name": Schema.String.annotate({ "description": "The name of the API key" }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the API key was created", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ + "description": "The Unix timestamp (in seconds) when the API key expires, or null if it does not expire." + }) + ), + "last_used_at": Schema.Union([ + Schema.Number.annotate({ "format": "unixtime" }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ "description": "The Unix timestamp (in seconds) of when the API key was last used." }), + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints" }), + "owner_project_access": Schema.Literals(["active", "inactive"]).annotate({ + "description": "Whether the API key's owner currently has effective access to the project." + }), + "owner": Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literals(["user", "service_account"]).annotate({ "description": "`user` or `service_account`" }) + ), + "user": Schema.optionalKey(ProjectApiKeyOwnerUser), + "service_account": Schema.optionalKey(ProjectApiKeyOwnerServiceAccount) + }) +}).annotate({ "description": "Represents an individual API key in a project.", "identifier": "ProjectApiKey" }) +export type ListProjectCertificatesResponse = { + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean + readonly "object": "list" +} +export const ListProjectCertificatesResponse = Schema.Struct({ + "data": Schema.Array(OrganizationProjectCertificate), + "first_id": Schema.Union([Schema.String, Schema.Null]), + "last_id": Schema.Union([Schema.String, Schema.Null]), + "has_more": Schema.Boolean, + "object": Schema.Literal("list") +}).annotate({ "identifier": "ListProjectCertificatesResponse" }) +export type OrganizationProjectCertificateActivationResponse = { + readonly "object": "organization.project.certificate.activation" + readonly "data": ReadonlyArray +} +export const OrganizationProjectCertificateActivationResponse = Schema.Struct({ + "object": Schema.Literal("organization.project.certificate.activation").annotate({ + "description": "The project certificate activation result type." + }), + "data": Schema.Array(OrganizationProjectCertificate) +}).annotate({ "identifier": "OrganizationProjectCertificateActivationResponse" }) +export type OrganizationProjectCertificateDeactivationResponse = { + readonly "object": "organization.project.certificate.deactivation" + readonly "data": ReadonlyArray +} +export const OrganizationProjectCertificateDeactivationResponse = Schema.Struct({ + "object": Schema.Literal("organization.project.certificate.deactivation").annotate({ + "description": "The project certificate deactivation result type." + }), + "data": Schema.Array(OrganizationProjectCertificate) +}).annotate({ "identifier": "OrganizationProjectCertificateDeactivationResponse" }) +export type ProjectGroupListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next": string | null +} +export const ProjectGroupListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(ProjectGroup).annotate({ + "description": "Project group memberships returned in the current page." + }), + "has_more": Schema.Boolean.annotate({ "description": "Whether additional project group memberships are available." }), + "next": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Cursor to fetch the next page of results, or `null` when there are no more results." + }) +}).annotate({ + "description": "Paginated list of groups that have access to a project.", + "identifier": "ProjectGroupListResource" +}) +export type ProjectHostedToolPermissions = { + readonly "file_search": HostedToolPermission + readonly "web_search": HostedToolPermission + readonly "image_generation": HostedToolPermission + readonly "mcp": HostedToolPermission + readonly "code_interpreter": HostedToolPermission +} +export const ProjectHostedToolPermissions = Schema.Struct({ + "file_search": HostedToolPermission, + "web_search": HostedToolPermission, + "image_generation": HostedToolPermission, + "mcp": HostedToolPermission, + "code_interpreter": HostedToolPermission +}).annotate({ + "description": "Represents hosted tool permissions for a project.", + "identifier": "ProjectHostedToolPermissions" +}) +export type ProjectHostedToolPermissionsUpdateRequest = { + readonly "file_search"?: HostedToolPermissionUpdate | null + readonly "web_search"?: HostedToolPermissionUpdate | null + readonly "image_generation"?: HostedToolPermissionUpdate | null + readonly "mcp"?: HostedToolPermissionUpdate | null + readonly "code_interpreter"?: HostedToolPermissionUpdate | null +} +export const ProjectHostedToolPermissionsUpdateRequest = Schema.Struct({ + "file_search": Schema.optionalKey( + Schema.Union([HostedToolPermissionUpdate, Schema.Null]).annotate({ + "description": "The file search permission update." + }) + ), + "web_search": Schema.optionalKey( + Schema.Union([HostedToolPermissionUpdate, Schema.Null]).annotate({ + "description": "The web search permission update." + }) + ), + "image_generation": Schema.optionalKey( + Schema.Union([HostedToolPermissionUpdate, Schema.Null]).annotate({ + "description": "The image generation permission update." + }) + ), + "mcp": Schema.optionalKey( + Schema.Union([HostedToolPermissionUpdate, Schema.Null]).annotate({ "description": "The MCP permission update." }) + ), + "code_interpreter": Schema.optionalKey( + Schema.Union([HostedToolPermissionUpdate, Schema.Null]).annotate({ + "description": "The code interpreter permission update." + }) + ) +}).annotate({ "identifier": "ProjectHostedToolPermissionsUpdateRequest" }) +export type ProjectRateLimitListResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ProjectRateLimitListResponse = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(ProjectRateLimit), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ProjectRateLimitListResponse" }) +export type ProjectServiceAccountListResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ProjectServiceAccountListResponse = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(ProjectServiceAccount), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ProjectServiceAccountListResponse" }) +export type ProjectServiceAccountCreateResponse = { + readonly "object": "organization.project.service_account" + readonly "id": string + readonly "name": string + readonly "role": "member" | "none" + readonly "created_at": number + readonly "api_key": ProjectServiceAccountApiKey | null +} +export const ProjectServiceAccountCreateResponse = Schema.Struct({ + "object": Schema.Literal("organization.project.service_account"), + "id": Schema.String, + "name": Schema.String, + "role": Schema.Literals(["member", "none"]).annotate({ + "description": + "Service accounts created with default project membership have role `member`. Accounts created with `create_service_account_only` have role `none`.\n" + }), + "created_at": Schema.Number.annotate({ "format": "unixtime" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "api_key": Schema.Union([ProjectServiceAccountApiKey, Schema.Null]) +}).annotate({ "identifier": "ProjectServiceAccountCreateResponse" }) +export type ProjectSpendAlert = { + readonly "id": string + readonly "object": "project.spend_alert" + readonly "threshold_amount": number + readonly "currency": "USD" + readonly "interval": "month" + readonly "notification_channel": SpendAlertNotificationChannel +} +export const ProjectSpendAlert = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("project.spend_alert").annotate({ + "description": "The object type, which is always `project.spend_alert`." + }), + "threshold_amount": Schema.Number.annotate({ "description": "The alert threshold amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "currency": Schema.Literal("USD").annotate({ "description": "The currency for the threshold amount." }), + "interval": Schema.Literal("month").annotate({ + "description": "The time interval for evaluating spend against the threshold." + }), + "notification_channel": SpendAlertNotificationChannel +}).annotate({ + "description": "Represents a spend alert configured at the project level.", + "identifier": "ProjectSpendAlert" +}) +export type CreateSpendAlertBody = { + readonly "threshold_amount": number + readonly "currency": "USD" + readonly "interval": "month" + readonly "notification_channel": SpendAlertNotificationChannel +} +export const CreateSpendAlertBody = Schema.Struct({ + "threshold_amount": Schema.Number.annotate({ "description": "The alert threshold amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })), + "currency": Schema.Literal("USD").annotate({ "description": "The currency for the threshold amount." }), + "interval": Schema.Literal("month").annotate({ + "description": "The time interval for evaluating spend against the threshold." + }), + "notification_channel": SpendAlertNotificationChannel +}).annotate({ + "description": "Parameters for creating or updating a spend alert.", + "identifier": "CreateSpendAlertBody" +}) +export type OrganizationSpendAlert = { + readonly "id": string + readonly "object": "organization.spend_alert" + readonly "threshold_amount": number + readonly "currency": "USD" + readonly "interval": "month" + readonly "notification_channel": SpendAlertNotificationChannel +} +export const OrganizationSpendAlert = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("organization.spend_alert").annotate({ + "description": "The object type, which is always `organization.spend_alert`." + }), + "threshold_amount": Schema.Number.annotate({ "description": "The alert threshold amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "currency": Schema.Literal("USD").annotate({ "description": "The currency for the threshold amount." }), + "interval": Schema.Literal("month").annotate({ + "description": "The time interval for evaluating spend against the threshold." + }), + "notification_channel": SpendAlertNotificationChannel +}).annotate({ + "description": "Represents a spend alert configured at the organization level.", + "identifier": "OrganizationSpendAlert" +}) +export type ProjectUserListResponse = { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ProjectUserListResponse = Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(ProjectUser), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ProjectUserListResponse" }) +export type UserListResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const UserListResponse = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(User), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "UserListResponse" }) +export type UserRoleAssignment = { readonly "object": "user.role"; readonly "user": User; readonly "role": Role } +export const UserRoleAssignment = Schema.Struct({ + "object": Schema.Literal("user.role").annotate({ "description": "Always `user.role`." }), + "user": User, + "role": Role +}).annotate({ "description": "Role assignment linking a user to a role.", "identifier": "UserRoleAssignment" }) +export type RealtimeTranscriptionSessionCreateRequest = { + readonly "turn_detection"?: { + readonly "type"?: "server_vad" + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + } + readonly "input_audio_noise_reduction"?: + | null + | { readonly "type"?: NoiseReductionType } & { readonly [x: string]: Schema.Json } + readonly "input_audio_format"?: "pcm16" | "g711_ulaw" | "g711_alaw" + readonly "input_audio_transcription"?: AudioTranscription + readonly "include"?: ReadonlyArray<"item.input_audio_transcription.logprobs"> +} +export const RealtimeTranscriptionSessionCreateRequest = Schema.Struct({ + "turn_detection": Schema.optionalKey( + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("server_vad").annotate({ + "description": + "Type of turn detection. Only `server_vad` is currently supported for transcription sessions.\n" + }) + ), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ + "description": + "Configuration for turn detection. Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\n" + }) + ), + "input_audio_noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.Null, + Schema.StructWithRest(Schema.Struct({ "type": Schema.optionalKey(NoiseReductionType) }), [ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + ]) + ]).annotate({ + "description": + "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + }) + ), + "input_audio_format": Schema.optionalKey( + Schema.Literals(["pcm16", "g711_ulaw", "g711_alaw"]).annotate({ + "description": + "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,\nsingle channel (mono), and little-endian byte order.\n" + }) + ), + "input_audio_transcription": Schema.optionalKey( + Schema.suspend((): Schema.Codec => AudioTranscription).annotate({ + "description": + "Configuration for input audio transcription. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + }) + ), + "include": Schema.optionalKey( + Schema.Array(Schema.Literal("item.input_audio_transcription.logprobs")).annotate({ + "description": + "The set of items to include in the transcription. Current available items are:\n`item.input_audio_transcription.logprobs`\n" + }) + ) +}).annotate({ + "title": "Realtime transcription session configuration", + "description": "Realtime transcription session object configuration.", + "identifier": "RealtimeTranscriptionSessionCreateRequest" +}) +export type RealtimeTranslationSessionCreateRequest = { + readonly "model": string + readonly "audio"?: { + readonly "input"?: { + readonly "transcription"?: { readonly "model": string } | null + readonly "noise_reduction"?: { readonly "type": NoiseReductionType } | null + } + readonly "output"?: { readonly "language"?: string } + } +} +export const RealtimeTranslationSessionCreateRequest = Schema.Struct({ + "model": Schema.String.annotate({ "description": "The Realtime translation model used for this session.\n" }), + "audio": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "transcription": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "model": Schema.String.annotate({ + "description": "The transcription model to use for source transcript deltas." + }) + }).annotate({ + "description": + "Optional source-language transcription. When configured, the server emits\n`session.input_transcript.delta` events. Translation itself still runs from\nthe input audio stream.\n" + }), + Schema.Null + ]) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ "type": NoiseReductionType }).annotate({ + "description": "Optional input noise reduction. Set to `null` to disable it.\n" + }), + Schema.Null + ]) + ) + })), + "output": Schema.optionalKey( + Schema.Struct({ + "language": Schema.optionalKey( + Schema.String.annotate({ + "description": "Target language for translated output audio and transcript deltas.\n" + }) + ) + }) + ) + }).annotate({ "description": "Configuration for translation input and output audio.\n" }) + ) +}).annotate({ + "title": "Realtime translation session configuration", + "description": + "Realtime translation session configuration. Translation sessions stream source\naudio in and translated audio plus transcript deltas out continuously.\n", + "identifier": "RealtimeTranslationSessionCreateRequest" +}) +export type RealtimeTranslationSession = { + readonly "id": string + readonly "type": "translation" + readonly "expires_at": number + readonly "model": string + readonly "audio": { + readonly "input"?: { + readonly "transcription"?: { readonly "model": string } | null + readonly "noise_reduction"?: { readonly "type": NoiseReductionType } | null + } + readonly "output"?: { readonly "language"?: string } + } +} +export const RealtimeTranslationSession = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "Unique identifier for the session that looks like `sess_1234567890abcdef`.\n" + }), + "type": Schema.Literal("translation").annotate({ + "description": "The session type. Always `translation` for Realtime translation sessions.\n" + }), + "expires_at": Schema.Number.annotate({ + "description": "Expiration timestamp for the session, in seconds since epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "model": Schema.String.annotate({ + "description": + "The Realtime translation model used for this session. This field is set at\nsession creation and cannot be changed with `session.update`.\n" + }), + "audio": Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "transcription": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "model": Schema.String.annotate({ + "description": "The transcription model used for source transcript deltas." + }) + }).annotate({ + "description": + "Optional source-language transcription. When configured, the server emits\n`session.input_transcript.delta` events. Translation itself still runs from\nthe input audio stream.\n" + }), + Schema.Null + ]) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ "type": NoiseReductionType }).annotate({ + "description": "Optional input noise reduction.\n" + }), + Schema.Null + ]) + ) + })), + "output": Schema.optionalKey( + Schema.Struct({ + "language": Schema.optionalKey( + Schema.String.annotate({ + "description": "Target language for translated output audio and transcript deltas.\n" + }) + ) + }) + ) + }).annotate({ "description": "Configuration for translation input and output audio.\n" }) +}).annotate({ + "title": "Realtime translation session", + "description": + "A Realtime translation session. Translation sessions continuously translate input\naudio into the configured output language.\n", + "identifier": "RealtimeTranslationSession" +}) +export type RealtimeTranscriptionSessionCreateRequestGA = { + readonly "type": "transcription" + readonly "audio"?: { + readonly "input"?: { + readonly "format"?: RealtimeAudioFormats + readonly "transcription"?: AudioTranscription + readonly "noise_reduction"?: + | null + | { readonly "type"?: NoiseReductionType } & { readonly [x: string]: Schema.Json } + readonly "turn_detection"?: RealtimeTurnDetection + } + } + readonly "include"?: ReadonlyArray<"item.input_audio_transcription.logprobs"> +} +export const RealtimeTranscriptionSessionCreateRequestGA = Schema.Struct({ + "type": Schema.Literal("transcription").annotate({ + "description": "The type of session to create. Always `transcription` for transcription sessions.\n" + }), + "audio": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey(RealtimeAudioFormats), + "transcription": Schema.optionalKey( + Schema.suspend((): Schema.Codec => AudioTranscription).annotate({ + "description": + "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + }) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.Null, + Schema.StructWithRest(Schema.Struct({ "type": Schema.optionalKey(NoiseReductionType) }), [ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + ]) + ]).annotate({ + "description": + "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + }) + ), + "turn_detection": Schema.optionalKey(RealtimeTurnDetection) + })) + }).annotate({ "description": "Configuration for input and output audio.\n" }) + ), + "include": Schema.optionalKey( + Schema.Array(Schema.Literal("item.input_audio_transcription.logprobs")).annotate({ + "description": + "Additional fields to include in server outputs.\n\n`item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.\n" + }) + ) +}).annotate({ + "title": "Realtime transcription session configuration", + "description": "Realtime transcription session object configuration.", + "identifier": "RealtimeTranscriptionSessionCreateRequestGA" +}) +export type RealtimeReasoning = { readonly "effort"?: RealtimeReasoningEffort } +export const RealtimeReasoning = Schema.Struct({ "effort": Schema.optionalKey(RealtimeReasoningEffort) }).annotate({ + "title": "Realtime reasoning configuration", + "description": "Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.\n", + "identifier": "RealtimeReasoning" +}) +export type RealtimeTranscriptionSessionCreateResponseGA = { + readonly "type": "transcription" + readonly "id": string + readonly "object": string + readonly "expires_at"?: number + readonly "include"?: ReadonlyArray<"item.input_audio_transcription.logprobs"> | null + readonly "audio"?: { + readonly "input"?: { + readonly "format"?: RealtimeAudioFormats + readonly "transcription"?: AudioTranscriptionResponse | null + readonly "noise_reduction"?: + | { readonly "type"?: NoiseReductionType } & { readonly [x: string]: Schema.Json } + | null + readonly "turn_detection"?: { + readonly "type"?: string + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + } | null + } + } +} +export const RealtimeTranscriptionSessionCreateResponseGA = Schema.Struct({ + "type": Schema.Literal("transcription").annotate({ + "description": "The type of session. Always `transcription` for transcription sessions.\n" + }), + "id": Schema.String.annotate({ + "description": "Unique identifier for the session that looks like `sess_1234567890abcdef`.\n" + }), + "object": Schema.String.annotate({ "description": "The object type. Always `realtime.transcription_session`." }), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Expiration timestamp for the session, in seconds since epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "include": Schema.optionalKey( + Schema.Union([Schema.Array(Schema.Literal("item.input_audio_transcription.logprobs")), Schema.Null]).annotate({ + "description": + "Additional fields to include in server outputs.\n- `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.\n" + }) + ), + "audio": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey(RealtimeAudioFormats), + "transcription": Schema.optionalKey( + Schema.Union([AudioTranscriptionResponse, Schema.Null]).annotate({ + "description": "Configuration of the transcription model.\n" + }) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest(Schema.Struct({ "type": Schema.optionalKey(NoiseReductionType) }), [ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + ]), + Schema.Null + ]).annotate({ "description": "Configuration for input audio noise reduction.\n" }) + ), + "turn_detection": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "type": Schema.optionalKey( + Schema.String.annotate({ + "description": "Type of turn detection, only `server_vad` is currently supported.\n" + }) + ), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ + "description": + "Configuration for turn detection. Can be set to `null` to turn off. Server\nVAD means that the model will detect the start and end of speech based on\naudio volume and respond at the end of user speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported.\n" + }), + Schema.Null + ]).annotate({ + "description": + "Configuration for turn detection. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported.\n" + }) + ) + })) + }).annotate({ "description": "Configuration for input audio for the session.\n" }) + ) +}).annotate({ + "title": "Realtime transcription session configuration object", + "description": "A Realtime transcription session configuration object.\n", + "identifier": "RealtimeTranscriptionSessionCreateResponseGA" +}) +export type RealtimeSessionCreateResponse = { + readonly "id"?: string + readonly "object"?: string + readonly "expires_at"?: number + readonly "include"?: ReadonlyArray<"item.input_audio_transcription.logprobs"> + readonly "model"?: string + readonly "output_modalities"?: + | null + | string + | number + | boolean + | { readonly [x: string]: Schema.Json } + | ReadonlyArray<"text" | "audio"> + readonly "instructions"?: string + readonly "audio"?: { + readonly "input"?: { + readonly "format"?: RealtimeAudioFormats + readonly "transcription"?: AudioTranscriptionResponse + readonly "noise_reduction"?: + | { readonly "type"?: NoiseReductionType } & { readonly [x: string]: Schema.Json } + | null + readonly "turn_detection"?: + | { + readonly "type"?: string + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + } & { readonly [x: string]: Schema.Json } + | null + } + readonly "output"?: { + readonly "format"?: RealtimeAudioFormats + readonly "voice"?: VoiceIdsShared + readonly "speed"?: number + } + } + readonly "tracing"?: "auto" | { + readonly "workflow_name"?: string + readonly "group_id"?: string + readonly "metadata"?: { readonly [x: string]: Schema.Json } + } + readonly "turn_detection"?: + | { + readonly "type"?: string + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + } & { readonly [x: string]: Schema.Json } + | null + readonly "tools"?: ReadonlyArray + readonly "tool_choice"?: string + readonly "max_output_tokens"?: number | "inf" +} +export const RealtimeSessionCreateResponse = Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": "Unique identifier for the session that looks like `sess_1234567890abcdef`.\n" + }) + ), + "object": Schema.optionalKey( + Schema.String.annotate({ "description": "The object type. Always `realtime.session`." }) + ), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Expiration timestamp for the session, in seconds since epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "include": Schema.optionalKey( + Schema.Array(Schema.Literal("item.input_audio_transcription.logprobs")).annotate({ + "description": + "Additional fields to include in server outputs.\n- `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.\n" + }) + ), + "model": Schema.optionalKey(Schema.String.annotate({ "description": "The Realtime model used for this session." })), + "output_modalities": Schema.optionalKey( + Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + Schema.Array(Schema.Literals(["text", "audio"])) + ]).annotate({ + "description": "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + }) + ), + "instructions": Schema.optionalKey(Schema.String.annotate({ + "description": + "The default system instructions (i.e. system message) prepended to model\ncalls. This field allows the client to guide the model on desired\nresponses. The model can be instructed on response content and format,\n(e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good\nresponses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion\ninto your voice\", \"laugh frequently\"). The instructions are not guaranteed\nto be followed by the model, but they provide guidance to the model on the\ndesired behavior.\n\nNote that the server sets default instructions which will be used if this\nfield is not set and are visible in the `session.created` event at the\nstart of the session.\n" + })), + "audio": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey(RealtimeAudioFormats), + "transcription": Schema.optionalKey( + Schema.suspend((): Schema.Codec => AudioTranscriptionResponse).annotate({ + "description": "Configuration for input audio transcription.\n" + }) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest(Schema.Struct({ "type": Schema.optionalKey(NoiseReductionType) }), [ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + ]), + Schema.Null + ]).annotate({ "description": "Configuration for input audio noise reduction.\n" }) + ), + "turn_detection": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "type": Schema.optionalKey( + Schema.String.annotate({ + "description": "Type of turn detection, only `server_vad` is currently supported.\n" + }) + ), + "threshold": Schema.optionalKey(Schema.Finite), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ "description": "Configuration for turn detection.\n" }) + ) + })), + "output": Schema.optionalKey( + Schema.Struct({ + "format": Schema.optionalKey(RealtimeAudioFormats), + "voice": Schema.optionalKey(VoiceIdsShared), + "speed": Schema.optionalKey(Schema.Finite) + }) + ) + }).annotate({ "description": "Configuration for input and output audio for the session.\n" }) + ), + "tracing": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto").annotate({ "description": "Default tracing mode for the session.\n" }), + Schema.Struct({ + "workflow_name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The name of the workflow to attach to this trace. This is used to\nname the trace in the traces dashboard.\n" + }) + ), + "group_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The group id to attach to this trace to enable filtering and\ngrouping in the traces dashboard.\n" + }) + ), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The arbitrary metadata to attach to this trace to enable\nfiltering in the traces dashboard.\n" + }) + ) + }).annotate({ "title": "Tracing Configuration", "description": "Granular configuration for tracing.\n" }) + ], { mode: "oneOf" }).annotate({ + "title": "Tracing Configuration", + "description": + "Configuration options for tracing. Set to null to disable tracing. Once\ntracing is enabled for a session, the configuration cannot be modified.\n\n`auto` will create a trace for the session with default values for the\nworkflow name, group id, and metadata.\n" + }) + ), + "turn_detection": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "type": Schema.optionalKey( + Schema.String.annotate({ + "description": "Type of turn detection, only `server_vad` is currently supported.\n" + }) + ), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": + "Configuration for turn detection. Can be set to `null` to turn off. Server\nVAD means that the model will detect the start and end of speech based on\naudio volume and respond at the end of user speech.\n" + }) + ), + "tools": Schema.optionalKey( + Schema.Array(RealtimeFunctionTool).annotate({ "description": "Tools (functions) available to the model." }) + ), + "tool_choice": Schema.optionalKey( + Schema.String.annotate({ + "description": "How the model chooses tools. Options are `auto`, `none`, `required`, or\nspecify a function.\n" + }) + ), + "max_output_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Literal("inf")], { + mode: "oneOf" + }).annotate({ + "description": + "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + }) + ) +}).annotate({ + "title": "Realtime session configuration object", + "description": "A Realtime session configuration object.\n", + "identifier": "RealtimeSessionCreateResponse" +}) +export type RealtimeTranscriptionSessionCreateResponse = { + readonly "client_secret": { readonly "value": string; readonly "expires_at": number } + readonly "modalities"?: + | null + | string + | number + | boolean + | { readonly [x: string]: Schema.Json } + | ReadonlyArray<"text" | "audio"> + readonly "input_audio_format"?: string + readonly "input_audio_transcription"?: AudioTranscriptionResponse + readonly "turn_detection"?: { + readonly "type"?: string + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + } +} +export const RealtimeTranscriptionSessionCreateResponse = Schema.Struct({ + "client_secret": Schema.Struct({ + "value": Schema.String.annotate({ + "description": + "Ephemeral key usable in client environments to authenticate connections\nto the Realtime API. Use this in client-side environments rather than\na standard API token, which should only be used server-side.\n" + }), + "expires_at": Schema.Number.annotate({ + "description": "Timestamp for when the token expires. Currently, all tokens expire\nafter one minute.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ + "description": + "Ephemeral key returned by the API. Only present when the session is\ncreated on the server via REST API.\n" + }), + "modalities": Schema.optionalKey( + Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })), + Schema.Array(Schema.Literals(["text", "audio"])) + ]).annotate({ + "description": "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + }) + ), + "input_audio_format": Schema.optionalKey( + Schema.String.annotate({ + "description": "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + }) + ), + "input_audio_transcription": Schema.optionalKey( + Schema.suspend((): Schema.Codec => AudioTranscriptionResponse).annotate({ + "description": "Configuration of the transcription model.\n" + }) + ), + "turn_detection": Schema.optionalKey( + Schema.Struct({ + "type": Schema.optionalKey( + Schema.String.annotate({ "description": "Type of turn detection, only `server_vad` is currently supported.\n" }) + ), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ + "description": + "Configuration for turn detection. Can be set to `null` to turn off. Server\nVAD means that the model will detect the start and end of speech based on\naudio volume and respond at the end of user speech.\n" + }) + ) +}).annotate({ + "description": + "A new Realtime transcription session configuration.\n\nWhen a session is created on the server via REST API, the session object\nalso contains an ephemeral key. Default TTL for keys is 10 minutes. This\nproperty is not present when a session is updated via the WebSocket API.\n", + "identifier": "RealtimeTranscriptionSessionCreateResponse" +}) +export type ToolChoiceParam = + | ToolChoiceOptions + | ToolChoiceAllowed + | ToolChoiceTypes + | ToolChoiceFunction + | ToolChoiceMCP + | ToolChoiceCustom + | SpecificProgrammaticToolCallingParam + | SpecificApplyPatchParam + | SpecificFunctionShellParam +export const ToolChoiceParam = Schema.Union([ + ToolChoiceOptions, + ToolChoiceAllowed, + ToolChoiceTypes, + ToolChoiceFunction, + ToolChoiceMCP, + ToolChoiceCustom, + SpecificProgrammaticToolCallingParam, + SpecificApplyPatchParam, + SpecificFunctionShellParam +], { mode: "oneOf" }).annotate({ + "description": + "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n", + "identifier": "ToolChoiceParam" +}) +export type AccessProgramsParam = { readonly "cyber"?: CyberAccessProgramEnum } +export const AccessProgramsParam = Schema.Struct({ + "cyber": Schema.optionalKey( + Schema.suspend((): Schema.Codec => CyberAccessProgramEnum).annotate({ + "description": + "The Cyber access program to use for this request. Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If omitted, the API resolves the program from the model's Cyber tier and your organization and project access, subject to model-specific eligibility restrictions. By default, models without a Cyber tier use Standard. Blue-tier models use Daybreak Blue when authorized; otherwise they fall back to Standard unless the model requires Daybreak access. Red-tier models use Daybreak Red and require authorization. Requests that require unavailable Daybreak access return 403. An implicit Standard fallback is represented by null in the response's access_programs field, rather than an explicit Standard selection." + }) + ) +}).annotate({ + "description": "Domain-specific access programs to use for this request.", + "identifier": "AccessProgramsParam" +}) +export type AccessProgramsBody = { readonly "cyber": CyberAccessProgramEnum } +export const AccessProgramsBody = Schema.Struct({ + "cyber": Schema.suspend((): Schema.Codec => CyberAccessProgramEnum).annotate({ + "description": "The effective Cyber access program used for this response." + }) +}).annotate({ "identifier": "AccessProgramsBody" }) +export type Reasoning = { + readonly "mode"?: ReasoningModeEnum + readonly "effort"?: ReasoningEffort + readonly "summary"?: "auto" | "concise" | "detailed" | null + readonly "context"?: "auto" | "current_turn" | "all_turns" | null + readonly "generate_summary"?: "auto" | "concise" | "detailed" | null +} +export const Reasoning = Schema.Struct({ + "mode": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ReasoningModeEnum).annotate({ + "description": + "Controls the reasoning execution mode for the request.\n\nWhen returned on a response, this is the effective execution mode.\n" + }) + ), + "effort": Schema.optionalKey(ReasoningEffort), + "summary": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "concise", "detailed"]).annotate({ + "description": + "A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n\n`concise` is supported for `computer-use-preview` models and all reasoning models after `gpt-5`.\n" + }), + Schema.Null + ])), + "context": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "current_turn", "all_turns"]).annotate({ + "description": + "Controls which reasoning items are rendered back to the model on later turns.\nIf omitted or set to `auto`, the model determines the context mode. The\n`gpt-5.6` model family defaults to `all_turns`; earlier models default to\n`current_turn`.\n\nWhen returned on a response, this is the effective reasoning context mode\nused for the response.\n" + }), + Schema.Null + ])), + "generate_summary": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["auto", "concise", "detailed"]).annotate({ + "description": + "**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Reasoning", + "description": + "Configuration options for\n[reasoning models](https://developers.openai.com/api/docs/guides/reasoning).\n", + "identifier": "Reasoning" +}) +export type ConversationParam = string | ConversationParam_2 +export const ConversationParam = Schema.Union([ + Schema.String.annotate({ "title": "Conversation ID", "description": "The unique ID of the conversation.\n" }), + ConversationParam_2 +], { mode: "oneOf" }).annotate({ + "description": + "The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request.\nInput items and output items from this response are automatically added to this conversation after this response completes.\n", + "identifier": "ConversationParam" +}) +export type PromptCacheMissDiagnosticsBody = { + readonly "type": "cache_miss" + readonly "reason": CacheMissReasonTypeEnum + readonly "cache_missed_tokens": number + readonly "comparison_reusable_tokens"?: number +} +export const PromptCacheMissDiagnosticsBody = Schema.Struct({ + "type": Schema.Literal("cache_miss"), + "reason": Schema.suspend((): Schema.Codec => CacheMissReasonTypeEnum).annotate({ + "description": "The reason prompt cache reuse did not occur." + }), + "cache_missed_tokens": Schema.Number.annotate({ + "description": "The estimated number of input tokens affected after the first detected divergence." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "comparison_reusable_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The raw token count of the reusable prefix in the compared response." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) + ) +}).annotate({ "identifier": "PromptCacheMissDiagnosticsBody" }) +export type ResponseShellCallOutputContentDeltaStreamingEvent = { + readonly "type": "response.shell_call_output_content.delta" + readonly "sequence_number": number + readonly "item_id": string + readonly "output_index": number + readonly "command_index": number + readonly "delta": ShellCallOutputDelta +} +export const ResponseShellCallOutputContentDeltaStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_output_content.delta").annotate({ + "description": "The type of the event, always `response.shell_call_output_content.delta`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The ID of the output item that was updated." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that produced output." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.suspend((): Schema.Codec => ShellCallOutputDelta).annotate({ + "description": "The stdout/stderr delta that was emitted." + }) +}).annotate({ + "title": "Response shell call output content delta event", + "description": "A streaming event that indicated shell call output was incrementally added.", + "identifier": "ResponseShellCallOutputContentDeltaStreamingEvent" +}) +export type ResponseTextDeltaEvent = { + readonly "type": "response.output_text.delta" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string + readonly "sequence_number": number + readonly "logprobs": ReadonlyArray +} +export const ResponseTextDeltaEvent = Schema.Struct({ + "type": Schema.Literal("response.output_text.delta").annotate({ + "description": "The type of the event. Always `response.output_text.delta`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the output item that the text delta was added to.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the text delta was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the text delta was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The text delta that was added.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "logprobs": Schema.Array(ResponseLogProb).annotate({ + "description": "The log probabilities of the tokens in the delta.\n" + }) +}).annotate({ + "description": "Emitted when there is an additional text delta.", + "identifier": "ResponseTextDeltaEvent" +}) +export type ResponseTextDoneEvent = { + readonly "type": "response.output_text.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "text": string + readonly "sequence_number": number + readonly "logprobs": ReadonlyArray +} +export const ResponseTextDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.output_text.done").annotate({ + "description": "The type of the event. Always `response.output_text.done`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the text content is finalized.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the text content is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the text content is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "text": Schema.String.annotate({ "description": "The text content that is finalized.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "logprobs": Schema.Array(ResponseLogProb).annotate({ + "description": "The log probabilities of the tokens in the delta.\n" + }) +}).annotate({ "description": "Emitted when text content is finalized.", "identifier": "ResponseTextDoneEvent" }) +export type CreateMessageRequest = { + readonly "role": "user" | "assistant" + readonly "content": + | string + | ReadonlyArray + readonly "attachments"?: + | ReadonlyArray< + { + readonly "file_id"?: string + readonly "tools"?: ReadonlyArray + } + > + | null + readonly "metadata"?: Metadata +} +export const CreateMessageRequest = Schema.Struct({ + "role": Schema.Literals(["user", "assistant"]).annotate({ + "description": + "The role of the entity that is creating the message. Allowed values include:\n- `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.\n- `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.\n" + }), + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text content", "description": "The text contents of the message." }), + Schema.Array( + Schema.Union([MessageContentImageFileObject, MessageContentImageUrlObject, MessageRequestContentTextObject], { + mode: "oneOf" + }) + ).annotate({ + "title": "Array of content parts", + "description": + "An array of content parts with a defined type, each can be of type `text` or images can be passed with `image_url` or `image_file`. Image types are only supported on [Vision-compatible models](https://developers.openai.com/api/docs/models)." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }), + "attachments": Schema.optionalKey(Schema.Union([ + Schema.Array(Schema.Struct({ + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the file to attach to the message." }) + ), + "tools": Schema.optionalKey( + Schema.Array(Schema.Union([AssistantToolsCode, AssistantToolsFileSearchTypeOnly], { mode: "oneOf" })).annotate({ + "description": "The tools to add this file to." + }) + ) + })).annotate({ "description": "A list of files attached to the message, and the tools they should be added to." }), + Schema.Null + ])), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ "identifier": "CreateMessageRequest" }) +export type AssistantsApiToolChoiceOption = "none" | "auto" | "required" | AssistantsNamedToolChoice +export const AssistantsApiToolChoiceOption = Schema.Union([ + Schema.Literals(["none", "auto", "required"]).annotate({ + "description": + "`none` means the model will not call any tools and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools before responding to the user.\n" + }), + AssistantsNamedToolChoice +], { mode: "oneOf" }).annotate({ + "description": + "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tools and instead generates a message.\n`auto` is the default value and means the model can pick between generating a message or calling one or more tools.\n`required` means the model must call one or more tools before responding to the user.\nSpecifying a particular tool like `{\"type\": \"file_search\"}` or `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n", + "identifier": "AssistantsApiToolChoiceOption" +}) +export type RunStepDetailsToolCallsCodeObject = { + readonly "id": string + readonly "type": "code_interpreter" + readonly "code_interpreter": { + readonly "input": string + readonly "outputs": ReadonlyArray< + RunStepDetailsToolCallsCodeOutputLogsObject | RunStepDetailsToolCallsCodeOutputImageObject + > + } +} +export const RunStepDetailsToolCallsCodeObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the tool call." }), + "type": Schema.Literal("code_interpreter").annotate({ + "description": "The type of tool call. This is always going to be `code_interpreter` for this type of tool call." + }), + "code_interpreter": Schema.Struct({ + "input": Schema.String.annotate({ "description": "The input to the Code Interpreter tool call." }), + "outputs": Schema.Array( + Schema.Union([RunStepDetailsToolCallsCodeOutputLogsObject, RunStepDetailsToolCallsCodeOutputImageObject], { + mode: "oneOf" + }) + ).annotate({ + "description": + "The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type." + }) + }).annotate({ "description": "The Code Interpreter tool call definition." }) +}).annotate({ + "title": "Code Interpreter tool call", + "description": "Details of the Code Interpreter tool call the run step was involved in.", + "identifier": "RunStepDetailsToolCallsCodeObject" +}) +export type RunStepDeltaStepDetailsToolCallsCodeObject = { + readonly "index": number + readonly "id"?: string + readonly "type": "code_interpreter" + readonly "code_interpreter"?: { + readonly "input"?: string + readonly "outputs"?: ReadonlyArray< + RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject | RunStepDeltaStepDetailsToolCallsCodeOutputImageObject + > + } +} +export const RunStepDeltaStepDetailsToolCallsCodeObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the tool call in the tool calls array." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the tool call." })), + "type": Schema.Literal("code_interpreter").annotate({ + "description": "The type of tool call. This is always going to be `code_interpreter` for this type of tool call." + }), + "code_interpreter": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey( + Schema.String.annotate({ "description": "The input to the Code Interpreter tool call." }) + ), + "outputs": Schema.optionalKey( + Schema.Array( + Schema.Union([ + RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject, + RunStepDeltaStepDetailsToolCallsCodeOutputImageObject + ], { mode: "oneOf" }) + ).annotate({ + "description": + "The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type." + }) + ) + }).annotate({ "description": "The Code Interpreter tool call definition." }) + ) +}).annotate({ + "title": "Code interpreter tool call", + "description": "Details of the Code Interpreter tool call the run step was involved in.", + "identifier": "RunStepDeltaStepDetailsToolCallsCodeObject" +}) +export type MessageContentTextObject = { + readonly "type": "text" + readonly "text": { + readonly "value": string + readonly "annotations": ReadonlyArray< + MessageContentTextAnnotationsFileCitationObject | MessageContentTextAnnotationsFilePathObject + > + } +} +export const MessageContentTextObject = Schema.Struct({ + "type": Schema.Literal("text").annotate({ "description": "Always `text`." }), + "text": Schema.Struct({ + "value": Schema.String.annotate({ "description": "The data that makes up the text." }), + "annotations": Schema.Array( + Schema.Union([MessageContentTextAnnotationsFileCitationObject, MessageContentTextAnnotationsFilePathObject], { + mode: "oneOf" + }) + ) + }) +}).annotate({ + "title": "Text", + "description": "The text content that is part of a message.", + "identifier": "MessageContentTextObject" +}) +export type MessageDeltaContentTextObject = { + readonly "index": number + readonly "type": "text" + readonly "text"?: { + readonly "value"?: string + readonly "annotations"?: ReadonlyArray< + MessageDeltaContentTextAnnotationsFileCitationObject | MessageDeltaContentTextAnnotationsFilePathObject + > + } +} +export const MessageDeltaContentTextObject = Schema.Struct({ + "index": Schema.Number.annotate({ "description": "The index of the content part in the message." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "type": Schema.Literal("text").annotate({ "description": "Always `text`." }), + "text": Schema.optionalKey(Schema.Struct({ + "value": Schema.optionalKey(Schema.String.annotate({ "description": "The data that makes up the text." })), + "annotations": Schema.optionalKey( + Schema.Array( + Schema.Union([ + MessageDeltaContentTextAnnotationsFileCitationObject, + MessageDeltaContentTextAnnotationsFilePathObject + ], { mode: "oneOf" }) + ) + ) + })) +}).annotate({ + "title": "Text", + "description": "The text content that is part of a message.", + "identifier": "MessageDeltaContentTextObject" +}) +export type VectorStoreObject = { + readonly "id": string + readonly "object": "vector_store" + readonly "created_at": number + readonly "name": string + readonly "usage_bytes": number + readonly "file_counts": { + readonly "in_progress": number + readonly "completed": number + readonly "failed": number + readonly "cancelled": number + readonly "total": number + } + readonly "status": "expired" | "in_progress" | "completed" + readonly "expires_after"?: VectorStoreExpirationAfter + readonly "expires_at"?: number | null + readonly "last_active_at": number | null + readonly "metadata": Metadata +} +export const VectorStoreObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("vector_store").annotate({ + "description": "The object type, which is always `vector_store`." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the vector store was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "name": Schema.String.annotate({ "description": "The name of the vector store." }), + "usage_bytes": Schema.Number.annotate({ + "description": "The total number of bytes used by the files in the vector store." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "file_counts": Schema.Struct({ + "in_progress": Schema.Number.annotate({ "description": "The number of files that are currently being processed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "completed": Schema.Number.annotate({ "description": "The number of files that have been successfully processed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "failed": Schema.Number.annotate({ "description": "The number of files that have failed to process." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "cancelled": Schema.Number.annotate({ "description": "The number of files that were cancelled." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "total": Schema.Number.annotate({ "description": "The total number of files." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }), + "status": Schema.Literals(["expired", "in_progress", "completed"]).annotate({ + "description": + "The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use." + }), + "expires_after": Schema.optionalKey(VectorStoreExpirationAfter), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the vector store will expire.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "last_active_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the vector store was last active.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "metadata": Metadata +}).annotate({ + "title": "Vector store", + "description": "A vector store is a collection of processed files can be used by the `file_search` tool.", + "identifier": "VectorStoreObject" +}) +export type UpdateVectorStoreRequest = { + readonly "name"?: string | null + readonly "expires_after"?: VectorStoreExpirationAfter | null + readonly "metadata"?: Metadata +} +export const UpdateVectorStoreRequest = Schema.Struct({ + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the vector store." }) + ), + "expires_after": Schema.optionalKey(Schema.Union([VectorStoreExpirationAfter, Schema.Null])), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ "identifier": "UpdateVectorStoreRequest" }) +export type StaticChunkingStrategyRequestParam = { + readonly "type": "static" + readonly "static": StaticChunkingStrategy +} +export const StaticChunkingStrategyRequestParam = Schema.Struct({ + "type": Schema.Literal("static").annotate({ "description": "Always `static`." }), + "static": StaticChunkingStrategy +}).annotate({ + "title": "Static Chunking Strategy", + "description": "Customize your own chunking strategy by setting chunk size and chunk overlap.", + "identifier": "StaticChunkingStrategyRequestParam" +}) +export type StaticChunkingStrategyResponseParam = { + readonly "type": "static" + readonly "static": StaticChunkingStrategy +} +export const StaticChunkingStrategyResponseParam = Schema.Struct({ + "type": Schema.Literal("static").annotate({ "description": "Always `static`." }), + "static": StaticChunkingStrategy +}).annotate({ "title": "Static Chunking Strategy", "identifier": "StaticChunkingStrategyResponseParam" }) +export type VectorStoreSearchResultItem = { + readonly "file_id": string + readonly "filename": string + readonly "score": number + readonly "attributes": VectorStoreFileAttributes + readonly "content": ReadonlyArray +} +export const VectorStoreSearchResultItem = Schema.Struct({ + "file_id": Schema.String.annotate({ "description": "The ID of the vector store file." }), + "filename": Schema.String.annotate({ "description": "The name of the vector store file." }), + "score": Schema.Number.annotate({ "description": "The similarity score for the result." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })).check( + Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" }) + ), + "attributes": VectorStoreFileAttributes, + "content": Schema.Array(VectorStoreSearchResultContentObject).annotate({ + "description": "Content chunks from the file." + }) +}).annotate({ "identifier": "VectorStoreSearchResultItem" }) +export type SafetyCaseNotice = { readonly "type": SafetyCaseNoticeType } +export const SafetyCaseNotice = Schema.Struct({ "type": SafetyCaseNoticeType }).annotate({ + "identifier": "SafetyCaseNotice" +}) +export type SafetyAlertResource = { + readonly "id": string + readonly "object": "safety.alert" + readonly "created_at": number + readonly "request_id": string + readonly "response_id": string + readonly "model": string + readonly "request_paused": boolean + readonly "error_type": SafetyAlertErrorType + readonly "reason": string | null + readonly "detailed_explanation"?: string | null +} +export const SafetyAlertResource = Schema.Struct({ + "id": Schema.String, + "object": Schema.Literal("safety.alert"), + "created_at": Schema.Number.annotate({ "format": "unixtime" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "request_id": Schema.String, + "response_id": Schema.String, + "model": Schema.String, + "request_paused": Schema.Boolean.annotate({ + "description": + "Whether block registration succeeded for this request. This does not confirm that response execution stopped." + }), + "error_type": SafetyAlertErrorType, + "reason": Schema.Union([ + Schema.String.annotate({ + "description": "A customer-safe description derived from error_type, or null for zero data retention requests." + }), + Schema.Null + ]), + "detailed_explanation": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "A generated explanation, temporarily available for eligible zero data retention alerts. Omitted when unavailable." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "SafetyAlertResource" }) +export type DecisionsUsageResult = { + readonly "api_source"?: DecisionsUsageApiSource | null + readonly "object": "organization.usage.decisions.result" + readonly "input_tokens": number + readonly "input_cached_tokens"?: number | null + readonly "input_cache_write_tokens"?: number | null + readonly "input_cache_write_12h_tokens"?: number | null + readonly "input_uncached_tokens"?: number | null + readonly "output_tokens": number + readonly "input_text_tokens"?: number | null + readonly "output_text_tokens"?: number | null + readonly "input_cached_text_tokens"?: number | null + readonly "input_audio_tokens"?: number | null + readonly "input_cached_audio_tokens"?: number | null + readonly "output_audio_tokens"?: number | null + readonly "input_image_tokens"?: number | null + readonly "input_cached_image_tokens"?: number | null + readonly "output_image_tokens"?: number | null + readonly "num_model_requests": number + readonly "project_id"?: string | null + readonly "user_id"?: string | null + readonly "api_key_id"?: string | null + readonly "model"?: string | null + readonly "batch"?: boolean | null + readonly "service_tier"?: string | null +} +export const DecisionsUsageResult = Schema.Struct({ + "api_source": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => DecisionsUsageApiSource).annotate({ + "description": + "When grouped by `api_source`, `agents_api` identifies attributed Agents API activity and `unlabeled` includes all records without published source attribution, including historical and unknown origins. Unlabeled does not imply direct API usage. Without source grouping, this field is null." + }), + Schema.Null + ]) + ), + "object": Schema.Literal("organization.usage.decisions.result"), + "input_tokens": Schema.Number.annotate({ + "description": + "The aggregated number of input tokens used, including cached and cache-write tokens. This includes text, audio, and image tokens. For customers subscribed to Scale Tier, this includes Scale Tier tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "input_cached_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The aggregated number of cached input tokens used across text, audio, and image inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "input_cache_write_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The aggregated number of input tokens written to the cache with a 30-minute retention period." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "input_cache_write_12h_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The aggregated number of input tokens written to the cache with a 12-hour retention period." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "input_uncached_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The aggregated number of uncached input tokens used across text, audio, and image inputs, excluding cache-write tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "output_tokens": Schema.Number.annotate({ + "description": + "The aggregated number of output tokens used across text, audio, and image outputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "input_text_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The aggregated number of uncached text input tokens used, excluding cache-write tokens." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "output_text_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of text output tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "input_cached_text_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of cached text input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "input_audio_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of uncached audio input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "input_cached_audio_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of cached audio input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "output_audio_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of audio output tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "input_image_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of uncached image input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "input_cached_image_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of cached image input tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "output_image_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "The aggregated number of image output tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + Schema.Null + ]) + ), + "num_model_requests": Schema.Number.annotate({ "description": "The count of requests made to the model." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "project_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=project_id`, this field provides the project ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "user_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=user_id`, this field provides the user ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "api_key_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result." + }), + Schema.Null + ]) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=model`, this field provides the model name of the grouped usage result." + }), + Schema.Null + ]) + ), + "batch": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": "When `group_by=batch`, this field tells whether the grouped usage result is batch or not." + }), + Schema.Null + ]) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "When `group_by=service_tier`, this field provides the service tier of the grouped usage result." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The aggregated Decisions API usage details of the specific time bucket.", + "identifier": "DecisionsUsageResult" +}) +export type DecisionsUsageErrorResponse = { readonly "error": DecisionsUsageErrorDetails } +export const DecisionsUsageErrorResponse = Schema.Struct({ "error": DecisionsUsageErrorDetails }).annotate({ + "identifier": "DecisionsUsageErrorResponse" +}) +export type DecisionsUsagePermissionErrorResponse = { readonly "error": DecisionsUsageErrorDetails | string } +export const DecisionsUsagePermissionErrorResponse = Schema.Struct({ + "error": Schema.Union([ + DecisionsUsageErrorDetails, + Schema.String.annotate({ "description": "An explanation of the missing API permissions." }) + ], { mode: "oneOf" }).annotate({ "description": "Details about the permission error." }) +}).annotate({ "identifier": "DecisionsUsagePermissionErrorResponse" }) +export type ExternalStorageResponse = { + readonly "object": "organization.external_storage" + readonly "id": string + readonly "project_id": string + readonly "provider": + | AwsExternalStorageProviderResponse + | AzureExternalStorageProviderResponse + | GcpExternalStorageProviderResponse + | OciExternalStorageProviderResponse + readonly "geography": string + readonly "status": ExternalStorageStatus + readonly "created_at": number +} +export const ExternalStorageResponse = Schema.Struct({ + "object": Schema.Literal("organization.external_storage"), + "id": Schema.String, + "project_id": Schema.String, + "provider": Schema.Union([ + AwsExternalStorageProviderResponse, + AzureExternalStorageProviderResponse, + GcpExternalStorageProviderResponse, + OciExternalStorageProviderResponse + ], { mode: "oneOf" }), + "geography": Schema.String, + "status": ExternalStorageStatus, + "created_at": Schema.Number.annotate({ "format": "unixtime" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "identifier": "ExternalStorageResponse" }) +export type CreateExternalStorageBody = { + readonly "project_id": string + readonly "provider": + | AwsExternalStorageProviderParams + | AzureExternalStorageProviderParams + | GcpExternalStorageProviderParams + | OciExternalStorageProviderParams +} +export const CreateExternalStorageBody = Schema.Struct({ + "project_id": Schema.String, + "provider": Schema.Union([ + AwsExternalStorageProviderParams, + AzureExternalStorageProviderParams, + GcpExternalStorageProviderParams, + OciExternalStorageProviderParams + ], { mode: "oneOf" }) +}).annotate({ "identifier": "CreateExternalStorageBody" }) +export type SpendLimitEnforcement = { readonly "status": SpendLimitEnforcementStatus } +export const SpendLimitEnforcement = Schema.Struct({ + "status": Schema.suspend((): Schema.Codec => SpendLimitEnforcementStatus).annotate({ + "description": "Whether the hard spend limit is currently enforcing." + }) +}).annotate({ + "description": "The current enforcement state of a hard spend limit.", + "identifier": "SpendLimitEnforcement" +}) +export type SynthIDProvenanceResult = { + readonly "type": "synthid" + readonly "outcome": ProvenanceDetectionResultApi + readonly "model": string | null + readonly "generated_at": string | null +} +export const SynthIDProvenanceResult = Schema.Struct({ + "type": Schema.Literal("synthid").annotate({ "description": "The provenance signal type. Always `synthid`." }), + "outcome": Schema.suspend((): Schema.Codec => ProvenanceDetectionResultApi).annotate({ + "description": + "Whether a supported OpenAI SynthID watermark was detected.\nIf `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect." + }), + "model": Schema.Union([ + Schema.String.annotate({ "description": "The OpenAI model recorded by the provenance signal, when available." }), + Schema.Null + ]), + "generated_at": Schema.Union([ + Schema.String.annotate({ + "description": + "The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset was generated, when available." + }), + Schema.Null + ]) +}).annotate({ "identifier": "SynthIDProvenanceResult" }) +export type C2PAProvenanceResult = { + readonly "type": "c2pa" + readonly "outcome": ProvenanceDetectionResultApi + readonly "validation_state": C2PAValidationStateApi + readonly "issuer": string | null + readonly "model": string | null + readonly "generated_at": string | null +} +export const C2PAProvenanceResult = Schema.Struct({ + "type": Schema.Literal("c2pa").annotate({ "description": "The provenance signal type. Always `c2pa`." }), + "outcome": Schema.suspend((): Schema.Codec => ProvenanceDetectionResultApi).annotate({ + "description": + "Whether a supported OpenAI C2PA provenance signal was detected.\nIf `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect." + }), + "validation_state": Schema.suspend((): Schema.Codec => C2PAValidationStateApi).annotate({ + "description": "The validation status of the C2PA manifest in the uploaded image." + }), + "issuer": Schema.Union([ + Schema.String.annotate({ "description": "The C2PA manifest issuer, when available." }), + Schema.Null + ]), + "model": Schema.Union([ + Schema.String.annotate({ "description": "The OpenAI model recorded by the provenance signal, when available." }), + Schema.Null + ]), + "generated_at": Schema.Union([ + Schema.String.annotate({ + "description": + "The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset was generated, when available." + }), + Schema.Null + ]) +}).annotate({ "identifier": "C2PAProvenanceResult" }) +export type CreateVideoJsonBody = { + readonly "model"?: VideoModel + readonly "prompt": string + readonly "input_reference"?: ImageRefParam_2 + readonly "seconds"?: VideoSeconds + readonly "size"?: VideoSize +} +export const CreateVideoJsonBody = Schema.Struct({ + "model": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VideoModel).annotate({ + "description": "The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`." + }) + ), + "prompt": Schema.String.annotate({ "description": "Text prompt that describes the video to generate." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" })), + "input_reference": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ImageRefParam_2).annotate({ + "description": + "Optional reference object that guides generation. Provide exactly one of `image_url` or `file_id`." + }) + ), + "seconds": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VideoSeconds).annotate({ + "description": "Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds." + }) + ), + "size": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VideoSize).annotate({ + "description": + "Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280." + }) + ) +}).annotate({ + "title": "Create video JSON request", + "description": "JSON parameters for creating a new video generation job.", + "identifier": "CreateVideoJsonBody" +}) +export type CreateVideoMultipartBodyMultipart = { + readonly "model"?: VideoModel + readonly "prompt": string + readonly "input_reference"?: __ClientMultipartFile | ImageRefParam_2 + readonly "seconds"?: VideoSeconds + readonly "size"?: VideoSize +} +export const CreateVideoMultipartBodyMultipart = Schema.Struct({ + "model": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VideoModel).annotate({ + "description": "The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`." + }) + ), + "prompt": Schema.String.annotate({ "description": "Text prompt that describes the video to generate." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" })), + "input_reference": Schema.optionalKey( + Schema.Union([ + __ClientMultipartFile, + Schema.suspend((): Schema.Codec => ImageRefParam_2).annotate({ + "description": + "Optional reference asset upload or reference object that guides generation. Provide exactly one of `image_url` or `file_id` when using an object." + }) + ], { mode: "oneOf" }) + ), + "seconds": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VideoSeconds).annotate({ + "description": "Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds." + }) + ), + "size": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VideoSize).annotate({ + "description": + "Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280." + }) + ) +}).annotate({ + "title": "Create video multipart request", + "description": "Multipart parameters for creating a new video generation job.", + "identifier": "CreateVideoMultipartBodyMultipart" +}) +export type CreateVideoEditJsonBody = { readonly "video": VideoReferenceInputParam; readonly "prompt": string } +export const CreateVideoEditJsonBody = Schema.Struct({ + "video": Schema.suspend((): Schema.Codec => VideoReferenceInputParam).annotate({ + "description": "Reference to the completed video to edit." + }), + "prompt": Schema.String.annotate({ "description": "Text prompt that describes how to edit the source video." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" })) +}).annotate({ + "title": "Create video edit JSON request", + "description": "JSON parameters for editing an existing generated video.", + "identifier": "CreateVideoEditJsonBody" +}) +export type CreateVideoEditMultipartBodyMultipart = { + readonly "video": __ClientMultipartFile | VideoReferenceInputParam + readonly "prompt": string +} +export const CreateVideoEditMultipartBodyMultipart = Schema.Struct({ + "video": Schema.Union([ + __ClientMultipartFile, + Schema.suspend((): Schema.Codec => VideoReferenceInputParam).annotate({ + "description": "Reference to the completed video to edit." + }) + ], { mode: "oneOf" }), + "prompt": Schema.String.annotate({ "description": "Text prompt that describes how to edit the source video." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" })) +}).annotate({ + "title": "Create video edit multipart request", + "description": "Parameters for editing an existing generated video.", + "identifier": "CreateVideoEditMultipartBodyMultipart" +}) +export type CreateVideoExtendJsonBody = { + readonly "video": VideoReferenceInputParam + readonly "prompt": string + readonly "seconds": VideoSeconds +} +export const CreateVideoExtendJsonBody = Schema.Struct({ + "video": Schema.suspend((): Schema.Codec => VideoReferenceInputParam).annotate({ + "description": "Reference to the completed video to extend." + }), + "prompt": Schema.String.annotate({ "description": "Updated text prompt that directs the extension generation." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" }) + ), + "seconds": Schema.suspend((): Schema.Codec => VideoSeconds).annotate({ + "description": "Length of the newly generated extension segment in seconds (allowed values: 4, 8, 12, 16, 20)." + }) +}).annotate({ + "title": "Create video extension JSON request", + "description": "JSON parameters for extending an existing generated video.", + "identifier": "CreateVideoExtendJsonBody" +}) +export type CreateVideoExtendMultipartBodyMultipart = { + readonly "video": VideoReferenceInputParam | __ClientMultipartFile + readonly "prompt": string + readonly "seconds": VideoSeconds +} +export const CreateVideoExtendMultipartBodyMultipart = Schema.Struct({ + "video": Schema.Union([ + Schema.suspend((): Schema.Codec => VideoReferenceInputParam).annotate({ + "description": "Reference to the completed video to extend." + }), + __ClientMultipartFile + ], { mode: "oneOf" }), + "prompt": Schema.String.annotate({ "description": "Updated text prompt that directs the extension generation." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(32000).annotate({ "expected": "a string with at most 32000 code points" }) + ), + "seconds": Schema.suspend((): Schema.Codec => VideoSeconds).annotate({ + "description": "Length of the newly generated extension segment in seconds (allowed values: 4, 8, 12, 16, 20)." + }) +}).annotate({ + "title": "Create video extension multipart request", + "description": "Multipart parameters for extending an existing generated video.", + "identifier": "CreateVideoExtendMultipartBodyMultipart" +}) +export type SkillListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SkillListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(SkillResource).annotate({ "description": "A list of items" }), + "first_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the first item in the list." }), + Schema.Null + ]), + "last_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the last item in the list." }), + Schema.Null + ]), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }) +}).annotate({ "identifier": "SkillListResource" }) +export type SkillVersionListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SkillVersionListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(SkillVersionResource).annotate({ "description": "A list of items" }), + "first_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the first item in the list." }), + Schema.Null + ]), + "last_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the last item in the list." }), + Schema.Null + ]), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }) +}).annotate({ "identifier": "SkillVersionListResource" }) +export type ChatkitWorkflow = { + readonly "id": string + readonly "version": string | null + readonly "state_variables": { readonly [x: string]: string | number | boolean | number | null } | null + readonly "tracing": ChatkitWorkflowTracing +} +export const ChatkitWorkflow = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the workflow backing the session." }), + "version": Schema.Union([ + Schema.String.annotate({ + "description": + "Specific workflow version used for the session. Defaults to null when using the latest deployment." + }), + Schema.Null + ]), + "state_variables": Schema.Union([ + Schema.Record( + Schema.String, + Schema.Union([ + Schema.String, + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Boolean, + Schema.Finite, + Schema.Null + ]) + ).annotate({ + "description": + "State variable key-value pairs applied when invoking the workflow. Defaults to null when no overrides were provided." + }), + Schema.Null + ]), + "tracing": Schema.suspend((): Schema.Codec => ChatkitWorkflowTracing).annotate({ + "description": "Tracing settings applied to the workflow." + }) +}).annotate({ + "title": "Workflow", + "description": "Workflow metadata and state returned for the session.", + "identifier": "ChatkitWorkflow" +}) +export type ChatSessionChatkitConfiguration = { + readonly "automatic_thread_titling": ChatSessionAutomaticThreadTitling + readonly "file_upload": ChatSessionFileUpload + readonly "history": ChatSessionHistory +} +export const ChatSessionChatkitConfiguration = Schema.Struct({ + "automatic_thread_titling": Schema.suspend((): Schema.Codec => + ChatSessionAutomaticThreadTitling + ).annotate({ "description": "Automatic thread titling preferences." }), + "file_upload": Schema.suspend((): Schema.Codec => ChatSessionFileUpload).annotate({ + "description": "Upload settings for the session." + }), + "history": Schema.suspend((): Schema.Codec => ChatSessionHistory).annotate({ + "description": "History retention configuration." + }) +}).annotate({ + "title": "ChatKit configuration", + "description": "ChatKit configuration for the session.", + "identifier": "ChatSessionChatkitConfiguration" +}) +export type WorkflowParam = { + readonly "id": string + readonly "version"?: string + readonly "state_variables"?: { readonly [x: string]: string | number | boolean | number } + readonly "tracing"?: WorkflowTracingParam +} +export const WorkflowParam = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier for the workflow invoked by the session." }), + "version": Schema.optionalKey( + Schema.String.annotate({ + "description": "Specific workflow version to run. Defaults to the latest deployed version." + }) + ), + "state_variables": Schema.optionalKey( + Schema.Record( + Schema.String, + Schema.Union([ + Schema.String.check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Boolean, + Schema.Finite + ], { mode: "oneOf" }) + ).annotate({ + "description": + "State variables forwarded to the workflow. Keys may be up to 64 characters, values must be primitive types, and the map defaults to an empty object." + }).check(Schema.isMaxProperties(64).annotate({ "expected": "a value with at most 64 entries" })) + ), + "tracing": Schema.optionalKey( + Schema.suspend((): Schema.Codec => WorkflowTracingParam).annotate({ + "description": + "Optional tracing overrides for the workflow invocation. When omitted, tracing is enabled by default." + }) + ) +}).annotate({ + "title": "Workflow settings", + "description": "Workflow reference and overrides applied to the chat session.", + "identifier": "WorkflowParam" +}) +export type ChatkitConfigurationParam = { + readonly "automatic_thread_titling"?: AutomaticThreadTitlingParam + readonly "file_upload"?: FileUploadParam + readonly "history"?: HistoryParam +} +export const ChatkitConfigurationParam = Schema.Struct({ + "automatic_thread_titling": Schema.optionalKey( + Schema.suspend((): Schema.Codec => AutomaticThreadTitlingParam).annotate({ + "description": + "Configuration for automatic thread titling. When omitted, automatic thread titling is enabled by default." + }) + ), + "file_upload": Schema.optionalKey( + Schema.suspend((): Schema.Codec => FileUploadParam).annotate({ + "description": + "Configuration for upload enablement and limits. When omitted, uploads are disabled by default (max_files 10, max_file_size 512 MB)." + }) + ), + "history": Schema.optionalKey( + Schema.suspend((): Schema.Codec => HistoryParam).annotate({ + "description": + "Configuration for chat history retention. When omitted, history is enabled by default with no limit on recent_threads (null)." + }) + ) +}).annotate({ + "title": "ChatKit configuration overrides", + "description": "Optional per-session configuration settings for ChatKit behavior.", + "identifier": "ChatkitConfigurationParam" +}) +export type Attachment = { + readonly "type": AttachmentType + readonly "id": string + readonly "name": string + readonly "mime_type": string + readonly "preview_url": string | null +} +export const Attachment = Schema.Struct({ + "type": Schema.suspend((): Schema.Codec => AttachmentType).annotate({ + "description": "Attachment discriminator." + }), + "id": Schema.String.annotate({ "description": "Identifier for the attachment." }), + "name": Schema.String.annotate({ "description": "Original display name for the attachment." }), + "mime_type": Schema.String.annotate({ "description": "MIME type of the attachment." }), + "preview_url": Schema.Union([ + Schema.String.annotate({ "description": "Preview URL for rendering the attachment inline.", "format": "uri" }), + Schema.Null + ]) +}).annotate({ + "title": "Attachment", + "description": "Attachment metadata included on thread items.", + "identifier": "Attachment" +}) +export type InferenceOptions = { readonly "tool_choice": ToolChoice | null; readonly "model": string | null } +export const InferenceOptions = Schema.Struct({ + "tool_choice": Schema.Union([ + Schema.suspend((): Schema.Codec => ToolChoice).annotate({ + "description": "Preferred tool to invoke. Defaults to null when ChatKit should auto-select." + }), + Schema.Null + ]), + "model": Schema.Union([ + Schema.String.annotate({ + "description": "Model name that generated the response. Defaults to null when using the session default." + }), + Schema.Null + ]) +}).annotate({ + "title": "Inference options", + "description": "Model and tool overrides applied when generating the assistant response.", + "identifier": "InferenceOptions" +}) +export type FileAnnotation = { readonly "type": "file"; readonly "source": FileAnnotationSource } +export const FileAnnotation = Schema.Struct({ + "type": Schema.Literal("file").annotate({ + "description": "Type discriminator that is always `file` for this annotation." + }), + "source": Schema.suspend((): Schema.Codec => FileAnnotationSource).annotate({ + "description": "File attachment referenced by the annotation." + }) +}).annotate({ + "title": "File annotation", + "description": "Annotation that references an uploaded file.", + "identifier": "FileAnnotation" +}) +export type UrlAnnotation = { readonly "type": "url"; readonly "source": UrlAnnotationSource } +export const UrlAnnotation = Schema.Struct({ + "type": Schema.Literal("url").annotate({ + "description": "Type discriminator that is always `url` for this annotation." + }), + "source": Schema.suspend((): Schema.Codec => UrlAnnotationSource).annotate({ + "description": "URL referenced by the annotation." + }) +}).annotate({ + "title": "URL annotation", + "description": "Annotation that references a URL.", + "identifier": "UrlAnnotation" +}) +export type ClientToolCallItem = { + readonly "id": string + readonly "object": "chatkit.thread_item" + readonly "created_at": number + readonly "thread_id": string + readonly "type": "chatkit.client_tool_call" + readonly "status": ClientToolCallStatus + readonly "call_id": string + readonly "name": string + readonly "arguments": string + readonly "output": string | null +} +export const ClientToolCallItem = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread item." }), + "object": Schema.Literal("chatkit.thread_item").annotate({ + "description": "Type discriminator that is always `chatkit.thread_item`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the item was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ "description": "Identifier of the parent thread." }), + "type": Schema.Literal("chatkit.client_tool_call").annotate({ + "description": "Type discriminator that is always `chatkit.client_tool_call`." + }), + "status": Schema.suspend((): Schema.Codec => ClientToolCallStatus).annotate({ + "description": "Execution status for the tool call." + }), + "call_id": Schema.String.annotate({ "description": "Identifier for the client tool call." }), + "name": Schema.String.annotate({ "description": "Tool name that was invoked." }), + "arguments": Schema.String.annotate({ "description": "JSON-encoded arguments that were sent to the tool." }), + "output": Schema.Union([ + Schema.String.annotate({ + "description": "JSON-encoded output captured from the tool. Defaults to null while execution is in progress." + }), + Schema.Null + ]) +}).annotate({ + "title": "Client tool call", + "description": "Record of a client side tool invocation initiated by the assistant.", + "identifier": "ClientToolCallItem" +}) +export type TaskItem = { + readonly "id": string + readonly "object": "chatkit.thread_item" + readonly "created_at": number + readonly "thread_id": string + readonly "type": "chatkit.task" + readonly "task_type": TaskType + readonly "heading": string | null + readonly "summary": string | null +} +export const TaskItem = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread item." }), + "object": Schema.Literal("chatkit.thread_item").annotate({ + "description": "Type discriminator that is always `chatkit.thread_item`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the item was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ "description": "Identifier of the parent thread." }), + "type": Schema.Literal("chatkit.task").annotate({ + "description": "Type discriminator that is always `chatkit.task`." + }), + "task_type": Schema.suspend((): Schema.Codec => TaskType).annotate({ + "description": "Subtype for the task." + }), + "heading": Schema.Union([ + Schema.String.annotate({ "description": "Optional heading for the task. Defaults to null when not provided." }), + Schema.Null + ]), + "summary": Schema.Union([ + Schema.String.annotate({ + "description": "Optional summary that describes the task. Defaults to null when omitted." + }), + Schema.Null + ]) +}).annotate({ + "title": "Task item", + "description": "Task emitted by the workflow to show progress and status updates.", + "identifier": "TaskItem" +}) +export type TaskGroupTask = { + readonly "type": TaskType + readonly "heading": string | null + readonly "summary": string | null +} +export const TaskGroupTask = Schema.Struct({ + "type": Schema.suspend((): Schema.Codec => TaskType).annotate({ + "description": "Subtype for the grouped task." + }), + "heading": Schema.Union([ + Schema.String.annotate({ + "description": "Optional heading for the grouped task. Defaults to null when not provided." + }), + Schema.Null + ]), + "summary": Schema.Union([ + Schema.String.annotate({ + "description": "Optional summary that describes the grouped task. Defaults to null when omitted." + }), + Schema.Null + ]) +}).annotate({ + "title": "Task group task", + "description": "Task entry that appears within a TaskGroup.", + "identifier": "TaskGroupTask" +}) +export type ThreadResource = { + readonly "id": string + readonly "object": "chatkit.thread" + readonly "created_at": number + readonly "title": string | null + readonly "status": ActiveStatus | LockedStatus | ClosedStatus + readonly "user": string +} +export const ThreadResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread." }), + "object": Schema.Literal("chatkit.thread").annotate({ + "description": "Type discriminator that is always `chatkit.thread`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the thread was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "title": Schema.Union([ + Schema.String.annotate({ + "description": "Optional human-readable title for the thread. Defaults to null when no title has been generated." + }), + Schema.Null + ]), + "status": Schema.Union([ActiveStatus, LockedStatus, ClosedStatus], { mode: "oneOf" }).annotate({ + "description": "Current status for the thread. Defaults to `active` for newly created threads." + }), + "user": Schema.String.annotate({ + "description": "Free-form string that identifies your end user who owns the thread." + }) +}).annotate({ + "title": "The thread object", + "description": "Represents a ChatKit thread and its current status.", + "identifier": "ThreadResource" +}) +export type DecisionInputImage = { + readonly "image_url": string + readonly "detail"?: ImageDetailParam | null + readonly "type": "input_image" +} +export const DecisionInputImage = Schema.Struct({ + "image_url": Schema.String.annotate({ + "description": "A base64-encoded image in a data URL or a publicly accessible HTTP(S) image URL." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1073741824).annotate({ "expected": "a string with at most 1073741824 code points" }) + ).check( + Schema.isPattern(new RegExp("^(data:|https?:\\/\\/)", "u")).annotate({ + "expected": "a string matching the RegExp ^(data:|https?:\\/\\/)" + }) + ), + "detail": Schema.optionalKey( + Schema.Union([ImageDetailParam, Schema.Null]).annotate({ + "description": "The image detail level, using the selected model's image profile. Defaults to auto." + }) + ), + "type": Schema.Literal("input_image") +}).annotate({ + "description": + "An image provided as a base64 data URL or a publicly accessible HTTP(S) URL. File IDs are not supported.", + "identifier": "DecisionInputImage" +}) +export type ChoiceOptionParam = { readonly "value": ChoiceValueParam; readonly "description"?: string } +export const ChoiceOptionParam = Schema.Struct({ + "value": ChoiceValueParam, + "description": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}).annotate({ "identifier": "ChoiceOptionParam" }) +export type QuestionParamScore = { + readonly "type": "score" + readonly "name"?: string + readonly "instructions": string + readonly "levels": ReadonlyArray +} +export const QuestionParamScore = Schema.Struct({ + "type": Schema.Literal("score").annotate({ "description": "The type of the object. Always `score`." }), + "name": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "instructions": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "levels": Schema.Array(ScoreLevelParam).check( + Schema.isMinLength(2).annotate({ "expected": "a value with a length of at least 2" }) + ).check(Schema.isMaxLength(10).annotate({ "expected": "a value with a length of at most 10" })) +}).annotate({ + "description": "Rate the input against the supplied ordered levels.", + "identifier": "QuestionParamScore" +}) +export type ChoiceProbabilityResource = { readonly "value": ChoiceValueResource; readonly "probability": number } +export const ChoiceProbabilityResource = Schema.Struct({ "value": ChoiceValueResource, "probability": Schema.Finite }) + .annotate({ "identifier": "ChoiceProbabilityResource" }) +export type AnswerResourceScore = { + readonly "type": "score" + readonly "name": string | null + readonly "score": number + readonly "probabilities": ReadonlyArray + readonly "confidence": number +} +export const AnswerResourceScore = Schema.Struct({ + "type": Schema.Literal("score").annotate({ "description": "The type of the object. Always `score`." }), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]), + "score": Schema.Finite, + "probabilities": Schema.Array(ScoreProbabilityResource).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "confidence": Schema.Finite +}).annotate({ "identifier": "AnswerResourceScore" }) +export type ResponseUsageResource = { + readonly "input_tokens": number + readonly "input_tokens_details": InputTokensDetailsResource + readonly "output_tokens": number + readonly "output_tokens_details": OutputTokensDetailsResource + readonly "total_tokens": number +} +export const ResponseUsageResource = Schema.Struct({ + "input_tokens": Schema.Number.annotate({ "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check( + Schema.isGreaterThanOrEqualTo(-2147483648).annotate({ "expected": "a value greater than or equal to -2147483648" }) + ).check(Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" })), + "input_tokens_details": InputTokensDetailsResource, + "output_tokens": Schema.Number.annotate({ "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check( + Schema.isGreaterThanOrEqualTo(-2147483648).annotate({ "expected": "a value greater than or equal to -2147483648" }) + ).check(Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" })), + "output_tokens_details": OutputTokensDetailsResource, + "total_tokens": Schema.Number.annotate({ "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check( + Schema.isGreaterThanOrEqualTo(-2147483648).annotate({ "expected": "a value greater than or equal to -2147483648" }) + ).check(Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" })) +}).annotate({ "identifier": "ResponseUsageResource" }) +export type HostedEnvironmentFileResource = HostedEnvironmentFileResourceFileId | HostedEnvironmentFileResourceInline +export const HostedEnvironmentFileResource = Schema.Union([ + HostedEnvironmentFileResourceFileId, + HostedEnvironmentFileResourceInline +], { mode: "oneOf" }).annotate({ + "description": "Metadata for a file materialized in an OpenAI-hosted execution environment.", + "identifier": "HostedEnvironmentFileResource" +}) +export type HostedSkillResource = HostedSkillResourceSkillReference | HostedSkillResourceInline +export const HostedSkillResource = Schema.Union([HostedSkillResourceSkillReference, HostedSkillResourceInline], { + mode: "oneOf" +}).annotate({ + "description": "A skill installed in an OpenAI-hosted environment.", + "identifier": "HostedSkillResource" +}) +export type HostedPluginResource = HostedPluginResourceInline +export const HostedPluginResource = Schema.Union([HostedPluginResourceInline], { mode: "oneOf" }).annotate({ + "description": "A plugin installed in an OpenAI-hosted environment.", + "identifier": "HostedPluginResource" +}) +export type ErrorResponse_2 = { readonly "error": ErrorBodyResource } +export const ErrorResponse_2 = Schema.Struct({ + "error": Schema.suspend((): Schema.Codec => ErrorBodyResource).annotate({ + "description": "The error returned by the API." + }) +}).annotate({ "description": "An API error response.", "identifier": "ErrorResponse-2" }) +export type NetworkPolicyParam = { + readonly "access": NetworkAccessParam + readonly "allowed_domains"?: ReadonlyArray | null + readonly "blocked_domains"?: ReadonlyArray | null +} +export const NetworkPolicyParam = Schema.Struct({ + "access": Schema.suspend((): Schema.Codec => NetworkAccessParam).annotate({ + "description": "The environment's network access mode." + }), + "allowed_domains": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Domains the environment may access when network access is restricted." }) + ), + "blocked_domains": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(100).annotate({ "expected": "a value with a length of at most 100" }) + ), + Schema.Null + ]).annotate({ + "description": + "Domains blocked for both executor and browser when access is restricted. A nonempty list requires `access: restricted` and cannot be combined with nonempty `allowed_domains`. Wildcard domains are not supported." + }) + ) +}).annotate({ "description": "Network access for an OpenAI-hosted environment.", "identifier": "NetworkPolicyParam" }) +export type InlineCapabilitySourceParam = InlineCapabilitySourceParamBase64 +export const InlineCapabilitySourceParam = Schema.Union([InlineCapabilitySourceParamBase64], { mode: "oneOf" }) + .annotate({ + "description": "The encoded ZIP archive for an inline skill or plugin.", + "identifier": "InlineCapabilitySourceParam" + }) +export type HostedEnvironmentFileParam = HostedEnvironmentFileParamFileId | HostedEnvironmentFileParamInline +export const HostedEnvironmentFileParam = Schema.Union([ + HostedEnvironmentFileParamFileId, + HostedEnvironmentFileParamInline +], { mode: "oneOf" }).annotate({ + "description": "A file materialized in an OpenAI-hosted execution environment.", + "identifier": "HostedEnvironmentFileParam" +}) +export type EnvironmentFileListResource = { + readonly "object": EnvironmentFilePageObjectResource + readonly "data": ReadonlyArray + readonly "next": string | null + readonly "has_more": boolean +} +export const EnvironmentFileListResource = Schema.Struct({ + "object": Schema.suspend((): Schema.Codec => EnvironmentFilePageObjectResource) + .annotate({ "description": "The object type. Always `page`." }), + "data": Schema.Array(EnvironmentFileResource).annotate({ "description": "Files available on the current page." }) + .check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "next": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The opaque cursor to use when requesting the next page, if any." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether more files follow this page." }) +}).annotate({ + "description": "A paginated list of live execution environment files.", + "identifier": "EnvironmentFileListResource" +}) +export type SessionEventAgentSessionTurnContentPartAdded = { + readonly "type": "agent.session.turn.content_part.added" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "part": OutputTextResource +} +export const SessionEventAgentSessionTurnContentPartAdded = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.content_part.added").annotate({ + "description": "The type of the object. Always `agent.session.turn.content_part.added`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the message item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part in the message.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "part": Schema.suspend((): Schema.Codec => OutputTextResource).annotate({ + "description": "The initial content part." + }) +}).annotate({ + "description": "Emitted when an output text content part is added.", + "identifier": "SessionEventAgentSessionTurnContentPartAdded" +}) +export type SessionEventAgentSessionTurnContentPartDone = { + readonly "type": "agent.session.turn.content_part.done" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "part": OutputTextResource +} +export const SessionEventAgentSessionTurnContentPartDone = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.content_part.done").annotate({ + "description": "The type of the object. Always `agent.session.turn.content_part.done`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the message item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part in the message.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "part": Schema.suspend((): Schema.Codec => OutputTextResource).annotate({ + "description": "The completed content part." + }) +}).annotate({ + "description": "Emitted when an output content part is complete.", + "identifier": "SessionEventAgentSessionTurnContentPartDone" +}) +export type AgentContentResource = OutputTextResource | EncryptedContentResource +export const AgentContentResource = Schema.Union([OutputTextResource, EncryptedContentResource], { mode: "oneOf" }) + .annotate({ + "description": "A plaintext or encrypted content part exchanged between agents.", + "identifier": "AgentContentResource" + }) +export type MessageContentResource = + | MessageContentResourceInputText + | MessageContentResourceInputImage + | MessageContentResourceOutputText +export const MessageContentResource = Schema.Union([ + MessageContentResourceInputText, + MessageContentResourceInputImage, + MessageContentResourceOutputText +], { mode: "oneOf" }).annotate({ + "description": "A content part in a session message.", + "identifier": "MessageContentResource" +}) +export type AssistantMessageItemResource = { + readonly "type": "message" + readonly "id": string + readonly "turn_id": string + readonly "role": "assistant" + readonly "status": OutputItemStatusResource + readonly "content": ReadonlyArray + readonly "phase": MessagePhaseResource | null +} +export const AssistantMessageItemResource = Schema.Struct({ + "type": Schema.Literal("message").annotate({ "description": "The item type. Always `message`." }), + "id": Schema.String.annotate({ "description": "The ID of the message." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "role": Schema.Literal("assistant").annotate({ + "description": "The role of the message author. Always `assistant`." + }), + "status": Schema.suspend((): Schema.Codec => OutputItemStatusResource).annotate({ + "description": "The status of the message." + }), + "content": Schema.Array(OutputTextResource).annotate({ "description": "The content of the message." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "phase": Schema.Union([MessagePhaseResource, Schema.Null]).annotate({ + "description": "The phase of the assistant message." + }) +}).annotate({ + "description": "An assistant message produced by the agent.", + "identifier": "AssistantMessageItemResource" +}) +export type ReasoningItemResource = { + readonly "type": "reasoning" + readonly "id": string + readonly "turn_id": string + readonly "summary": ReadonlyArray + readonly "status": OutputItemStatusResource | null +} +export const ReasoningItemResource = Schema.Struct({ + "type": Schema.Literal("reasoning").annotate({ "description": "The item type. Always `reasoning`." }), + "id": Schema.String.annotate({ "description": "The ID of the reasoning item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "summary": Schema.Array(SummaryTextResource).annotate({ + "description": "The reasoning summaries produced by the agent." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "status": Schema.Union([OutputItemStatusResource, Schema.Null]).annotate({ + "description": "The status of the reasoning item." + }) +}).annotate({ "description": "A reasoning item produced by the agent.", "identifier": "ReasoningItemResource" }) +export type SessionEventAgentSessionTurnReasoningSummaryPartAdded = { + readonly "type": "agent.session.turn.reasoning_summary_part.added" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "part": SummaryTextResource +} +export const SessionEventAgentSessionTurnReasoningSummaryPartAdded = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.reasoning_summary_part.added").annotate({ + "description": "The type of the object. Always `agent.session.turn.reasoning_summary_part.added`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the reasoning item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary content part.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "part": Schema.suspend((): Schema.Codec => SummaryTextResource).annotate({ + "description": "The initial summary part." + }) +}).annotate({ + "description": "Emitted when a reasoning summary content part is added.", + "identifier": "SessionEventAgentSessionTurnReasoningSummaryPartAdded" +}) +export type SessionEventAgentSessionTurnReasoningSummaryPartDone = { + readonly "type": "agent.session.turn.reasoning_summary_part.done" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "part": SummaryTextResource + readonly "status": "incomplete" | null +} +export const SessionEventAgentSessionTurnReasoningSummaryPartDone = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.reasoning_summary_part.done").annotate({ + "description": "The type of the object. Always `agent.session.turn.reasoning_summary_part.done`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "item_id": Schema.String.annotate({ "description": "The ID of the reasoning item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "summary_index": Schema.Number.annotate({ "description": "The index of the summary part.", "format": "int64" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })).check( + Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" }) + ), + "part": Schema.suspend((): Schema.Codec => SummaryTextResource).annotate({ + "description": "The completed summary part." + }), + "status": Schema.Union([Schema.Literal("incomplete"), Schema.Null]).annotate({ + "description": "Present as `incomplete` when summary generation was interrupted." + }) +}).annotate({ + "description": "Emitted when a reasoning summary part is complete.", + "identifier": "SessionEventAgentSessionTurnReasoningSummaryPartDone" +}) +export type FunctionCallItemResource = { + readonly "type": "function_call" + readonly "id": string + readonly "turn_id": string + readonly "call_id": string + readonly "name": string + readonly "arguments": Schema.Json + readonly "status": FunctionCallStatusResource +} +export const FunctionCallItemResource = Schema.Struct({ + "type": Schema.Literal("function_call").annotate({ "description": "The item type. Always `function_call`." }), + "id": Schema.String.annotate({ "description": "The ID of the function call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "call_id": Schema.String.annotate({ "description": "The ID used to submit the function result." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.String.annotate({ "description": "The name of the function to call." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "arguments": Schema.Json.annotate({ + "expected": "JSON value", + "description": "The arguments to pass to the function." + }), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the function call." + }) +}).annotate({ "description": "A function call produced by the agent.", "identifier": "FunctionCallItemResource" }) +export type McpCallItemResource = { + readonly "type": "mcp_call" + readonly "id": string + readonly "turn_id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": Schema.Json + readonly "status": FunctionCallStatusResource + readonly "output": Schema.Json | null + readonly "error": Schema.Json | null +} +export const McpCallItemResource = Schema.Struct({ + "type": Schema.Literal("mcp_call").annotate({ "description": "The item type. Always `mcp_call`." }), + "id": Schema.String.annotate({ "description": "The ID of the MCP call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.String.annotate({ "description": "The name of the MCP tool." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "arguments": Schema.Json.annotate({ + "expected": "JSON value", + "description": "The arguments passed to the MCP tool." + }), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the MCP tool call." + }), + "output": Schema.Union([Schema.Json.annotate({ "expected": "JSON value" }), Schema.Null]).annotate({ + "description": "The output returned by the MCP tool, if any." + }), + "error": Schema.Union([Schema.Json.annotate({ "expected": "JSON value" }), Schema.Null]).annotate({ + "description": "The error returned by the MCP tool, if any." + }) +}).annotate({ "description": "A call to a tool on an MCP server.", "identifier": "McpCallItemResource" }) +export type CommandExecutionItemResource = { + readonly "type": "command_execution" + readonly "id": string + readonly "turn_id": string + readonly "command": string + readonly "cwd": string | null + readonly "status": FunctionCallStatusResource + readonly "output": string | null + readonly "exit_code": number | null + readonly "duration_ms": number | null +} +export const CommandExecutionItemResource = Schema.Struct({ + "type": Schema.Literal("command_execution").annotate({ "description": "The item type. Always `command_execution`." }), + "id": Schema.String.annotate({ "description": "The ID of the command execution item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "command": Schema.String.annotate({ "description": "The command that was executed." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "cwd": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The working directory used to execute the command." }), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the command execution." + }), + "output": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The command output, if available." }), + "exit_code": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The process exit code, if the command completed.", "format": "int64" }), + "duration_ms": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The command duration in milliseconds.", "format": "int64" }) +}).annotate({ + "description": "A command execution produced by the agent.", + "identifier": "CommandExecutionItemResource" +}) +export type ResumeSubagentCallItemResource = { + readonly "type": "resume_subagent_call" + readonly "id": string + readonly "turn_id": string + readonly "status": FunctionCallStatusResource + readonly "sender_agent_id": string + readonly "recipient_agent_id": string +} +export const ResumeSubagentCallItemResource = Schema.Struct({ + "type": Schema.Literal("resume_subagent_call").annotate({ + "description": "The item type. Always `resume_subagent_call`." + }), + "id": Schema.String.annotate({ "description": "The ID of the tool call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the tool call." + }), + "sender_agent_id": Schema.String.annotate({ "description": "The ID of the agent requesting the resume." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "recipient_agent_id": Schema.String.annotate({ "description": "The ID of the agent to resume." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "A request to resume a subagent.", "identifier": "ResumeSubagentCallItemResource" }) +export type WaitForSubagentsCallItemResource = { + readonly "type": "wait_for_subagents_call" + readonly "id": string + readonly "turn_id": string + readonly "status": FunctionCallStatusResource + readonly "sender_agent_id": string + readonly "recipient_agent_ids": ReadonlyArray +} +export const WaitForSubagentsCallItemResource = Schema.Struct({ + "type": Schema.Literal("wait_for_subagents_call").annotate({ + "description": "The item type. Always `wait_for_subagents_call`." + }), + "id": Schema.String.annotate({ "description": "The ID of the tool call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the tool call." + }), + "sender_agent_id": Schema.String.annotate({ "description": "The ID of the agent waiting for results." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "recipient_agent_ids": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "The IDs of the agents to wait for." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +}).annotate({ + "description": "A request to wait for one or more subagents.", + "identifier": "WaitForSubagentsCallItemResource" +}) +export type InterruptSubagentCallItemResource = { + readonly "type": "interrupt_subagent_call" + readonly "id": string + readonly "turn_id": string + readonly "status": FunctionCallStatusResource + readonly "sender_agent_id": string + readonly "recipient_agent_id": string +} +export const InterruptSubagentCallItemResource = Schema.Struct({ + "type": Schema.Literal("interrupt_subagent_call").annotate({ + "description": "The item type. Always `interrupt_subagent_call`." + }), + "id": Schema.String.annotate({ "description": "The ID of the tool call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the tool call." + }), + "sender_agent_id": Schema.String.annotate({ "description": "The ID of the agent requesting the interrupt." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "recipient_agent_id": Schema.String.annotate({ "description": "The ID of the agent to interrupt." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A request to interrupt a subagent's current turn. The subagent remains available.", + "identifier": "InterruptSubagentCallItemResource" +}) +export type CloseSubagentCallItemResource = { + readonly "type": "close_subagent_call" + readonly "id": string + readonly "turn_id": string + readonly "status": FunctionCallStatusResource + readonly "sender_agent_id": string + readonly "recipient_agent_id": string +} +export const CloseSubagentCallItemResource = Schema.Struct({ + "type": Schema.Literal("close_subagent_call").annotate({ + "description": "The item type. Always `close_subagent_call`." + }), + "id": Schema.String.annotate({ "description": "The ID of the tool call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the tool call." + }), + "sender_agent_id": Schema.String.annotate({ "description": "The ID of the agent requesting the close." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "recipient_agent_id": Schema.String.annotate({ "description": "The ID of the agent to close." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ "description": "A request to close a subagent.", "identifier": "CloseSubagentCallItemResource" }) +export type InputContentResource = InputContentResourceInputText | InputContentResourceInputImage +export const InputContentResource = Schema.Union([InputContentResourceInputText, InputContentResourceInputImage], { + mode: "oneOf" +}).annotate({ + "description": "User-provided content recorded in a session item.", + "identifier": "InputContentResource" +}) +export type ComputerUseCallItemResource = { + readonly "type": "computer_use_call" + readonly "id": string + readonly "turn_id": string + readonly "title": string | null + readonly "status": FunctionCallStatusResource + readonly "output": ComputerScreenshotResource | null +} +export const ComputerUseCallItemResource = Schema.Struct({ + "type": Schema.Literal("computer_use_call").annotate({ "description": "The item type. Always `computer_use_call`." }), + "id": Schema.String.annotate({ "description": "The ID of the activity item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "title": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "A model-generated description of the activity, when available." }), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The execution status of the activity." + }), + "output": Schema.Union([ComputerScreenshotResource, Schema.Null]).annotate({ + "description": + "The last screenshot emitted by the model. Null when screenshot inclusion is disabled or the call emitted no screenshot." + }) +}).annotate({ + "description": "One execution of the platform-provided computer-use capability.", + "identifier": "ComputerUseCallItemResource" +}) +export type BrowserAuthenticationHistoryRequestKindResourceBrowserAuthentication = { + readonly "type": "browser_authentication" + readonly "reason": string | null + readonly "credential_origin": string | null + readonly "fields": ReadonlyArray + readonly "options": ReadonlyArray +} +export const BrowserAuthenticationHistoryRequestKindResourceBrowserAuthentication = Schema.Struct({ + "type": Schema.Literal("browser_authentication").annotate({ + "description": "The type of the object. Always `browser_authentication`." + }), + "reason": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "Why the agent needs the user to sign in." }), + "credential_origin": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The registered form or frame origin where values will be entered." }), + "fields": Schema.Array(BrowserAuthenticationFieldResource).annotate({ + "description": "Controls to render. All submitted values are sensitive." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "options": Schema.Array(BrowserAuthenticationOptionResource).annotate({ + "description": "Sign-in methods. Empty for a plain form." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) +}).annotate({ + "description": "A registered form awaiting the application's response.", + "identifier": "BrowserAuthenticationHistoryRequestKindResourceBrowserAuthentication" +}) +export type ComputerUseApprovalRequestKindResourceBrowserAuthentication = { + readonly "type": "browser_authentication" + readonly "reason": string | null + readonly "credential_origin": string | null + readonly "fields": ReadonlyArray + readonly "options": ReadonlyArray +} +export const ComputerUseApprovalRequestKindResourceBrowserAuthentication = Schema.Struct({ + "type": Schema.Literal("browser_authentication").annotate({ + "description": "The type of the object. Always `browser_authentication`." + }), + "reason": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "Why the agent needs the user to sign in." }), + "credential_origin": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The registered form or frame origin where values will be entered." }), + "fields": Schema.Array(BrowserAuthenticationFieldResource).annotate({ + "description": "Controls to render. All submitted values are sensitive." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "options": Schema.Array(BrowserAuthenticationOptionResource).annotate({ + "description": "Sign-in methods. Empty for a plain form." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) +}).annotate({ + "description": "A registered form awaiting the application's response.", + "identifier": "ComputerUseApprovalRequestKindResourceBrowserAuthentication" +}) +export type ComputerUseApprovalResponseKindResource = + | ComputerUseApprovalResponseKindResourceBrowserAuthenticationSubmitResource + | ComputerUseApprovalResponseKindResourceBrowserAuthenticationCancelResource +export const ComputerUseApprovalResponseKindResource = Schema.Union([ + ComputerUseApprovalResponseKindResourceBrowserAuthenticationSubmitResource, + ComputerUseApprovalResponseKindResourceBrowserAuthenticationCancelResource +], { mode: "oneOf" }).annotate({ "identifier": "ComputerUseApprovalResponseKindResource" }) +export type WebSearchActionResource = + | WebSearchActionResourceSearch + | WebSearchActionResourceOpenPage + | WebSearchActionResourceFindInPage + | WebSearchActionResourceOther +export const WebSearchActionResource = Schema.Union([ + WebSearchActionResourceSearch, + WebSearchActionResourceOpenPage, + WebSearchActionResourceFindInPage, + WebSearchActionResourceOther +], { mode: "oneOf" }).annotate({ + "description": "An action performed by the web search tool.", + "identifier": "WebSearchActionResource" +}) +export type SessionTurnErrorResource = { readonly "code": SessionTurnErrorCodeResource; readonly "message": string } +export const SessionTurnErrorResource = Schema.Struct({ + "code": Schema.suspend((): Schema.Codec => SessionTurnErrorCodeResource).annotate({ + "description": "A stable, machine-readable failure category." + }), + "message": Schema.String.annotate({ "description": "A customer-safe explanation of the failure." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ) +}).annotate({ + "description": "A customer-safe error describing why a session request failed.", + "identifier": "SessionTurnErrorResource" +}) +export type TokenUsageResource = { + readonly "input_tokens": number + readonly "input_tokens_details": InputTokensDetailsResource_2 + readonly "output_tokens": number + readonly "output_tokens_details": OutputTokensDetailsResource_2 + readonly "total_tokens": number +} +export const TokenUsageResource = Schema.Struct({ + "input_tokens": Schema.Number.annotate({ + "description": "The number of input tokens used by the agent.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "input_tokens_details": Schema.suspend((): Schema.Codec => InputTokensDetailsResource_2) + .annotate({ "description": "A breakdown of the agent's input token usage." }), + "output_tokens": Schema.Number.annotate({ + "description": "The number of output tokens generated by the agent.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "output_tokens_details": Schema.suspend((): Schema.Codec => + OutputTokensDetailsResource_2 + ).annotate({ "description": "A breakdown of the agent's output token usage." }), + "total_tokens": Schema.Number.annotate({ + "description": "The total number of input and output tokens used by the agent.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Recorded token usage for a session or turn. Usage is best effort and may change.", + "identifier": "TokenUsageResource" +}) +export type ReasoningResource = { + readonly "effort": ReasoningEffortResource | null + readonly "summary": ReasoningSummaryResource | null +} +export const ReasoningResource = Schema.Struct({ + "effort": Schema.Union([ReasoningEffortResource, Schema.Null]).annotate({ + "description": "The requested reasoning effort, or `null` when the model selects its own default." + }), + "summary": Schema.Union([ReasoningSummaryResource, Schema.Null]).annotate({ + "description": "The requested reasoning summary format, or `null` when summaries are disabled." + }) +}).annotate({ "description": "The reasoning configuration used by an agent.", "identifier": "ReasoningResource" }) +export type TextFormatResource = TextFormatResourceText | TextFormatResourceJsonSchema +export const TextFormatResource = Schema.Union([TextFormatResourceText, TextFormatResourceJsonSchema], { + mode: "oneOf" +}).annotate({ "description": "The effective output format for generated text.", "identifier": "TextFormatResource" }) +export type PersistedMcpTransportResource = PersistedMcpTransportResourceHttp | PersistedMcpTransportResourceStdio +export const PersistedMcpTransportResource = Schema.Union([ + PersistedMcpTransportResourceHttp, + PersistedMcpTransportResourceStdio +], { mode: "oneOf" }).annotate({ + "description": "A credential-free transport used to connect to an MCP server.", + "identifier": "PersistedMcpTransportResource" +}) +export type PersistedAgentToolResourceWebSearch = { + readonly "type": "web_search" + readonly "mode": WebSearchModeResource + readonly "context_size": WebSearchContextSizeResource + readonly "allowed_domains": ReadonlyArray | null + readonly "location": WebSearchLocationResource | null +} +export const PersistedAgentToolResourceWebSearch = Schema.Struct({ + "type": Schema.Literal("web_search").annotate({ "description": "The type of the object. Always `web_search`." }), + "mode": Schema.suspend((): Schema.Codec => WebSearchModeResource).annotate({ + "description": "The source used for web search results." + }), + "context_size": Schema.suspend((): Schema.Codec => WebSearchContextSizeResource) + .annotate({ "description": "The amount of search context made available to the model. Defaults to `medium`." }), + "allowed_domains": Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + Schema.Null + ]).annotate({ "description": "Allowed search domains, or `null` when the search is unrestricted." }), + "location": Schema.Union([WebSearchLocationResource, Schema.Null]).annotate({ + "description": "Approximate location used to localize search results, if provided." + }) +}).annotate({ "description": "Web search.", "identifier": "PersistedAgentToolResourceWebSearch" }) +export type AgentToolResourceWebSearch = { + readonly "type": "web_search" + readonly "mode": WebSearchModeResource + readonly "context_size": WebSearchContextSizeResource + readonly "allowed_domains": ReadonlyArray | null + readonly "location": WebSearchLocationResource | null +} +export const AgentToolResourceWebSearch = Schema.Struct({ + "type": Schema.Literal("web_search").annotate({ "description": "The type of the object. Always `web_search`." }), + "mode": Schema.suspend((): Schema.Codec => WebSearchModeResource).annotate({ + "description": "The source used for web search results." + }), + "context_size": Schema.suspend((): Schema.Codec => WebSearchContextSizeResource) + .annotate({ "description": "The amount of search context made available to the model. Defaults to `medium`." }), + "allowed_domains": Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + Schema.Null + ]).annotate({ "description": "Allowed search domains, or `null` when the search is unrestricted." }), + "location": Schema.Union([WebSearchLocationResource, Schema.Null]).annotate({ + "description": "Approximate location used to localize search results, if provided." + }) +}).annotate({ "description": "Web search.", "identifier": "AgentToolResourceWebSearch" }) +export type UpdateSessionReasoningParam = { readonly "effort"?: ReasoningEffortParam | null } +export const UpdateSessionReasoningParam = Schema.Struct({ + "effort": Schema.optionalKey( + Schema.Union([ReasoningEffortParam, Schema.Null]).annotate({ + "description": "Omit to keep the current effort. Null selects the model's default effort." + }) + ) +}).annotate({ + "description": "Reasoning effort for subsequent turns. The reasoning summary stays unchanged.", + "identifier": "UpdateSessionReasoningParam" +}) +export type ReasoningParam = { + readonly "effort"?: ReasoningEffortParam | null + readonly "summary"?: ReasoningSummaryParam | null +} +export const ReasoningParam = Schema.Struct({ + "effort": Schema.optionalKey( + Schema.Union([ReasoningEffortParam, Schema.Null]).annotate({ + "description": "The amount of reasoning effort the model should use. Omission lets the model select it." + }) + ), + "summary": Schema.optionalKey( + Schema.Union([ReasoningSummaryParam, Schema.Null]).annotate({ + "description": "Controls whether the response includes a reasoning summary." + }) + ) +}).annotate({ "description": "Reasoning configuration for the agent.", "identifier": "ReasoningParam" }) +export type TextFormatParam = TextFormatParamText | TextFormatParamJsonSchema +export const TextFormatParam = Schema.Union([TextFormatParamText, TextFormatParamJsonSchema], { mode: "oneOf" }) + .annotate({ "description": "The output format for generated text.", "identifier": "TextFormatParam" }) +export type PersistedMcpTransportConfigParam = + | PersistedMcpTransportConfigParamHttp + | PersistedMcpTransportConfigParamStdio +export const PersistedMcpTransportConfigParam = Schema.Union([ + PersistedMcpTransportConfigParamHttp, + PersistedMcpTransportConfigParamStdio +], { mode: "oneOf" }).annotate({ + "description": "A credential-free transport used to connect to an MCP server.", + "identifier": "PersistedMcpTransportConfigParam" +}) +export type PersistedAgentToolConfigParamWebSearch = { + readonly "type": "web_search" + readonly "mode"?: WebSearchModeParam | null + readonly "context_size"?: WebSearchContextSizeParam | null + readonly "allowed_domains"?: ReadonlyArray | null + readonly "location"?: WebSearchLocationParam | null +} +export const PersistedAgentToolConfigParamWebSearch = Schema.Struct({ + "type": Schema.Literal("web_search").annotate({ "description": "The type of the object. Always `web_search`." }), + "mode": Schema.optionalKey( + Schema.Union([WebSearchModeParam, Schema.Null]).annotate({ + "description": "The source used for web search results. Defaults to `live`." + }) + ), + "context_size": Schema.optionalKey( + Schema.Union([WebSearchContextSizeParam, Schema.Null]).annotate({ + "description": "The amount of search context made available to the model. Defaults to `medium`." + }) + ), + "allowed_domains": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Domains the search may include." }) + ), + "location": Schema.optionalKey( + Schema.Union([WebSearchLocationParam, Schema.Null]).annotate({ + "description": "Approximate location used to localize search results." + }) + ) +}).annotate({ "description": "Web search.", "identifier": "PersistedAgentToolConfigParamWebSearch" }) +export type AgentToolConfigParamWebSearch = { + readonly "type": "web_search" + readonly "mode"?: WebSearchModeParam | null + readonly "context_size"?: WebSearchContextSizeParam | null + readonly "allowed_domains"?: ReadonlyArray | null + readonly "location"?: WebSearchLocationParam | null +} +export const AgentToolConfigParamWebSearch = Schema.Struct({ + "type": Schema.Literal("web_search").annotate({ "description": "The type of the object. Always `web_search`." }), + "mode": Schema.optionalKey( + Schema.Union([WebSearchModeParam, Schema.Null]).annotate({ + "description": "The source used for web search results. Defaults to `live`." + }) + ), + "context_size": Schema.optionalKey( + Schema.Union([WebSearchContextSizeParam, Schema.Null]).annotate({ + "description": "The amount of search context made available to the model. Defaults to `medium`." + }) + ), + "allowed_domains": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Domains the search may include." }) + ), + "location": Schema.optionalKey( + Schema.Union([WebSearchLocationParam, Schema.Null]).annotate({ + "description": "Approximate location used to localize search results." + }) + ) +}).annotate({ "description": "Web search.", "identifier": "AgentToolConfigParamWebSearch" }) +export type NetworkPolicyResource = { + readonly "access": NetworkAccessResource + readonly "allowed_domains": ReadonlyArray +} +export const NetworkPolicyResource = Schema.Struct({ + "access": Schema.suspend((): Schema.Codec => NetworkAccessResource).annotate({ + "description": "The environment's network access mode." + }), + "allowed_domains": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Domains the environment may access when network access is restricted." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +}).annotate({ + "description": "Network access for an OpenAI-hosted environment.", + "identifier": "NetworkPolicyResource" +}) +export type HostedTemplateSkillResource = HostedTemplateSkillResourceSkillReference | HostedTemplateSkillResourceInline +export const HostedTemplateSkillResource = Schema.Union([ + HostedTemplateSkillResourceSkillReference, + HostedTemplateSkillResourceInline +], { mode: "oneOf" }).annotate({ + "description": "Safe metadata for a skill configured by an environment template.", + "identifier": "HostedTemplateSkillResource" +}) +export type HostedTemplateFileResource = HostedTemplateFileResourceFileId | HostedTemplateFileResourceInline +export const HostedTemplateFileResource = Schema.Union([ + HostedTemplateFileResourceFileId, + HostedTemplateFileResourceInline +], { mode: "oneOf" }).annotate({ + "description": "Safe metadata for a file configured by an environment template.", + "identifier": "HostedTemplateFileResource" +}) +export type McpTransportResource = McpTransportResourceHttp | McpTransportResourceStdio +export const McpTransportResource = Schema.Union([McpTransportResourceHttp, McpTransportResourceStdio], { + mode: "oneOf" +}).annotate({ "description": "The transport used to connect to an MCP server.", "identifier": "McpTransportResource" }) +export type McpTransportConfigParam = McpTransportConfigParamHttp | McpTransportConfigParamStdio +export const McpTransportConfigParam = Schema.Union([McpTransportConfigParamHttp, McpTransportConfigParamStdio], { + mode: "oneOf" +}).annotate({ + "description": "The transport used to connect to an MCP server.", + "identifier": "McpTransportConfigParam" +}) +export type InputContentParam = InputContentParamInputText | InputContentParamInputImage +export const InputContentParam = Schema.Union([InputContentParamInputText, InputContentParamInputImage], { + mode: "oneOf" +}).annotate({ "description": "Content included in an input message.", "identifier": "InputContentParam" }) +export type SessionEventError = { + readonly "type": "error" + readonly "event_id": string + readonly "session_id": string + readonly "error": SessionErrorResource +} +export const SessionEventError = Schema.Struct({ + "type": Schema.Literal("error").annotate({ "description": "The type of the object. Always `error`." }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "error": Schema.suspend((): Schema.Codec => SessionErrorResource).annotate({ + "description": "The error that occurred." + }) +}).annotate({ "description": "Emitted when a turn or session fails.", "identifier": "SessionEventError" }) +export type SessionEnvironmentStateResource = { + readonly "id": string + readonly "type": string + readonly "status": SessionEnvironmentStatusResource + readonly "error": SessionEnvironmentErrorResource | null +} +export const SessionEnvironmentStateResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The public ID of the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "type": Schema.String.annotate({ "description": "The environment type." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => SessionEnvironmentStatusResource) + .annotate({ "description": "The environment's connection status." }), + "error": Schema.Union([SessionEnvironmentErrorResource, Schema.Null]).annotate({ + "description": "The error reported while preparing the environment, if any." + }) +}).annotate({ + "description": "The current state of a session environment.", + "identifier": "SessionEnvironmentStateResource" +}) +export type SessionArtifactListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SessionArtifactListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(SessionArtifactResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "SessionArtifactListResource" +}) +export type ComputerUseApprovalResponseParamBrowserAuthentication = { + readonly "type": "browser_authentication" + readonly "action": "submit" + readonly "fields": ReadonlyArray + readonly "selected_option"?: string | null +} | { readonly "type": "browser_authentication"; readonly "action": "cancel" } +export const ComputerUseApprovalResponseParamBrowserAuthentication = Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("browser_authentication"), + "action": Schema.Literal("submit"), + "fields": Schema.Array(BrowserAuthenticationFieldValueParam).annotate({ + "description": + "Values for up to six active fields in the required action. The submitted field-value mapping and selected option must fit within 120 KiB of JSON." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(6).annotate({ "expected": "a value with a length of at most 6" }) + ), + "selected_option": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The chosen method. Required when the required action contains options." }) + ) + }), + Schema.Struct({ "type": Schema.Literal("browser_authentication"), "action": Schema.Literal("cancel") }) +], { mode: "oneOf" }).annotate({ "identifier": "ComputerUseApprovalResponseParamBrowserAuthentication" }) +export type ComputerUseApprovalResponseParamBrowserOriginAccessParam = { + readonly "type": "browser_origin_access" + readonly "decision": BrowserOriginAccessDecisionParam +} +export const ComputerUseApprovalResponseParamBrowserOriginAccessParam = Schema.Struct({ + "type": Schema.Literal("browser_origin_access"), + "decision": Schema.suspend((): Schema.Codec => BrowserOriginAccessDecisionParam) + .annotate({ "description": "Whether to allow, deny, or cancel the requested origin access." }) +}).annotate({ "identifier": "ComputerUseApprovalResponseParamBrowserOriginAccessParam" }) +export type SessionTraceListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SessionTraceListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(SessionTurnTraceResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "SessionTraceListResource" +}) +export type VaultStatusFilterParam = VaultStatusParam | ReadonlyArray +export const VaultStatusFilterParam = Schema.Union([ + VaultStatusParam, + Schema.Array(VaultStatusParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })) +], { mode: "oneOf" }).annotate({ + "description": "One or more lifecycle statuses to include when listing vaults or credentials.", + "identifier": "VaultStatusFilterParam" +}) +export type VaultListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const VaultListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(VaultResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "VaultListResource" +}) +export type McpOauthTokenEndpointAuthResource = + | McpOauthTokenEndpointAuthResourceNone + | McpOauthTokenEndpointAuthResourceClientSecretBasic + | McpOauthTokenEndpointAuthResourceClientSecretPost +export const McpOauthTokenEndpointAuthResource = Schema.Union([ + McpOauthTokenEndpointAuthResourceNone, + McpOauthTokenEndpointAuthResourceClientSecretBasic, + McpOauthTokenEndpointAuthResourceClientSecretPost +], { mode: "oneOf" }).annotate({ + "description": "The client authentication method used for OAuth token refresh.", + "identifier": "McpOauthTokenEndpointAuthResource" +}) +export type VaultCredentialNetworkingResource = + | VaultCredentialNetworkingResourceUnrestricted + | VaultCredentialNetworkingResourceLimited +export const VaultCredentialNetworkingResource = Schema.Union([ + VaultCredentialNetworkingResourceUnrestricted, + VaultCredentialNetworkingResourceLimited +], { mode: "oneOf" }).annotate({ + "description": + "Destination permissions for an environment-variable credential. These do not grant network access to the environment.", + "identifier": "VaultCredentialNetworkingResource" +}) +export type CreateMcpOauthTokenEndpointAuthParam = + | CreateMcpOauthTokenEndpointAuthParamNone + | CreateMcpOauthTokenEndpointAuthParamClientSecretBasic + | CreateMcpOauthTokenEndpointAuthParamClientSecretPost +export const CreateMcpOauthTokenEndpointAuthParam = Schema.Union([ + CreateMcpOauthTokenEndpointAuthParamNone, + CreateMcpOauthTokenEndpointAuthParamClientSecretBasic, + CreateMcpOauthTokenEndpointAuthParamClientSecretPost +], { mode: "oneOf" }).annotate({ + "description": "Client authentication credentials for OAuth token refresh.", + "identifier": "CreateMcpOauthTokenEndpointAuthParam" +}) +export type VaultCredentialNetworkingParam = + | VaultCredentialNetworkingParamUnrestricted + | VaultCredentialNetworkingParamLimited +export const VaultCredentialNetworkingParam = Schema.Union([ + VaultCredentialNetworkingParamUnrestricted, + VaultCredentialNetworkingParamLimited +], { mode: "oneOf" }).annotate({ + "description": + "Destination permissions for an environment-variable credential. These do not grant network access to the environment.", + "identifier": "VaultCredentialNetworkingParam" +}) +export type RotateMcpOauthTokenEndpointAuthParam = + | RotateMcpOauthTokenEndpointAuthParamClientSecretBasic + | RotateMcpOauthTokenEndpointAuthParamClientSecretPost +export const RotateMcpOauthTokenEndpointAuthParam = Schema.Union([ + RotateMcpOauthTokenEndpointAuthParamClientSecretBasic, + RotateMcpOauthTokenEndpointAuthParamClientSecretPost +], { mode: "oneOf" }).annotate({ + "description": "Client-secret updates that preserve the credential's OAuth authentication method.", + "identifier": "RotateMcpOauthTokenEndpointAuthParam" +}) +export type WebhookEndpointListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const WebhookEndpointListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always list." }), + "data": Schema.Array(WebhookEndpointBody).annotate({ "description": "The webhook endpoints in this page." }), + "first_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the first endpoint in this page." }), + Schema.Null + ]), + "last_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the last endpoint in this page." }), + Schema.Null + ]), + "has_more": Schema.Boolean.annotate({ "description": "Whether more webhook endpoints are available." }) +}).annotate({ "identifier": "WebhookEndpointListResource" }) +export type PublicCreateEndpointBody = { + readonly "name": string + readonly "url": string + readonly "event_types": ReadonlyArray +} +export const PublicCreateEndpointBody = Schema.Struct({ + "name": Schema.String.annotate({ "description": "A human-readable name for the webhook endpoint." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })), + "url": Schema.String.annotate({ "description": "The HTTPS URL that receives webhook deliveries." }).check( + Schema.isMaxCodePoints(2048).annotate({ "expected": "a string with at most 2048 code points" }) + ).check( + Schema.isPattern(new RegExp("^https:\\/\\/", "u")).annotate({ + "expected": "a string matching the RegExp ^https:\\/\\/" + }) + ), + "event_types": Schema.Array(ProjectEventTypeEnum).annotate({ + "description": "The event types that trigger deliveries to this endpoint." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) +}).annotate({ "identifier": "PublicCreateEndpointBody" }) +export type PublicUpdateEndpointBody = { + readonly "name"?: string + readonly "url"?: string + readonly "event_types"?: ReadonlyArray +} +export const PublicUpdateEndpointBody = Schema.Struct({ + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "A new human-readable name for the webhook endpoint." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ), + "url": Schema.optionalKey( + Schema.String.annotate({ "description": "A new HTTPS URL that receives webhook deliveries." }).check( + Schema.isMaxCodePoints(2048).annotate({ "expected": "a string with at most 2048 code points" }) + ).check( + Schema.isPattern(new RegExp("^https:\\/\\/", "u")).annotate({ + "expected": "a string matching the RegExp ^https:\\/\\/" + }) + ) + ), + "event_types": Schema.optionalKey( + Schema.Array(ProjectEventTypeEnum).annotate({ + "description": "The complete set of event types that should trigger deliveries." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ) +}).annotate({ "identifier": "PublicUpdateEndpointBody" }) +export type PublicTestEndpointBody = { readonly "event_type": ProjectEventTypeEnum } +export const PublicTestEndpointBody = Schema.Struct({ + "event_type": Schema.suspend((): Schema.Codec => ProjectEventTypeEnum).annotate({ + "description": "The event type to send as a sample delivery." + }) +}).annotate({ "identifier": "PublicTestEndpointBody" }) +export type BetaPromptCacheOptions = { + readonly "ttl": BetaPromptCacheTTLEnum + readonly "mode": BetaPromptCacheModeEnum + readonly "comparison_response_id"?: string | null +} +export const BetaPromptCacheOptions = Schema.Struct({ + "ttl": Schema.suspend((): Schema.Codec => BetaPromptCacheTTLEnum).annotate({ + "description": "The minimum lifetime applied to each cache breakpoint." + }), + "mode": Schema.suspend((): Schema.Codec => BetaPromptCacheModeEnum).annotate({ + "description": "Whether implicit prompt-cache breakpoints were enabled." + }), + "comparison_response_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The response ID supplied as the prompt cache diagnostics comparison." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Prompt cache options", + "description": + "The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.", + "identifier": "BetaPromptCacheOptions" +}) +export type BetaPromptCacheOptionsParam = { + readonly "ttl"?: BetaPromptCacheTTLEnum + readonly "mode"?: BetaPromptCacheModeEnum +} +export const BetaPromptCacheOptionsParam = Schema.Struct({ + "ttl": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaPromptCacheTTLEnum).annotate({ + "description": + "The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer." + }) + ), + "mode": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaPromptCacheModeEnum).annotate({ + "description": + "Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching." + }) + ) +}).annotate({ + "title": "Prompt cache options", + "description": + "Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.", + "identifier": "BetaPromptCacheOptionsParam" +}) +export type BetaModelIdsResponses = + | BetaModelIdsShared + | "o1-pro" + | "o1-pro-2025-03-19" + | "o3-pro" + | "o3-pro-2025-06-10" + | "o3-deep-research" + | "o3-deep-research-2025-06-26" + | "o4-mini-deep-research" + | "o4-mini-deep-research-2025-06-26" + | "computer-use-preview" + | "computer-use-preview-2025-03-11" + | "gpt-5.5-pro" + | "gpt-5.5-pro-2026-04-23" + | "gpt-5-codex" + | "gpt-5-pro" + | "gpt-5-pro-2025-10-06" + | "gpt-5.1-codex-max" + | "gpt-daybreak-blue-latest" + | "gpt-daybreak-red-latest" + | "gpt-5.6-cyber" + | "gpt-rosalind-research" +export const BetaModelIdsResponses = Schema.Union([ + BetaModelIdsShared, + Schema.Literals([ + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5.5-pro", + "gpt-5.5-pro-2026-04-23", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + "gpt-daybreak-blue-latest", + "gpt-daybreak-red-latest", + "gpt-5.6-cyber", + "gpt-rosalind-research" + ]).annotate({ "title": "ResponsesOnlyModel" }) +]).annotate({ "identifier": "BetaModelIdsResponses" }) +export type BetaTextResponseFormatJsonSchema = { + readonly "type": "json_schema" + readonly "description"?: string + readonly "name": string + readonly "schema": BetaResponseFormatJsonSchemaSchema + readonly "strict"?: boolean | null +} +export const BetaTextResponseFormatJsonSchema = Schema.Struct({ + "type": Schema.Literal("json_schema").annotate({ + "description": "The type of response format being defined. Always `json_schema`." + }), + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + }) + ), + "name": Schema.String.annotate({ + "description": + "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + }), + "schema": BetaResponseFormatJsonSchemaSchema, + "strict": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](https://developers.openai.com/api/docs/guides/structured-outputs).\n" + }), + Schema.Null + ])) +}).annotate({ + "title": "JSON schema", + "description": + "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).\n", + "identifier": "BetaTextResponseFormatJsonSchema" +}) +export type BetaFunctionTool = { + readonly "type": "function" + readonly "name": string + readonly "async"?: boolean + readonly "description"?: string | null + readonly "parameters": { readonly [x: string]: Schema.Json } | null + readonly "output_schema"?: { readonly [x: string]: Schema.Json } | null + readonly "strict": boolean | null + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaFunctionTool = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of the function tool. Always `function`." }), + "name": Schema.String.annotate({ "description": "The name of the function to call." }), + "async": Schema.optionalKey(Schema.Boolean), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "A description of the function. Used by the model to determine whether or not to call the function." + }), + Schema.Null + ]) + ), + "parameters": Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON schema object describing the parameters of the function." + }), + Schema.Null + ]), + "output_schema": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "A JSON schema object describing the JSON value encoded in string outputs for this function." + }), + Schema.Null + ]) + ), + "strict": Schema.Union([ + Schema.Boolean.annotate({ + "description": "Whether strict parameter validation is enforced for this function tool." + }), + Schema.Null + ]), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this function is deferred and loaded via tool search." }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Function", + "description": + "Defines a function in your own code the model can choose to call. Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).", + "identifier": "BetaFunctionTool" +}) +export type BetaApplyPatchToolParam = { + readonly "type": "apply_patch" + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaApplyPatchToolParam = Schema.Struct({ + "type": Schema.Literal("apply_patch").annotate({ "description": "The type of the tool. Always `apply_patch`." }), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Apply patch tool", + "description": "Allows the assistant to create, delete, or update files using unified diffs.", + "identifier": "BetaApplyPatchToolParam" +}) +export type BetaRankingOptions = { + readonly "ranker"?: BetaRankerVersionType + readonly "score_threshold"?: number + readonly "hybrid_search"?: BetaHybridSearchOptions +} +export const BetaRankingOptions = Schema.Struct({ + "ranker": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaRankerVersionType).annotate({ + "description": "The ranker to use for the file search." + }) + ), + "score_threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The score threshold for the file search, a number between 0 and 1. Numbers closer to 1 will attempt to return only the most relevant results, but may return fewer results." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "hybrid_search": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaHybridSearchOptions).annotate({ + "description": + "Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled." + }) + ) +}).annotate({ "identifier": "BetaRankingOptions" }) +export type BetaFilters = BetaComparisonFilter | BetaCompoundFilter +export const BetaFilters = Schema.Union([BetaComparisonFilter, BetaCompoundFilter]).annotate({ + "identifier": "BetaFilters" +}) +export type BetaComputerUsePreviewTool = { + readonly "type": "computer_use_preview" + readonly "environment": BetaComputerEnvironment + readonly "display_width": number + readonly "display_height": number +} +export const BetaComputerUsePreviewTool = Schema.Struct({ + "type": Schema.Literal("computer_use_preview").annotate({ + "description": "The type of the computer use tool. Always `computer_use_preview`." + }), + "environment": Schema.suspend((): Schema.Codec => BetaComputerEnvironment).annotate({ + "description": "The type of computer environment to control." + }), + "display_width": Schema.Number.annotate({ "description": "The width of the computer display." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "display_height": Schema.Number.annotate({ "description": "The height of the computer display." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "Computer use preview", + "description": + "A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).", + "identifier": "BetaComputerUsePreviewTool" +}) +export type BetaWebSearchTool = { + readonly "type": "web_search" | "web_search_2025_08_26" + readonly "external_web_access"?: boolean + readonly "filters"?: { readonly "allowed_domains"?: ReadonlyArray | null } | null + readonly "user_location"?: BetaWebSearchApproximateLocation + readonly "search_context_size"?: "low" | "medium" | "high" +} +export const BetaWebSearchTool = Schema.Struct({ + "type": Schema.Literals(["web_search", "web_search_2025_08_26"]).annotate({ + "description": "The type of the web search tool. One of `web_search` or `web_search_2025_08_26`." + }), + "external_web_access": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Allow live internet access for web search. Defaults to true when omitted. When false, the web search tool runs in offline/cache-only mode and will not fetch new external content." + }) + ), + "filters": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "allowed_domains": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String.annotate({ "description": "Allowed domain for the search." })).annotate({ + "title": "Allowed domains for the search.", + "description": + "Allowed domains for the search. If not provided, all domains are allowed.\nSubdomains of the provided domains are allowed as well.\n\nExample: `[\"pubmed.ncbi.nlm.nih.gov\"]`\n" + }), + Schema.Null + ]) + ) + }).annotate({ "description": "Filters for the search.\n" }), + Schema.Null + ])), + "user_location": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaWebSearchApproximateLocation).annotate({ + "description": + "The approximate location of the user. If omitted or null, defaults to the\nUnited States. To avoid this fallback, pass `{\"type\": \"approximate\"}` without\nlocation fields. To localize results, provide the relevant location fields.\n" + }) + ), + "search_context_size": Schema.optionalKey( + Schema.Literals(["low", "medium", "high"]).annotate({ + "description": + "High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default." + }) + ) +}).annotate({ + "title": "Web search", + "description": + "Search the Internet for sources related to the prompt. Learn more about the\n[web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).\n", + "identifier": "BetaWebSearchTool" +}) +export type BetaMCPTool = { + readonly "type": "mcp" + readonly "server_label": string + readonly "server_url"?: string + readonly "connector_id"?: + | "connector_dropbox" + | "connector_gmail" + | "connector_googlecalendar" + | "connector_googledrive" + | "connector_microsoftteams" + | "connector_outlookcalendar" + | "connector_outlookemail" + | "connector_sharepoint" + readonly "tunnel_id"?: string + readonly "authorization"?: string + readonly "server_description"?: string + readonly "headers"?: { readonly [x: string]: string } | null + readonly "allowed_tools"?: ReadonlyArray | BetaMCPToolFilter | null + readonly "allowed_callers"?: ReadonlyArray | null + readonly "require_approval"?: + | { readonly "always"?: BetaMCPToolFilter; readonly "never"?: BetaMCPToolFilter } + | "always" + | "never" + | null + readonly "defer_loading"?: boolean +} +export const BetaMCPTool = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The type of the MCP tool. Always `mcp`." }), + "server_label": Schema.String.annotate({ + "description": "A label for this MCP server, used to identify it in tool calls.\n" + }), + "server_url": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The URL for the MCP server. One of `server_url`, `connector_id`, or\n`tunnel_id` must be provided.\n", + "format": "uri" + }) + ), + "connector_id": Schema.optionalKey( + Schema.Literals([ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint" + ]).annotate({ + "description": + "Identifier for service connectors, like those available in ChatGPT. One of\n`server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more\nabout service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).\n\nThis field is deprecated for models released after September 1, 2026.\nUse `server_url` to connect to a remote MCP server, or `tunnel_id` to\nconnect through a Secure MCP Tunnel.\n\nCurrently supported `connector_id` values are:\n\n- Dropbox: `connector_dropbox`\n- Gmail: `connector_gmail`\n- Google Calendar: `connector_googlecalendar`\n- Google Drive: `connector_googledrive`\n- Microsoft Teams: `connector_microsoftteams`\n- Outlook Calendar: `connector_outlookcalendar`\n- Outlook Email: `connector_outlookemail`\n- SharePoint: `connector_sharepoint`\n" + }) + ), + "tunnel_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The Secure MCP Tunnel ID to use instead of a direct server URL. One of\n`server_url`, `connector_id`, or `tunnel_id` must be provided.\n" + }).check( + Schema.isPattern(new RegExp("^tunnel_[a-z0-9]{32}$", "u")).annotate({ + "expected": "a string matching the RegExp ^tunnel_[a-z0-9]{32}$" + }) + ) + ), + "authorization": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An OAuth access token that can be used with a remote MCP server, either\nwith a custom MCP server URL or a service connector. Your application\nmust handle the OAuth authorization flow and provide the token here.\n" + }) + ), + "server_description": Schema.optionalKey( + Schema.String.annotate({ "description": "Optional description of the MCP server, used to provide more context.\n" }) + ), + "headers": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.String).annotate({ + "description": "Optional HTTP headers to send to the MCP server. Use for authentication\nor other purposes.\n" + }), + Schema.Null + ]) + ), + "allowed_tools": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Array(Schema.String).annotate({ + "title": "MCP allowed tools", + "description": "A string array of allowed tool names" + }), + BetaMCPToolFilter + ], { mode: "oneOf" }).annotate({ "description": "List of allowed tool names or a filter object.\n" }), + Schema.Null + ]) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ), + "require_approval": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Struct({ + "always": Schema.optionalKey(BetaMCPToolFilter), + "never": Schema.optionalKey(BetaMCPToolFilter) + }).annotate({ + "title": "MCP tool approval filter", + "description": + "Specify which of the MCP server's tools require approval. Can be\n`always`, `never`, or a filter object associated with tools\nthat require approval.\n" + }), + Schema.Literals(["always", "never"]).annotate({ + "title": "MCP tool approval setting", + "description": + "Specify a single approval policy for all tools. One of `always` or\n`never`. When set to `always`, all tools will require approval. When\nset to `never`, all tools will not require approval.\n" + }) + ], { mode: "oneOf" }).annotate({ "description": "Specify which of the MCP server's tools require approval." }), + Schema.Null + ]) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this MCP tool is deferred and discovered via tool search.\n" }) + ) +}).annotate({ + "title": "MCP tool", + "description": + "Give the model access to additional tools via remote Model Context Protocol\n(MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).\n", + "identifier": "BetaMCPTool" +}) +export type BetaContainerNetworkPolicyAllowlistParam = { + readonly "type": "allowlist" + readonly "allowed_domains": ReadonlyArray + readonly "domain_secrets"?: ReadonlyArray +} +export const BetaContainerNetworkPolicyAllowlistParam = Schema.Struct({ + "type": Schema.Literal("allowlist").annotate({ + "description": "Allow outbound network access only to specified domains. Always `allowlist`." + }), + "allowed_domains": Schema.Array(Schema.String).annotate({ + "description": "A list of allowed domains when type is `allowlist`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "domain_secrets": Schema.optionalKey( + Schema.Array(BetaContainerNetworkPolicyDomainSecretParam).annotate({ + "description": "Optional domain-scoped secrets for allowlisted domains." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ) +}).annotate({ "identifier": "BetaContainerNetworkPolicyAllowlistParam" }) +export type BetaImageGenTool = { + readonly "type": "image_generation" + readonly "model"?: + | string + | "gpt-image-1" + | "gpt-image-1-mini" + | "gpt-image-1.5" + | "gpt-image-2" + | "gpt-image-2-2026-04-21" + | "gpt-image-2.5-sunburst" + | "gpt-image-2.5-sunburst-2026-09-08" + | "gpt-image-2.5-flare" + | "gpt-image-2.5-flare-2026-09-08" + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" | "auto" + readonly "output_format"?: "png" | "webp" | "jpeg" + readonly "output_compression"?: number + readonly "moderation"?: "auto" | "low" + readonly "background"?: "transparent" | "opaque" | "auto" + readonly "input_fidelity"?: BetaInputFidelity | null + readonly "input_image_mask"?: { readonly "image_url"?: string; readonly "file_id"?: string } + readonly "partial_images"?: number + readonly "action"?: BetaImageGenActionEnum +} +export const BetaImageGenTool = Schema.Struct({ + "type": Schema.Literal("image_generation").annotate({ + "description": "The type of the image generation tool. Always `image_generation`.\n" + }), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-1.5", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-2.5-sunburst", + "gpt-image-2.5-sunburst-2026-09-08", + "gpt-image-2.5-flare", + "gpt-image-2.5-flare-2026-09-08" + ]).annotate({ + "description": + "The image generation model to use. One of `gpt-image-1`,\n`gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,\n`gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,\n`gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,\n`gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:\n`gpt-image-1`.\n" + }) + ]) + ), + "quality": Schema.optionalKey( + Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": + "The quality of the generated image. The GPT image models support `low`,\n`medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`,\nincluding their `2026-09-08` snapshots, also support `xhigh` and `max`.\nDefault: `auto`.\n" + }) + ), + "size": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024", "auto"])]).annotate({ + "description": + "The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing." + }) + ), + "output_format": Schema.optionalKey( + Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format of the generated image. One of `png`, `webp`, or\n`jpeg`. Default: `png`.\n" + }) + ), + "output_compression": Schema.optionalKey( + Schema.Number.annotate({ "description": "Compression level for the output image. Default: 100.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" })).check( + Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" }) + ) + ), + "moderation": Schema.optionalKey( + Schema.Literals(["auto", "low"]).annotate({ + "description": "Moderation level for the generated image. Default: `auto`.\n" + }) + ), + "background": Schema.optionalKey( + Schema.Literals(["transparent", "opaque", "auto"]).annotate({ + "description": + "Set the background of the generated image. One of `transparent`, `opaque`,\nor `auto`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including\ntheir `2026-09-08` snapshots, support `opaque` and `transparent`\nbackgrounds. Transparent backgrounds are available for supported GPT Image\nmodels. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in\npreview. When using `transparent`, set the output format to `png` or `webp`.\nDefault: `auto`.\n" + }) + ), + "input_fidelity": Schema.optionalKey(Schema.Union([BetaInputFidelity, Schema.Null])), + "input_image_mask": Schema.optionalKey( + Schema.Struct({ + "image_url": Schema.optionalKey(Schema.String.annotate({ "description": "Base64-encoded mask image.\n" })), + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "File ID for the mask image.\n" })) + }).annotate({ + "description": + "Optional mask for inpainting. Contains `image_url`\n(string, optional) and `file_id` (string, optional).\n" + }) + ), + "partial_images": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Number of partial images to generate in streaming mode, from 0 (default value) to 3.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(3).annotate({ "expected": "a value less than or equal to 3" })) + ), + "action": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaImageGenActionEnum).annotate({ + "description": "Whether to generate a new image or edit an existing image. Default: `auto`.\n" + }) + ) +}).annotate({ + "title": "Image generation tool", + "description": "A tool that generates images using the GPT image models.\n", + "identifier": "BetaImageGenTool" +}) +export type BetaInlineSkillParam = { + readonly "type": "inline" + readonly "name": string + readonly "description": string + readonly "source": BetaInlineSkillSourceParam +} +export const BetaInlineSkillParam = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "Defines an inline skill for this request." }), + "name": Schema.String.annotate({ "description": "The name of the skill." }), + "description": Schema.String.annotate({ "description": "The description of the skill." }), + "source": Schema.suspend((): Schema.Codec => BetaInlineSkillSourceParam).annotate({ + "description": "Inline skill payload" + }) +}).annotate({ "identifier": "BetaInlineSkillParam" }) +export type BetaLocalEnvironmentParam = { + readonly "type": "local" + readonly "skills"?: ReadonlyArray +} +export const BetaLocalEnvironmentParam = Schema.Struct({ + "type": Schema.Literal("local").annotate({ "description": "Use a local computer environment." }), + "skills": Schema.optionalKey( + Schema.Array(BetaLocalSkillParam).annotate({ "description": "An optional list of skills." }).check( + Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" }) + ) + ) +}).annotate({ "identifier": "BetaLocalEnvironmentParam" }) +export type BetaCustomGrammarFormatParam = { + readonly "type": "grammar" + readonly "syntax": BetaGrammarSyntax1 + readonly "definition": string +} +export const BetaCustomGrammarFormatParam = Schema.Struct({ + "type": Schema.Literal("grammar").annotate({ "description": "Grammar format. Always `grammar`." }), + "syntax": Schema.suspend((): Schema.Codec => BetaGrammarSyntax1).annotate({ + "description": "The syntax of the grammar definition. One of `lark` or `regex`." + }), + "definition": Schema.String.annotate({ "description": "The grammar definition." }) +}).annotate({ + "title": "Grammar format", + "description": "A grammar defined by the user.", + "identifier": "BetaCustomGrammarFormatParam" +}) +export type BetaFunctionToolParam = { + readonly "name": string + readonly "description"?: string | null + readonly "parameters"?: BetaEmptyModelParam | null + readonly "strict"?: boolean | null + readonly "type": "function" + readonly "async"?: boolean + readonly "output_schema"?: { readonly [x: string]: Schema.Json } | null + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaFunctionToolParam = Schema.Struct({ + "name": Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })).check( + Schema.isPattern(new RegExp("^[a-zA-Z0-9_-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^[a-zA-Z0-9_-]+$" + }) + ), + "description": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "parameters": Schema.optionalKey(Schema.Union([BetaEmptyModelParam, Schema.Null])), + "strict": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enforce strict parameter validation. If omitted, Responses attempts to use strict validation when the schema is compatible, and falls back to non-strict validation otherwise." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("function"), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the tool response can be returned asynchronously versus immediately returned on next response creation." + }) + ), + "output_schema": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "A JSON Schema describing the JSON value encoded in string outputs for this function tool. This does not describe content-array outputs." + }), + Schema.Null + ]) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this function should be deferred and discovered via tool search." + }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaFunctionToolParam" }) +export type BetaToolSearchOutputFunctionToolParam = { + readonly "name": string + readonly "description"?: string | null + readonly "parameters"?: BetaEmptyModelParam | null + readonly "strict"?: boolean | null + readonly "type": "function" + readonly "async"?: boolean + readonly "output_schema"?: { readonly [x: string]: Schema.Json } | null + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaToolSearchOutputFunctionToolParam = Schema.Struct({ + "name": Schema.String.annotate({ "description": "The name of the loaded function tool." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })).check( + Schema.isPattern(new RegExp("^(?![\\s\\S]*[\\r\\n])[a-zA-Z0-9_.-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^(?![\\s\\S]*[\\r\\n])[a-zA-Z0-9_.-]+$" + }) + ), + "description": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "parameters": Schema.optionalKey(Schema.Union([BetaEmptyModelParam, Schema.Null])), + "strict": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to enforce strict parameter validation. If omitted, Responses attempts to use strict validation when the schema is compatible, and falls back to non-strict validation otherwise." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("function"), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the tool response can be returned asynchronously versus immediately returned on next response creation." + }) + ), + "output_schema": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "A JSON Schema describing the JSON value encoded in string outputs for this function tool. This does not describe content-array outputs." + }), + Schema.Null + ]) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this function should be deferred and discovered via tool search." + }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaToolSearchOutputFunctionToolParam" }) +export type BetaToolSearchToolParam = { + readonly "type": "tool_search" + readonly "execution"?: BetaToolSearchExecutionType + readonly "description"?: string | null + readonly "parameters"?: BetaEmptyModelParam | null +} +export const BetaToolSearchToolParam = Schema.Struct({ + "type": Schema.Literal("tool_search").annotate({ "description": "The type of the tool. Always `tool_search`." }), + "execution": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaToolSearchExecutionType).annotate({ + "description": "Whether tool search is executed by the server or by the client." + }) + ), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "Description shown to the model for a client-executed tool search tool." + }), + Schema.Null + ]) + ), + "parameters": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaEmptyModelParam).annotate({ + "description": "Parameter schema for a client-executed tool search tool." + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Tool search tool", + "description": "Hosted or BYOT tool search configuration for deferred tools.", + "identifier": "BetaToolSearchToolParam" +}) +export type BetaWebSearchPreviewTool = { + readonly "type": "web_search_preview" | "web_search_preview_2025_03_11" + readonly "user_location"?: BetaApproximateLocation | null + readonly "search_context_size"?: BetaSearchContextSize + readonly "search_content_types"?: ReadonlyArray +} +export const BetaWebSearchPreviewTool = Schema.Struct({ + "type": Schema.Literals(["web_search_preview", "web_search_preview_2025_03_11"]).annotate({ + "description": "The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`." + }), + "user_location": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaApproximateLocation).annotate({ + "description": + "The approximate location of the user. If omitted or null, defaults to the United States. To avoid this fallback, pass `{\"type\": \"approximate\"}` without location fields. To localize results, provide the relevant location fields." + }), + Schema.Null + ]) + ), + "search_context_size": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaSearchContextSize).annotate({ + "description": + "High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default." + }) + ), + "search_content_types": Schema.optionalKey(Schema.Array(BetaSearchContentType)) +}).annotate({ + "title": "Web search preview", + "description": + "This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).", + "identifier": "BetaWebSearchPreviewTool" +}) +export type BetaToolChoiceParam = + | BetaToolChoiceOptions + | BetaToolChoiceAllowed + | BetaToolChoiceTypes + | BetaToolChoiceFunction + | BetaToolChoiceMCP + | BetaToolChoiceCustom + | BetaSpecificProgrammaticToolCallingParam + | BetaSpecificApplyPatchParam + | BetaSpecificFunctionShellParam +export const BetaToolChoiceParam = Schema.Union([ + BetaToolChoiceOptions, + BetaToolChoiceAllowed, + BetaToolChoiceTypes, + BetaToolChoiceFunction, + BetaToolChoiceMCP, + BetaToolChoiceCustom, + BetaSpecificProgrammaticToolCallingParam, + BetaSpecificApplyPatchParam, + BetaSpecificFunctionShellParam +], { mode: "oneOf" }).annotate({ + "description": + "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n", + "identifier": "BetaToolChoiceParam" +}) +export type BetaInputTextContent = { + readonly "type": "input_text" + readonly "text": string + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointConfig +} +export const BetaInputTextContent = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the input item. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text input to the model." }), + "prompt_cache_breakpoint": Schema.optionalKey(BetaPromptCacheBreakpointConfig) +}).annotate({ + "title": "Input text", + "description": "A text input to the model.", + "identifier": "BetaInputTextContent" +}) +export type BetaInputImageContent = { + readonly "type": "input_image" + readonly "image_url"?: string | null + readonly "file_id"?: string | null + readonly "detail": BetaImageDetail + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointConfig +} +export const BetaInputImageContent = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ + "description": "The type of the input item. Always `input_image`." + }), + "image_url": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.", + "format": "uri" + }), + Schema.Null + ]) + ), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "detail": Schema.suspend((): Schema.Codec => BetaImageDetail).annotate({ + "description": + "The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`." + }), + "prompt_cache_breakpoint": Schema.optionalKey(BetaPromptCacheBreakpointConfig) +}).annotate({ + "title": "Input image", + "description": + "An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).", + "identifier": "BetaInputImageContent" +}) +export type BetaComputerScreenshotContent = { + readonly "type": "computer_screenshot" + readonly "image_url": string | null + readonly "file_id": string | null + readonly "detail": BetaImageDetail + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointConfig +} +export const BetaComputerScreenshotContent = Schema.Struct({ + "type": Schema.Literal("computer_screenshot").annotate({ + "description": + "Specifies the event type. For a computer screenshot, this property is always set to `computer_screenshot`." + }), + "image_url": Schema.Union([ + Schema.String.annotate({ "description": "The URL of the screenshot image.", "format": "uri" }), + Schema.Null + ]), + "file_id": Schema.Union([ + Schema.String.annotate({ "description": "The identifier of an uploaded file that contains the screenshot." }), + Schema.Null + ]), + "detail": Schema.suspend((): Schema.Codec => BetaImageDetail).annotate({ + "description": + "The detail level of the screenshot image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`." + }), + "prompt_cache_breakpoint": Schema.optionalKey(BetaPromptCacheBreakpointConfig) +}).annotate({ + "title": "Computer screenshot", + "description": "A screenshot of a computer.", + "identifier": "BetaComputerScreenshotContent" +}) +export type BetaInputFileContent = { + readonly "type": "input_file" + readonly "file_id"?: string | null + readonly "filename"?: string + readonly "file_data"?: string + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointConfig + readonly "file_url"?: string + readonly "detail"?: BetaFileInputDetail +} +export const BetaInputFileContent = Schema.Struct({ + "type": Schema.Literal("input_file").annotate({ "description": "The type of the input item. Always `input_file`." }), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "filename": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the file to be sent to the model." }) + ), + "file_data": Schema.optionalKey( + Schema.String.annotate({ "description": "The content of the file to be sent to the model.\n" }) + ), + "prompt_cache_breakpoint": Schema.optionalKey(BetaPromptCacheBreakpointConfig), + "file_url": Schema.optionalKey( + Schema.String.annotate({ "description": "The URL of the file to be sent to the model.", "format": "uri" }) + ), + "detail": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaFileInputDetail).annotate({ + "description": + "The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`." + }) + ) +}).annotate({ + "title": "Input file", + "description": "A file input to the model.", + "identifier": "BetaInputFileContent" +}) +export type BetaAccessProgramsParam = { readonly "cyber"?: BetaCyberAccessProgramEnum } +export const BetaAccessProgramsParam = Schema.Struct({ + "cyber": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaCyberAccessProgramEnum).annotate({ + "description": + "The Cyber access program to use for this request. Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If omitted, the API resolves the program from the model's Cyber tier and your organization and project access, subject to model-specific eligibility restrictions. By default, models without a Cyber tier use Standard. Blue-tier models use Daybreak Blue when authorized; otherwise they fall back to Standard unless the model requires Daybreak access. Red-tier models use Daybreak Red and require authorization. Requests that require unavailable Daybreak access return 403. An implicit Standard fallback is represented by null in the response's access_programs field, rather than an explicit Standard selection." + }) + ) +}).annotate({ + "description": "Domain-specific access programs to use for this request.", + "identifier": "BetaAccessProgramsParam" +}) +export type BetaAccessProgramsBody = { readonly "cyber": BetaCyberAccessProgramEnum } +export const BetaAccessProgramsBody = Schema.Struct({ + "cyber": Schema.suspend((): Schema.Codec => BetaCyberAccessProgramEnum).annotate({ + "description": "The effective Cyber access program used for this response." + }) +}).annotate({ "identifier": "BetaAccessProgramsBody" }) +export type BetaReasoning = { + readonly "mode"?: BetaReasoningModeEnum + readonly "effort"?: BetaReasoningEffort + readonly "summary"?: "auto" | "concise" | "detailed" | null + readonly "context"?: "auto" | "current_turn" | "all_turns" | null + readonly "generate_summary"?: "auto" | "concise" | "detailed" | null +} +export const BetaReasoning = Schema.Struct({ + "mode": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaReasoningModeEnum).annotate({ + "description": + "Controls the reasoning execution mode for the request.\n\nWhen returned on a response, this is the effective execution mode.\n" + }) + ), + "effort": Schema.optionalKey(BetaReasoningEffort), + "summary": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "concise", "detailed"]).annotate({ + "description": + "A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n\n`concise` is supported for `computer-use-preview` models and all reasoning models after `gpt-5`.\n" + }), + Schema.Null + ])), + "context": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "current_turn", "all_turns"]).annotate({ + "description": + "Controls which reasoning items are rendered back to the model on later turns.\nIf omitted or set to `auto`, the model determines the context mode. The\n`gpt-5.6` model family defaults to `all_turns`; earlier models default to\n`current_turn`.\n\nWhen returned on a response, this is the effective reasoning context mode\nused for the response.\n" + }), + Schema.Null + ])), + "generate_summary": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["auto", "concise", "detailed"]).annotate({ + "description": + "**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Reasoning", + "description": + "Configuration options for\n[reasoning models](https://developers.openai.com/api/docs/guides/reasoning).\n", + "identifier": "BetaReasoning" +}) +export type BetaProgram = { + readonly "agent"?: BetaAgentTag + readonly "type": "program" + readonly "id": string + readonly "call_id": string + readonly "code": string + readonly "fingerprint": string +} +export const BetaProgram = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("program").annotate({ "description": "The type of the item. Always `program`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the program item." }), + "call_id": Schema.String.annotate({ "description": "The stable call ID of the program item." }), + "code": Schema.String.annotate({ "description": "The JavaScript source executed by programmatic tool calling." }), + "fingerprint": Schema.String.annotate({ + "description": "Opaque program replay fingerprint that must be round-tripped." + }) +}).annotate({ "identifier": "BetaProgram" }) +export type BetaCompactionBody = { + readonly "agent"?: BetaAgentTag + readonly "type": "compaction" + readonly "id": string + readonly "encrypted_content": string + readonly "created_by"?: string +} +export const BetaCompactionBody = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("compaction").annotate({ "description": "The type of the item. Always `compaction`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the compaction item." }), + "encrypted_content": Schema.String.annotate({ + "description": "The encrypted content that was produced by compaction." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ + "title": "Compaction item", + "description": + "A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).", + "identifier": "BetaCompactionBody" +}) +export type BetaLocalShellToolCallOutput = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "local_shell_call_output" + readonly "id": string + readonly "output": string + readonly "status"?: "in_progress" | "completed" | "incomplete" | null + readonly "call_id": Schema.Json +} +export const BetaLocalShellToolCallOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("local_shell_call_output").annotate({ + "description": "The type of the local shell tool call output. Always `local_shell_call_output`.\n" + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "output": Schema.String.annotate({ "description": "A JSON string of the output of the local shell tool call.\n" }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the item. One of `in_progress`, `completed`, or `incomplete`.\n" + }), + Schema.Null + ]) + ), + "call_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "title": "Local shell call output", + "description": "The output of a local shell tool call.\n", + "identifier": "BetaLocalShellToolCallOutput" +}) +export type BetaMCPApprovalRequest = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_approval_request" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string +} +export const BetaMCPApprovalRequest = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_approval_request").annotate({ + "description": "The type of the item. Always `mcp_approval_request`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the approval request.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server making the request.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of arguments for the tool.\n" }) +}).annotate({ + "title": "MCP approval request", + "description": "A request for human approval of a tool invocation.\n", + "identifier": "BetaMCPApprovalRequest" +}) +export type BetaMCPApprovalResponseResource = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_approval_response" + readonly "id": string + readonly "approval_request_id": string + readonly "approve": boolean + readonly "reason"?: string | null + readonly "request_id": Schema.Json +} +export const BetaMCPApprovalResponseResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_approval_response").annotate({ + "description": "The type of the item. Always `mcp_approval_response`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the approval response\n" }), + "approval_request_id": Schema.String.annotate({ "description": "The ID of the approval request being answered.\n" }), + "approve": Schema.Boolean.annotate({ "description": "Whether the request was approved.\n" }), + "reason": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional reason for the decision.\n" }), Schema.Null]) + ), + "request_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "title": "MCP approval response", + "description": "A response to an MCP approval request.\n", + "identifier": "BetaMCPApprovalResponseResource" +}) +export type BetaResponseAudioDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.audio.delta" + readonly "sequence_number": number + readonly "delta": string +} +export const BetaResponseAudioDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.audio.delta").annotate({ + "description": "The type of the event. Always `response.audio.delta`.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "A sequence number for this chunk of the stream response.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "A chunk of Base64 encoded response audio bytes.\n" }) +}).annotate({ + "description": "Emitted when there is a partial audio response.", + "identifier": "BetaResponseAudioDeltaEvent" +}) +export type BetaResponseAudioDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.audio.done" + readonly "sequence_number": number + readonly "response_id": Schema.Json +} +export const BetaResponseAudioDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.audio.done").annotate({ + "description": "The type of the event. Always `response.audio.done`.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the delta.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "description": "Emitted when the audio response is complete.", + "identifier": "BetaResponseAudioDoneEvent" +}) +export type BetaResponseAudioTranscriptDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.audio.transcript.delta" + readonly "delta": string + readonly "sequence_number": number + readonly "response_id": Schema.Json +} +export const BetaResponseAudioTranscriptDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.audio.transcript.delta").annotate({ + "description": "The type of the event. Always `response.audio.transcript.delta`.\n" + }), + "delta": Schema.String.annotate({ "description": "The partial transcript of the audio response.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "description": "Emitted when there is a partial transcript of audio.", + "identifier": "BetaResponseAudioTranscriptDeltaEvent" +}) +export type BetaResponseAudioTranscriptDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.audio.transcript.done" + readonly "sequence_number": number + readonly "response_id": Schema.Json +} +export const BetaResponseAudioTranscriptDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.audio.transcript.done").annotate({ + "description": "The type of the event. Always `response.audio.transcript.done`.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response_id": Schema.Json.annotate({ "expected": "JSON value" }) +}).annotate({ + "description": "Emitted when the full audio transcript is completed.", + "identifier": "BetaResponseAudioTranscriptDoneEvent" +}) +export type BetaResponseCodeInterpreterCallCodeDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.code_interpreter_call_code.delta" + readonly "output_index": number + readonly "item_id": string + readonly "delta": string + readonly "sequence_number": number +} +export const BetaResponseCodeInterpreterCallCodeDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.code_interpreter_call_code.delta").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call_code.delta`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code is being streamed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "delta": Schema.String.annotate({ + "description": "The partial code snippet being streamed by the code interpreter." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a partial code snippet is streamed by the code interpreter.", + "identifier": "BetaResponseCodeInterpreterCallCodeDeltaEvent" +}) +export type BetaResponseCodeInterpreterCallCodeDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.code_interpreter_call_code.done" + readonly "output_index": number + readonly "item_id": string + readonly "code": string + readonly "sequence_number": number +} +export const BetaResponseCodeInterpreterCallCodeDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.code_interpreter_call_code.done").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call_code.done`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code is finalized." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "code": Schema.String.annotate({ "description": "The final code snippet output by the code interpreter." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when the code snippet is finalized by the code interpreter.", + "identifier": "BetaResponseCodeInterpreterCallCodeDoneEvent" +}) +export type BetaResponseCodeInterpreterCallCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.code_interpreter_call.completed" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseCodeInterpreterCallCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.code_interpreter_call.completed").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call.completed`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code interpreter call is completed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when the code interpreter call is completed.", + "identifier": "BetaResponseCodeInterpreterCallCompletedEvent" +}) +export type BetaResponseCodeInterpreterCallInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.code_interpreter_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseCodeInterpreterCallInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.code_interpreter_call.in_progress").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call.in_progress`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code interpreter call is in progress." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a code interpreter call is in progress.", + "identifier": "BetaResponseCodeInterpreterCallInProgressEvent" +}) +export type BetaResponseCodeInterpreterCallInterpretingEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.code_interpreter_call.interpreting" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseCodeInterpreterCallInterpretingEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.code_interpreter_call.interpreting").annotate({ + "description": "The type of the event. Always `response.code_interpreter_call.interpreting`." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response for which the code interpreter is interpreting code." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The unique identifier of the code interpreter tool call item." }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of this event, used to order streaming events." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when the code interpreter is actively interpreting the code snippet.", + "identifier": "BetaResponseCodeInterpreterCallInterpretingEvent" +}) +export type BetaResponseCompactionCompactingStreamingEvent = { + readonly "type": "response.compaction.compacting" + readonly "sequence_number": number + readonly "agent"?: BetaAgentTag + readonly "output_index": number + readonly "item_id": string +} +export const BetaResponseCompactionCompactingStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.compaction.compacting").annotate({ + "description": "The type of the event, always `response.compaction.compacting`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the compaction output item." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ "description": "The ID of the compaction output item." }) +}).annotate({ + "title": "Response compaction compacting event", + "description": "Emitted when new summary content is sampled for a compaction trigger. Contains no summary content.", + "identifier": "BetaResponseCompactionCompactingStreamingEvent" +}) +export type BetaResponseErrorEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "error" + readonly "code": string | null + readonly "message": string + readonly "param": string | null + readonly "sequence_number": number +} +export const BetaResponseErrorEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("error").annotate({ "description": "The type of the event. Always `error`.\n" }), + "code": Schema.Union([Schema.String.annotate({ "description": "The error code.\n" }), Schema.Null]), + "message": Schema.String.annotate({ "description": "The error message.\n" }), + "param": Schema.Union([Schema.String.annotate({ "description": "The error parameter.\n" }), Schema.Null]), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when an error occurs.", "identifier": "BetaResponseErrorEvent" }) +export type BetaResponseFileSearchCallCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.file_search_call.completed" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseFileSearchCallCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.file_search_call.completed").annotate({ + "description": "The type of the event. Always `response.file_search_call.completed`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the file search call is initiated.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the file search call is initiated.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a file search call is completed (results found).", + "identifier": "BetaResponseFileSearchCallCompletedEvent" +}) +export type BetaResponseFileSearchCallInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.file_search_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseFileSearchCallInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.file_search_call.in_progress").annotate({ + "description": "The type of the event. Always `response.file_search_call.in_progress`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the file search call is initiated.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the file search call is initiated.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a file search call is initiated.", + "identifier": "BetaResponseFileSearchCallInProgressEvent" +}) +export type BetaResponseFileSearchCallSearchingEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.file_search_call.searching" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseFileSearchCallSearchingEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.file_search_call.searching").annotate({ + "description": "The type of the event. Always `response.file_search_call.searching`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the file search call is searching.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the file search call is initiated.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a file search is currently searching.", + "identifier": "BetaResponseFileSearchCallSearchingEvent" +}) +export type BetaResponseFunctionCallArgumentsDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.function_call_arguments.delta" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number + readonly "delta": string +} +export const BetaResponseFunctionCallArgumentsDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.function_call_arguments.delta").annotate({ + "description": "The type of the event. Always `response.function_call_arguments.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the function-call arguments delta is added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the function-call arguments delta is added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "delta": Schema.String.annotate({ "description": "The function-call arguments delta that is added.\n" }) +}).annotate({ + "description": "Emitted when there is a partial function-call arguments delta.", + "identifier": "BetaResponseFunctionCallArgumentsDeltaEvent" +}) +export type BetaResponseFunctionCallArgumentsDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.function_call_arguments.done" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number + readonly "arguments": string +} +export const BetaResponseFunctionCallArgumentsDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.function_call_arguments.done"), + "item_id": Schema.String.annotate({ "description": "The ID of the item." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "arguments": Schema.String.annotate({ "description": "The function-call arguments." }) +}).annotate({ + "description": "Emitted when function-call arguments are finalized.", + "identifier": "BetaResponseFunctionCallArgumentsDoneEvent" +}) +export type BetaResponseShellCallCommandAddedStreamingEvent = { + readonly "type": "response.shell_call_command.added" + readonly "sequence_number": number + readonly "agent"?: BetaAgentTag + readonly "output_index": number + readonly "command_index": number + readonly "command": string +} +export const BetaResponseShellCallCommandAddedStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_command.added").annotate({ + "description": "The type of the event, always `response.shell_call_command.added`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that was added." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command": Schema.String.annotate({ "description": "The shell command that was added." }) +}).annotate({ + "title": "Response shell command added event", + "description": "A streaming event that indicated a shell command was added to a tool call.", + "identifier": "BetaResponseShellCallCommandAddedStreamingEvent" +}) +export type BetaResponseShellCallCommandDeltaStreamingEvent = { + readonly "type": "response.shell_call_command.delta" + readonly "sequence_number": number + readonly "agent"?: BetaAgentTag + readonly "output_index": number + readonly "command_index": number + readonly "delta": string + readonly "obfuscation"?: string +} +export const BetaResponseShellCallCommandDeltaStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_command.delta").annotate({ + "description": "The type of the event, always `response.shell_call_command.delta`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "delta": Schema.String.annotate({ "description": "The shell command delta that was appended." }), + "obfuscation": Schema.optionalKey( + Schema.String.annotate({ "description": "An obfuscation string that was added to pad the event payload." }) + ) +}).annotate({ + "title": "Response shell command delta event", + "description": "A streaming event that indicated a shell command was incrementally updated.", + "identifier": "BetaResponseShellCallCommandDeltaStreamingEvent" +}) +export type BetaResponseShellCallCommandDoneStreamingEvent = { + readonly "type": "response.shell_call_command.done" + readonly "sequence_number": number + readonly "agent"?: BetaAgentTag + readonly "output_index": number + readonly "command_index": number + readonly "command": string +} +export const BetaResponseShellCallCommandDoneStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_command.done").annotate({ + "description": "The type of the event, always `response.shell_call_command.done`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that was completed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "command": Schema.String.annotate({ "description": "The final shell command that was emitted." }) +}).annotate({ + "title": "Response shell command done event", + "description": "A streaming event that indicated a shell command was completed.", + "identifier": "BetaResponseShellCallCommandDoneStreamingEvent" +}) +export type BetaResponseReasoningSummaryPartAddedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.reasoning_summary_part.added" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "sequence_number": number + readonly "part": { readonly "type": "summary_text"; readonly "text": string } +} +export const BetaResponseReasoningSummaryPartAddedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.reasoning_summary_part.added").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_part.added`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this summary part is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary part is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ + "description": "The type of the summary part. Always `summary_text`." + }), + "text": Schema.String.annotate({ "description": "The text of the summary part." }) + }).annotate({ "description": "The summary part that was added.\n" }) +}).annotate({ + "description": "Emitted when a new reasoning summary part is added.", + "identifier": "BetaResponseReasoningSummaryPartAddedEvent" +}) +export type BetaResponseReasoningSummaryPartDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.reasoning_summary_part.done" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "status"?: "incomplete" + readonly "sequence_number": number + readonly "part": { readonly "type": "summary_text"; readonly "text": string } +} +export const BetaResponseReasoningSummaryPartDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.reasoning_summary_part.done").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_part.done`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this summary part is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary part is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "status": Schema.optionalKey( + Schema.Literal("incomplete").annotate({ + "description": + "The completion status of the summary part. Omitted when the part completed\nnormally and set to `incomplete` when generation was interrupted.\n" + }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.Struct({ + "type": Schema.Literal("summary_text").annotate({ + "description": "The type of the summary part. Always `summary_text`." + }), + "text": Schema.String.annotate({ "description": "The text of the summary part." }) + }).annotate({ "description": "The completed summary part.\n" }) +}).annotate({ + "description": "Emitted when a reasoning summary part is completed.", + "identifier": "BetaResponseReasoningSummaryPartDoneEvent" +}) +export type BetaResponseReasoningSummaryTextDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.reasoning_summary_text.delta" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "delta": string + readonly "sequence_number": number +} +export const BetaResponseReasoningSummaryTextDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.reasoning_summary_text.delta").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_text.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the item this summary text delta is associated with.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary text delta is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The text delta that was added to the summary.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a delta is added to a reasoning summary text.", + "identifier": "BetaResponseReasoningSummaryTextDeltaEvent" +}) +export type BetaResponseReasoningSummaryTextDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.reasoning_summary_text.done" + readonly "item_id": string + readonly "output_index": number + readonly "summary_index": number + readonly "text": string + readonly "sequence_number": number +} +export const BetaResponseReasoningSummaryTextDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.reasoning_summary_text.done").annotate({ + "description": "The type of the event. Always `response.reasoning_summary_text.done`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this summary text is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this summary text is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "summary_index": Schema.Number.annotate({ + "description": "The index of the summary part within the reasoning summary.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "text": Schema.String.annotate({ "description": "The full text of the completed reasoning summary.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a reasoning summary text is completed.", + "identifier": "BetaResponseReasoningSummaryTextDoneEvent" +}) +export type BetaResponseReasoningTextDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.reasoning_text.delta" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string + readonly "sequence_number": number +} +export const BetaResponseReasoningTextDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.reasoning_text.delta").annotate({ + "description": "The type of the event. Always `response.reasoning_text.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the item this reasoning text delta is associated with.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this reasoning text delta is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the reasoning content part this delta is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The text delta that was added to the reasoning content.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a delta is added to a reasoning text.", + "identifier": "BetaResponseReasoningTextDeltaEvent" +}) +export type BetaResponseReasoningTextDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.reasoning_text.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "text": string + readonly "sequence_number": number +} +export const BetaResponseReasoningTextDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.reasoning_text.done").annotate({ + "description": "The type of the event. Always `response.reasoning_text.done`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the item this reasoning text is associated with.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item this reasoning text is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the reasoning content part.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "text": Schema.String.annotate({ "description": "The full text of the completed reasoning content.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a reasoning text is completed.", + "identifier": "BetaResponseReasoningTextDoneEvent" +}) +export type BetaResponseRefusalDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.refusal.delta" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string + readonly "sequence_number": number +} +export const BetaResponseRefusalDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.refusal.delta").annotate({ + "description": "The type of the event. Always `response.refusal.delta`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the refusal text is added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the refusal text is added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the refusal text is added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The refusal text that is added.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when there is a partial refusal text.", + "identifier": "BetaResponseRefusalDeltaEvent" +}) +export type BetaResponseRefusalDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.refusal.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "refusal": string + readonly "sequence_number": number +} +export const BetaResponseRefusalDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.refusal.done").annotate({ + "description": "The type of the event. Always `response.refusal.done`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the refusal text is finalized.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the refusal text is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the refusal text is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "refusal": Schema.String.annotate({ "description": "The refusal text that is finalized.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when refusal text is finalized.", "identifier": "BetaResponseRefusalDoneEvent" }) +export type BetaResponseWebSearchCallCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.web_search_call.completed" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseWebSearchCallCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.web_search_call.completed").annotate({ + "description": "The type of the event. Always `response.web_search_call.completed`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the web search call is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "Unique ID for the output item associated with the web search call.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the web search call being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a web search call is completed.", + "identifier": "BetaResponseWebSearchCallCompletedEvent" +}) +export type BetaResponseWebSearchCallInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.web_search_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseWebSearchCallInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.web_search_call.in_progress").annotate({ + "description": "The type of the event. Always `response.web_search_call.in_progress`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the web search call is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "Unique ID for the output item associated with the web search call.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the web search call being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a web search call is initiated.", + "identifier": "BetaResponseWebSearchCallInProgressEvent" +}) +export type BetaResponseWebSearchCallSearchingEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.web_search_call.searching" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseWebSearchCallSearchingEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.web_search_call.searching").annotate({ + "description": "The type of the event. Always `response.web_search_call.searching`.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the web search call is associated with.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "Unique ID for the output item associated with the web search call.\n" + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the web search call being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Emitted when a web search call is executing.", + "identifier": "BetaResponseWebSearchCallSearchingEvent" +}) +export type BetaResponseImageGenCallCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.image_generation_call.completed" + readonly "output_index": number + readonly "sequence_number": number + readonly "item_id": string +} +export const BetaResponseImageGenCallCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.image_generation_call.completed").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.completed'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }) +}).annotate({ + "title": "ResponseImageGenCallCompletedEvent", + "description": "Emitted when an image generation tool call has completed and the final image is available.\n", + "identifier": "BetaResponseImageGenCallCompletedEvent" +}) +export type BetaResponseImageGenCallGeneratingEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.image_generation_call.generating" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseImageGenCallGeneratingEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.image_generation_call.generating").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.generating'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the image generation item being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "ResponseImageGenCallGeneratingEvent", + "description": "Emitted when an image generation tool call is actively generating an image (intermediate state).\n", + "identifier": "BetaResponseImageGenCallGeneratingEvent" +}) +export type BetaResponseImageGenCallInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.image_generation_call.in_progress" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number +} +export const BetaResponseImageGenCallInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.image_generation_call.in_progress").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.in_progress'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the image generation item being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "title": "ResponseImageGenCallInProgressEvent", + "description": "Emitted when an image generation tool call is in progress.\n", + "identifier": "BetaResponseImageGenCallInProgressEvent" +}) +export type BetaResponseImageGenCallPartialImageEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.image_generation_call.partial_image" + readonly "output_index": number + readonly "item_id": string + readonly "sequence_number": number + readonly "partial_image_index": number + readonly "partial_image_b64": string + readonly "size"?: string + readonly "quality"?: string + readonly "background"?: string + readonly "output_format"?: string +} +export const BetaResponseImageGenCallPartialImageEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.image_generation_call.partial_image").annotate({ + "description": "The type of the event. Always 'response.image_generation_call.partial_image'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the image generation item being processed." + }), + "sequence_number": Schema.Number.annotate({ + "description": "The sequence number of the image generation item being processed." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "partial_image_index": Schema.Number.annotate({ + "description": "0-based index for the partial image (backend is 1-based, but this is 0-based for the user)." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "partial_image_b64": Schema.String.annotate({ + "description": "Base64-encoded partial image data, suitable for rendering as an image." + }), + "size": Schema.optionalKey(Schema.String.annotate({ "description": "The image size that was used." })), + "quality": Schema.optionalKey(Schema.String.annotate({ "description": "The image quality that was used." })), + "background": Schema.optionalKey(Schema.String.annotate({ "description": "The background setting that was used." })), + "output_format": Schema.optionalKey(Schema.String.annotate({ "description": "The output format that was used." })) +}).annotate({ + "title": "ResponseImageGenCallPartialImageEvent", + "description": "Emitted when a partial image is available during image generation streaming.\n", + "identifier": "BetaResponseImageGenCallPartialImageEvent" +}) +export type BetaResponseMCPCallArgumentsDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_call_arguments.delta" + readonly "output_index": number + readonly "item_id": string + readonly "delta": string + readonly "sequence_number": number +} +export const BetaResponseMCPCallArgumentsDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_call_arguments.delta").annotate({ + "description": "The type of the event. Always 'response.mcp_call_arguments.delta'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the MCP tool call item being processed." + }), + "delta": Schema.String.annotate({ + "description": "A JSON string containing the partial update to the arguments for the MCP tool call.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallArgumentsDeltaEvent", + "description": "Emitted when there is a delta (partial update) to the arguments of an MCP tool call.\n", + "identifier": "BetaResponseMCPCallArgumentsDeltaEvent" +}) +export type BetaResponseMCPCallArgumentsDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_call_arguments.done" + readonly "output_index": number + readonly "item_id": string + readonly "arguments": string + readonly "sequence_number": number +} +export const BetaResponseMCPCallArgumentsDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_call_arguments.done").annotate({ + "description": "The type of the event. Always 'response.mcp_call_arguments.done'." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the MCP tool call item being processed." + }), + "arguments": Schema.String.annotate({ + "description": "A JSON string containing the finalized arguments for the MCP tool call.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallArgumentsDoneEvent", + "description": "Emitted when the arguments for an MCP tool call are finalized.\n", + "identifier": "BetaResponseMCPCallArgumentsDoneEvent" +}) +export type BetaResponseMCPCallCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_call.completed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const BetaResponseMCPCallCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_call.completed").annotate({ + "description": "The type of the event. Always 'response.mcp_call.completed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that completed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that completed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallCompletedEvent", + "description": "Emitted when an MCP tool call has completed successfully.\n", + "identifier": "BetaResponseMCPCallCompletedEvent" +}) +export type BetaResponseMCPCallFailedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_call.failed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const BetaResponseMCPCallFailedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_call.failed").annotate({ + "description": "The type of the event. Always 'response.mcp_call.failed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that failed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that failed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPCallFailedEvent", + "description": "Emitted when an MCP tool call has failed.\n", + "identifier": "BetaResponseMCPCallFailedEvent" +}) +export type BetaResponseMCPCallInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_call.in_progress" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string +} +export const BetaResponseMCPCallInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_call.in_progress").annotate({ + "description": "The type of the event. Always 'response.mcp_call.in_progress'." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the MCP tool call item being processed." + }) +}).annotate({ + "title": "ResponseMCPCallInProgressEvent", + "description": "Emitted when an MCP tool call is in progress.\n", + "identifier": "BetaResponseMCPCallInProgressEvent" +}) +export type BetaResponseMCPListToolsCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_list_tools.completed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const BetaResponseMCPListToolsCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_list_tools.completed").annotate({ + "description": "The type of the event. Always 'response.mcp_list_tools.completed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that produced this output." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was processed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPListToolsCompletedEvent", + "description": "Emitted when the list of available MCP tools has been successfully retrieved.\n", + "identifier": "BetaResponseMCPListToolsCompletedEvent" +}) +export type BetaResponseMCPListToolsFailedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_list_tools.failed" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const BetaResponseMCPListToolsFailedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_list_tools.failed").annotate({ + "description": "The type of the event. Always 'response.mcp_list_tools.failed'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that failed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that failed." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPListToolsFailedEvent", + "description": "Emitted when the attempt to list available MCP tools has failed.\n", + "identifier": "BetaResponseMCPListToolsFailedEvent" +}) +export type BetaResponseMCPListToolsInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.mcp_list_tools.in_progress" + readonly "item_id": string + readonly "output_index": number + readonly "sequence_number": number +} +export const BetaResponseMCPListToolsInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.mcp_list_tools.in_progress").annotate({ + "description": "The type of the event. Always 'response.mcp_list_tools.in_progress'." + }), + "item_id": Schema.String.annotate({ "description": "The ID of the MCP tool call item that is being processed." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that is being processed." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseMCPListToolsInProgressEvent", + "description": "Emitted when the system is in the process of retrieving the list of available MCP tools.\n", + "identifier": "BetaResponseMCPListToolsInProgressEvent" +}) +export type BetaResponseCustomToolCallInputDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.custom_tool_call_input.delta" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string + readonly "delta": string +} +export const BetaResponseCustomToolCallInputDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.custom_tool_call_input.delta").annotate({ + "description": "The event type identifier." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output this delta applies to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "Unique identifier for the API item associated with this event." + }), + "delta": Schema.String.annotate({ "description": "The incremental input data (delta) for the custom tool call." }) +}).annotate({ + "title": "ResponseCustomToolCallInputDelta", + "description": "Event representing a delta (partial update) to the input of a custom tool call.\n", + "identifier": "BetaResponseCustomToolCallInputDeltaEvent" +}) +export type BetaResponseCustomToolCallInputDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.custom_tool_call_input.done" + readonly "sequence_number": number + readonly "output_index": number + readonly "item_id": string + readonly "input": string +} +export const BetaResponseCustomToolCallInputDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.custom_tool_call_input.done").annotate({ + "description": "The event type identifier." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "output_index": Schema.Number.annotate({ "description": "The index of the output this event applies to." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item_id": Schema.String.annotate({ + "description": "Unique identifier for the API item associated with this event." + }), + "input": Schema.String.annotate({ "description": "The complete input data for the custom tool call." }) +}).annotate({ + "title": "ResponseCustomToolCallInputDone", + "description": "Event indicating that input for a custom tool call is complete.\n", + "identifier": "BetaResponseCustomToolCallInputDoneEvent" +}) +export type BetaResponseConfigurationUpdate = { + readonly "agent"?: BetaAgentTag + readonly "id": string + readonly "type": "configuration_update" + readonly "reasoning"?: { readonly "effort"?: BetaReasoningEffort } +} +export const BetaResponseConfigurationUpdate = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the configuration update item." }), + "type": Schema.Literal("configuration_update").annotate({ + "description": "The item type. Always `configuration_update`." + }), + "reasoning": Schema.optionalKey( + Schema.Struct({ + "effort": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaReasoningEffort).annotate({ + "description": + "The reasoning effort used for subsequent responses until another\nconfiguration update replaces it.\n" + }) + ) + }).annotate({ "description": "The reasoning configuration applied by this update." }) + ) +}).annotate({ + "description": + "A configuration update that applies to subsequent responses until it is\nreplaced by another configuration update.\n", + "identifier": "BetaResponseConfigurationUpdate" +}) +export type BetaAnnotation = BetaFileCitationBody | BetaUrlCitationBody | BetaContainerFileCitationBody | BetaFilePath +export const BetaAnnotation = Schema.Union([ + BetaFileCitationBody, + BetaUrlCitationBody, + BetaContainerFileCitationBody, + BetaFilePath +], { mode: "oneOf" }).annotate({ + "description": "An annotation that applies to a span of output text.", + "identifier": "BetaAnnotation" +}) +export type BetaLogProb = { + readonly "token": string + readonly "logprob": number + readonly "bytes": ReadonlyArray + readonly "top_logprobs": ReadonlyArray +} +export const BetaLogProb = Schema.Struct({ + "token": Schema.String, + "logprob": Schema.Finite, + "bytes": Schema.Array(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "top_logprobs": Schema.Array(BetaTopLogProb) +}).annotate({ + "title": "Log probability", + "description": "The log probability of a token.", + "identifier": "BetaLogProb" +}) +export type BetaFileSearchToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "file_search_call" + readonly "status": "in_progress" | "searching" | "completed" | "incomplete" | "failed" + readonly "queries": ReadonlyArray + readonly "results"?: + | ReadonlyArray< + { + readonly "file_id"?: string + readonly "text"?: string + readonly "filename"?: string + readonly "attributes"?: BetaVectorStoreFileAttributes + readonly "score"?: number + } + > + | null +} +export const BetaFileSearchToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the file search tool call.\n" }), + "type": Schema.Literal("file_search_call").annotate({ + "description": "The type of the file search tool call. Always `file_search_call`.\n" + }), + "status": Schema.Literals(["in_progress", "searching", "completed", "incomplete", "failed"]).annotate({ + "description": + "The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n" + }), + "queries": Schema.Array(Schema.String).annotate({ "description": "The queries used to search for files.\n" }), + "results": Schema.optionalKey(Schema.Union([ + Schema.Array(Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the file.\n" })), + "text": Schema.optionalKey( + Schema.String.annotate({ "description": "The text that was retrieved from the file.\n" }) + ), + "filename": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the file.\n" })), + "attributes": Schema.optionalKey(BetaVectorStoreFileAttributes), + "score": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The relevance score of the file - a value between 0 and 1.\n", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ) + })).annotate({ "description": "The results of the file search tool call.\n" }), + Schema.Null + ])) +}).annotate({ + "title": "File search tool call", + "description": + "The results of a file search tool call. See the\n[file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.\n", + "identifier": "BetaFileSearchToolCall" +}) +export type BetaClickParam = { + readonly "type": "click" + readonly "button": BetaClickButtonType + readonly "x": number + readonly "y": number + readonly "keys"?: ReadonlyArray | null +} +export const BetaClickParam = Schema.Struct({ + "type": Schema.Literal("click").annotate({ + "description": "Specifies the event type. For a click action, this property is always `click`." + }), + "button": Schema.suspend((): Schema.Codec => BetaClickButtonType).annotate({ + "description": + "Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`." + }), + "x": Schema.Number.annotate({ "description": "The x-coordinate where the click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "y": Schema.Number.annotate({ "description": "The y-coordinate where the click occurred." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while clicking." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Click", "description": "A click action.", "identifier": "BetaClickParam" }) +export type BetaDragParam = { + readonly "type": "drag" + readonly "path": ReadonlyArray + readonly "keys"?: ReadonlyArray | null +} +export const BetaDragParam = Schema.Struct({ + "type": Schema.Literal("drag").annotate({ + "description": "Specifies the event type. For a drag action, this property is always set to `drag`." + }), + "path": Schema.Array(BetaCoordParam).annotate({ + "description": + "An array of coordinates representing the path of the drag action. Coordinates will appear as an array of objects, eg\n```\n[\n { x: 100, y: 200 },\n { x: 200, y: 300 }\n]\n```" + }), + "keys": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ "description": "The keys being held while dragging the mouse." }), + Schema.Null + ]) + ) +}).annotate({ "title": "Drag", "description": "A drag action.", "identifier": "BetaDragParam" }) +export type BetaCompactionTriggerItemParam = { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "type": "compaction_trigger" +} +export const BetaCompactionTriggerItemParam = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The unique ID of this compaction trigger." }), Schema.Null]) + ), + "type": Schema.Literal("compaction_trigger").annotate({ + "description": "The type of the item. Always `compaction_trigger`." + }) +}).annotate({ + "title": "Compaction trigger", + "description": "Compacts the current context. Must be the final input item.", + "identifier": "BetaCompactionTriggerItemParam" +}) +export type BetaItemReferenceParam = { + readonly "agent"?: Beta_AgentTagParam | null + readonly "type"?: "item_reference" | null + readonly "id": string +} +export const BetaItemReferenceParam = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "type": Schema.optionalKey( + Schema.Union([ + Schema.Literal("item_reference").annotate({ + "description": "The type of item to reference. Always `item_reference`." + }), + Schema.Null + ]) + ), + "id": Schema.String.annotate({ "description": "The ID of the item to reference." }) +}).annotate({ + "title": "Item reference", + "description": "An internal identifier for an item to reference.", + "identifier": "BetaItemReferenceParam" +}) +export type BetaProgramItemParam = { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id": string + readonly "type": "program" + readonly "call_id": string + readonly "code": string + readonly "fingerprint": string +} +export const BetaProgramItemParam = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.String.annotate({ "description": "The unique ID of this program item." }), + "type": Schema.Literal("program").annotate({ "description": "The item type. Always `program`." }), + "call_id": Schema.String.annotate({ "description": "The stable call ID of the program item." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "code": Schema.String.annotate({ "description": "The JavaScript source executed by programmatic tool calling." }) + .check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })), + "fingerprint": Schema.String.annotate({ + "description": "Opaque program replay fingerprint that must be round-tripped." + }).check(Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" })) +}).annotate({ "identifier": "BetaProgramItemParam" }) +export type BetaComputerToolCallOutputResource = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "computer_call_output" + readonly "id": string + readonly "call_id": string + readonly "acknowledged_safety_checks"?: ReadonlyArray + readonly "output": BetaComputerScreenshotImage + readonly "status": "completed" | "incomplete" + readonly "created_by"?: string +} +export const BetaComputerToolCallOutputResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("computer_call_output").annotate({ + "description": "The type of the computer tool call output. Always `computer_call_output`.\n" + }), + "id": Schema.String.annotate({ "description": "The ID of the computer tool call output.\n" }), + "call_id": Schema.String.annotate({ "description": "The ID of the computer tool call that produced the output.\n" }), + "acknowledged_safety_checks": Schema.optionalKey( + Schema.Array(BetaComputerCallSafetyCheckParam).annotate({ + "description": "The safety checks reported by the API that have been acknowledged by the\ndeveloper.\n" + }) + ), + "output": BetaComputerScreenshotImage, + "status": Schema.Literals(["completed", "incomplete"]).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "identifier": "BetaComputerToolCallOutputResource" }) +export type BetaWebSearchToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "web_search_call" + readonly "status": BetaWebSearchCallStatus + readonly "action"?: BetaWebSearchActionSearch | BetaWebSearchActionOpenPage | BetaWebSearchActionFind +} +export const BetaWebSearchToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the web search tool call.\n" }), + "type": Schema.Literal("web_search_call").annotate({ + "description": "The type of the web search tool call. Always `web_search_call`.\n" + }), + "status": Schema.suspend((): Schema.Codec => BetaWebSearchCallStatus).annotate({ + "description": "The status of the web search tool call.\n" + }), + "action": Schema.optionalKey( + Schema.Union([BetaWebSearchActionSearch, BetaWebSearchActionOpenPage, BetaWebSearchActionFind], { mode: "oneOf" }) + .annotate({ + "description": + "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find_in_page).\n" + }) + ) +}).annotate({ + "title": "Web search tool call", + "description": + "The results of a web search tool call. See the\n[web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.\n", + "identifier": "BetaWebSearchToolCall" +}) +export type BetaToolCallCaller = BetaDirectToolCallCaller | BetaProgramToolCallCaller +export const BetaToolCallCaller = Schema.Union([BetaDirectToolCallCaller, BetaProgramToolCallCaller], { mode: "oneOf" }) + .annotate({ + "description": "The execution context that produced this tool call.", + "identifier": "BetaToolCallCaller" + }) +export type BetaInputTextContentParam = { + readonly "type": "input_text" + readonly "text": string + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointParam | null +} +export const BetaInputTextContentParam = Schema.Struct({ + "type": Schema.Literal("input_text").annotate({ "description": "The type of the input item. Always `input_text`." }), + "text": Schema.String.annotate({ "description": "The text input to the model." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "prompt_cache_breakpoint": Schema.optionalKey(Schema.Union([BetaPromptCacheBreakpointParam, Schema.Null])) +}).annotate({ + "title": "Input text", + "description": "A text input to the model.", + "identifier": "BetaInputTextContentParam" +}) +export type BetaInputImageContentParamAutoParam = { + readonly "type": "input_image" + readonly "image_url"?: string | null + readonly "file_id"?: string | null + readonly "detail"?: BetaDetailEnum | null + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointParam | null +} +export const BetaInputImageContentParamAutoParam = Schema.Struct({ + "type": Schema.Literal("input_image").annotate({ + "description": "The type of the input item. Always `input_image`." + }), + "image_url": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.", + "format": "uri" + }).check(Schema.isMaxCodePoints(20971520).annotate({ "expected": "a string with at most 20971520 code points" })), + Schema.Null + ]) + ), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "detail": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaDetailEnum).annotate({ + "description": + "The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`." + }), + Schema.Null + ]) + ), + "prompt_cache_breakpoint": Schema.optionalKey(Schema.Union([BetaPromptCacheBreakpointParam, Schema.Null])) +}).annotate({ + "title": "Input image", + "description": + "An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)", + "identifier": "BetaInputImageContentParamAutoParam" +}) +export type BetaInputFileContentParam = { + readonly "type": "input_file" + readonly "file_id"?: string | null + readonly "filename"?: string | null + readonly "file_data"?: string | null + readonly "file_url"?: string | null + readonly "detail"?: BetaFileDetailEnum + readonly "prompt_cache_breakpoint"?: BetaPromptCacheBreakpointParam | null +} +export const BetaInputFileContentParam = Schema.Struct({ + "type": Schema.Literal("input_file").annotate({ "description": "The type of the input item. Always `input_file`." }), + "file_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the file to be sent to the model." }), + Schema.Null + ]) + ), + "filename": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the file to be sent to the model." }), + Schema.Null + ]) + ), + "file_data": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The base64-encoded data of the file to be sent to the model." }).check( + Schema.isMaxCodePoints(73400320).annotate({ "expected": "a string with at most 73400320 code points" }) + ), + Schema.Null + ]) + ), + "file_url": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The URL of the file to be sent to the model.", "format": "uri" }), + Schema.Null + ]) + ), + "detail": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaFileDetailEnum).annotate({ + "description": + "The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`." + }) + ), + "prompt_cache_breakpoint": Schema.optionalKey(Schema.Union([BetaPromptCacheBreakpointParam, Schema.Null])) +}).annotate({ + "title": "Input file", + "description": "A file input to the model.", + "identifier": "BetaInputFileContentParam" +}) +export type BetaToolCallCallerParam = BetaDirectToolCallCallerParam | BetaProgramToolCallCallerParam +export const BetaToolCallCallerParam = Schema.Union([BetaDirectToolCallCallerParam, BetaProgramToolCallCallerParam], { + mode: "oneOf" +}).annotate({ + "description": "The execution context that produced this tool call.", + "identifier": "BetaToolCallCallerParam" +}) +export type BetaOutputTextContentParam = { + readonly "type": "output_text" + readonly "text": string + readonly "annotations"?: ReadonlyArray +} +export const BetaOutputTextContentParam = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ "description": "The content type. Always `output_text`." }), + "text": Schema.String.annotate({ "description": "The text content." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "annotations": Schema.optionalKey( + Schema.Array( + Schema.Union([BetaFileCitationParam, BetaUrlCitationParam, BetaContainerFileCitationParam], { mode: "oneOf" }) + ).annotate({ "description": "Citations associated with the text content." }) + ) +}).annotate({ "identifier": "BetaOutputTextContentParam" }) +export type BetaReasoningItem = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "reasoning" + readonly "id": string + readonly "encrypted_content"?: string | null + readonly "summary": ReadonlyArray + readonly "content"?: ReadonlyArray + readonly "status"?: "in_progress" | "completed" | "incomplete" +} +export const BetaReasoningItem = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("reasoning").annotate({ "description": "The type of the object. Always `reasoning`.\n" }), + "id": Schema.String.annotate({ "description": "The unique identifier of the reasoning content.\n" }), + "encrypted_content": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The encrypted content of the reasoning item. This is populated by default\nfor reasoning items returned by `POST /v1/responses` and WebSocket\n`response.create` requests.\n\nWhen streaming, use the completed reasoning item and its\n`encrypted_content` from the `response.output_item.done` event in\nsubsequent requests. The `encrypted_content` in\n`response.output_item.added` may be incomplete. This is especially\nimportant when `store` is `false` or when using Zero Data Retention.\n" + }), + Schema.Null + ])), + "summary": Schema.Array(BetaSummaryTextContent).annotate({ "description": "Reasoning summary content.\n" }), + "content": Schema.optionalKey( + Schema.Array(BetaReasoningTextContent).annotate({ "description": "Reasoning text content.\n" }) + ), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ) +}).annotate({ + "title": "Reasoning", + "description": + "A description of the chain of thought used by a reasoning model while generating\na response. Be sure to include these items in your `input` to the Responses API\nfor subsequent turns of a conversation if you are manually\n[managing context](https://developers.openai.com/api/docs/guides/conversation-state).\n", + "identifier": "BetaReasoningItem" +}) +export type BetaImageGenToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "image_generation_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "generating" | "failed" + readonly "result": string | null + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" | null + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | null + readonly "action"?: BetaImageGenActionEnum | null + readonly "background"?: BetaImageBackground | null + readonly "output_format"?: BetaImageOutputFormat | null + readonly "revised_prompt"?: string | null +} +export const BetaImageGenToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("image_generation_call").annotate({ + "description": "The type of the image generation call. Always `image_generation_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the image generation call." }), + "status": Schema.Literals(["in_progress", "completed", "generating", "failed"]).annotate({ + "description": "The status of the image generation call." + }), + "result": Schema.Union([ + Schema.String.annotate({ "description": "The generated image encoded in base64." }), + Schema.Null + ]), + "size": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + Schema.Null + ]) + ), + "quality": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": + "The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`." + }), + Schema.Null + ]) + ), + "action": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaImageGenActionEnum).annotate({ + "description": "The action used for image generation." + }), + Schema.Null + ]) + ), + "background": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaImageBackground).annotate({ + "description": "The background setting used for generation." + }), + Schema.Null + ]) + ), + "output_format": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaImageOutputFormat).annotate({ + "description": "The output format used for generation." + }), + Schema.Null + ]) + ), + "revised_prompt": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The prompt that was used after any model prompt rewriting." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "Image generation call", + "description": "An image generation request made by the model.", + "identifier": "BetaImageGenToolCall" +}) +export type BetaCodeInterpreterToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "code_interpreter_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "incomplete" | "interpreting" | "failed" + readonly "container_id": string + readonly "code": string | null + readonly "outputs": ReadonlyArray | null +} +export const BetaCodeInterpreterToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("code_interpreter_call").annotate({ + "description": "The type of the code interpreter tool call. Always `code_interpreter_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the code interpreter tool call.\n" }), + "status": Schema.Literals(["in_progress", "completed", "incomplete", "interpreting", "failed"]).annotate({ + "description": + "The status of the code interpreter tool call. Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and `failed`.\n" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the container used to run the code.\n" }), + "code": Schema.Union([ + Schema.String.annotate({ "description": "The code to run, or null if not available.\n" }), + Schema.Null + ]), + "outputs": Schema.Union([ + Schema.Array(Schema.Union([BetaCodeInterpreterOutputLogs, BetaCodeInterpreterOutputImage], { mode: "oneOf" })) + .annotate({ + "description": + "The outputs generated by the code interpreter, such as logs or images.\nCan be null if no outputs are available.\n" + }), + Schema.Null + ]) +}).annotate({ + "title": "Code interpreter tool call", + "description": "A tool call to run code.\n", + "identifier": "BetaCodeInterpreterToolCall" +}) +export type BetaLocalShellToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "local_shell_call" + readonly "id": string + readonly "call_id": string + readonly "action": BetaLocalShellExecAction + readonly "status": "in_progress" | "completed" | "incomplete" +} +export const BetaLocalShellToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("local_shell_call").annotate({ + "description": "The type of the local shell call. Always `local_shell_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the local shell call.\n" }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "action": BetaLocalShellExecAction, + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the local shell call.\n" + }) +}).annotate({ + "title": "Local shell call", + "description": "A tool call to run a command on the local shell.\n", + "identifier": "BetaLocalShellToolCall" +}) +export type BetaFunctionShellCallOutputOutcomeParam = + | BetaFunctionShellCallOutputTimeoutOutcomeParam + | BetaFunctionShellCallOutputExitOutcomeParam +export const BetaFunctionShellCallOutputOutcomeParam = Schema.Union([ + BetaFunctionShellCallOutputTimeoutOutcomeParam, + BetaFunctionShellCallOutputExitOutcomeParam +], { mode: "oneOf" }).annotate({ + "title": "Shell call outcome", + "description": "The exit or timeout outcome associated with this shell call.", + "identifier": "BetaFunctionShellCallOutputOutcomeParam" +}) +export type BetaApplyPatchOperationParam = + | BetaApplyPatchCreateFileOperationParam + | BetaApplyPatchDeleteFileOperationParam + | BetaApplyPatchUpdateFileOperationParam +export const BetaApplyPatchOperationParam = Schema.Union([ + BetaApplyPatchCreateFileOperationParam, + BetaApplyPatchDeleteFileOperationParam, + BetaApplyPatchUpdateFileOperationParam +], { mode: "oneOf" }).annotate({ + "title": "Apply patch operation", + "description": "One of the create_file, delete_file, or update_file operations supplied to the apply_patch tool.", + "identifier": "BetaApplyPatchOperationParam" +}) +export type BetaMCPListTools = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_list_tools" + readonly "id": string + readonly "server_label": string + readonly "tools": ReadonlyArray + readonly "error"?: string | null +} +export const BetaMCPListTools = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_list_tools").annotate({ + "description": "The type of the item. Always `mcp_list_tools`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the list.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server.\n" }), + "tools": Schema.Array(BetaMCPListToolsTool).annotate({ "description": "The tools available on the server.\n" }), + "error": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Error message if the server could not list tools.\n" }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP list tools", + "description": "A list of tools available on an MCP server.\n", + "identifier": "BetaMCPListTools" +}) +export type BetaMCPToolCallError = BetaMCPProtocolError | BetaMCPToolExecutionError | BetaHTTPError +export const BetaMCPToolCallError = Schema.Union([BetaMCPProtocolError, BetaMCPToolExecutionError, BetaHTTPError], { + mode: "oneOf" +}).annotate({ "identifier": "BetaMCPToolCallError" }) +export type BetaProgramOutputItemParam = { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id": string + readonly "type": "program_output" + readonly "call_id": string + readonly "result": string + readonly "status": BetaProgramOutputItemStatus +} +export const BetaProgramOutputItemParam = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.String.annotate({ "description": "The unique ID of this program output item." }), + "type": Schema.Literal("program_output").annotate({ "description": "The item type. Always `program_output`." }), + "call_id": Schema.String.annotate({ "description": "The call ID of the program item." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "result": Schema.String.annotate({ "description": "The result produced by the program item." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => BetaProgramOutputItemStatus).annotate({ + "description": "The terminal status of the program output." + }) +}).annotate({ "identifier": "BetaProgramOutputItemParam" }) +export type BetaModerationConfigParam = { readonly "mode": BetaModerationMode } +export const BetaModerationConfigParam = Schema.Struct({ "mode": BetaModerationMode }).annotate({ + "description": "The moderation policy for the response input.", + "identifier": "BetaModerationConfigParam" +}) +export type BetaConversationParam = string | BetaConversationParam_2 +export const BetaConversationParam = Schema.Union([ + Schema.String.annotate({ "title": "Conversation ID", "description": "The unique ID of the conversation.\n" }), + BetaConversationParam_2 +], { mode: "oneOf" }).annotate({ + "description": + "The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request.\nInput items and output items from this response are automatically added to this conversation after this response completes.\n", + "identifier": "BetaConversationParam" +}) +export type BetaMisalignmentErrorDetailsResource = { + readonly "review_target"?: string | null + readonly "error_type"?: Beta_MisalignmentErrorType + readonly "detailed_explanation"?: string + readonly "steer"?: Beta_MisalignmentSteer +} +export const BetaMisalignmentErrorDetailsResource = Schema.Struct({ + "review_target": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "An opaque target for explicitly continuing this review, or null when unavailable." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(96).annotate({ "expected": "a string with at most 96 code points" }) + ).check( + Schema.isPattern(new RegExp("^[A-Za-z0-9._~:-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^[A-Za-z0-9._~:-]+$" + }) + ), + Schema.Null + ]) + ), + "error_type": Schema.optionalKey( + Schema.suspend((): Schema.Codec => Beta_MisalignmentErrorType).annotate({ + "description": "An optional classification; clients must accept additional values." + }) + ), + "detailed_explanation": Schema.optionalKey( + Schema.String.annotate({ "description": "The public explanation for this block." }) + ), + "steer": Schema.optionalKey( + Schema.suspend((): Schema.Codec => Beta_MisalignmentSteer).annotate({ + "description": "An optional public continuation instruction." + }) + ) +}).annotate({ "identifier": "BetaMisalignmentErrorDetailsResource" }) +export type BetaMultiAgentCall = { + readonly "agent"?: BetaAgentTag + readonly "type": "multi_agent_call" + readonly "id": string + readonly "call_id": string + readonly "action": BetaMultiAgentAction + readonly "arguments": string +} +export const BetaMultiAgentCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("multi_agent_call").annotate({ + "description": "The type of the multi-agent call. Always `multi_agent_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the multi-agent call item." }), + "call_id": Schema.String.annotate({ "description": "The unique ID linking this call to its output." }), + "action": Schema.suspend((): Schema.Codec => BetaMultiAgentAction).annotate({ + "description": "The multi-agent action to execute." + }), + "arguments": Schema.String.annotate({ "description": "The JSON string of arguments generated for the action." }) +}).annotate({ "identifier": "BetaMultiAgentCall" }) +export type BetaProgramOutput = { + readonly "agent"?: BetaAgentTag + readonly "type": "program_output" + readonly "id": string + readonly "call_id": string + readonly "result": string + readonly "status": BetaProgramOutputStatus +} +export const BetaProgramOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("program_output").annotate({ + "description": "The type of the item. Always `program_output`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the program output item." }), + "call_id": Schema.String.annotate({ "description": "The call ID of the program item." }), + "result": Schema.String.annotate({ "description": "The result produced by the program item." }), + "status": Schema.suspend((): Schema.Codec => BetaProgramOutputStatus).annotate({ + "description": "The terminal status of the program output item." + }) +}).annotate({ "identifier": "BetaProgramOutput" }) +export type BetaToolSearchCall = { + readonly "agent"?: BetaAgentTag + readonly "type": "tool_search_call" + readonly "id": string + readonly "call_id": string | null + readonly "execution": BetaToolSearchExecutionType + readonly "arguments": Schema.Json + readonly "status": BetaFunctionCallStatus + readonly "created_by"?: string +} +export const BetaToolSearchCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("tool_search_call").annotate({ + "description": "The type of the item. Always `tool_search_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool search call item." }), + "call_id": Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }), + Schema.Null + ]), + "execution": Schema.suspend((): Schema.Codec => BetaToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }), + "arguments": Schema.Json.annotate({ + "expected": "JSON value", + "description": "Arguments used for the tool search call." + }), + "status": Schema.suspend((): Schema.Codec => BetaFunctionCallStatus).annotate({ + "description": "The status of the tool search call item that was recorded." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ "identifier": "BetaToolSearchCall" }) +export type BetaFunctionShellCallOutputContent = { + readonly "stdout": string + readonly "stderr": string + readonly "outcome": BetaFunctionShellCallOutputTimeoutOutcome | BetaFunctionShellCallOutputExitOutcome + readonly "created_by"?: string +} +export const BetaFunctionShellCallOutputContent = Schema.Struct({ + "stdout": Schema.String.annotate({ "description": "The standard output that was captured." }), + "stderr": Schema.String.annotate({ "description": "The standard error output that was captured." }), + "outcome": Schema.Union([BetaFunctionShellCallOutputTimeoutOutcome, BetaFunctionShellCallOutputExitOutcome], { + mode: "oneOf" + }).annotate({ + "title": "Shell call outcome", + "description": + "Represents either an exit outcome (with an exit code) or a timeout outcome for a shell call output chunk." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ + "title": "Shell call output content", + "description": "The content of a shell tool call output that was emitted.", + "identifier": "BetaFunctionShellCallOutputContent" +}) +export type BetaPromptCacheMissDiagnosticsBody = { + readonly "type": "cache_miss" + readonly "reason": BetaCacheMissReasonTypeEnum + readonly "cache_missed_tokens": number + readonly "comparison_reusable_tokens"?: number +} +export const BetaPromptCacheMissDiagnosticsBody = Schema.Struct({ + "type": Schema.Literal("cache_miss"), + "reason": Schema.suspend((): Schema.Codec => BetaCacheMissReasonTypeEnum).annotate({ + "description": "The reason prompt cache reuse did not occur." + }), + "cache_missed_tokens": Schema.Number.annotate({ + "description": "The estimated number of input tokens affected after the first detected divergence." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "comparison_reusable_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The raw token count of the reusable prefix in the compared response." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) + ) +}).annotate({ "identifier": "BetaPromptCacheMissDiagnosticsBody" }) +export type BetaModerationResultBody = { + readonly "type": "moderation_result" + readonly "model": string + readonly "flagged": boolean + readonly "categories": { readonly [x: string]: boolean } + readonly "category_scores": { readonly [x: string]: number } + readonly "category_applied_input_types": { readonly [x: string]: ReadonlyArray } +} +export const BetaModerationResultBody = Schema.Struct({ + "type": Schema.Literal("moderation_result").annotate({ + "description": "The object type, which was always `moderation_result` for successful moderation results." + }), + "model": Schema.String.annotate({ "description": "The moderation model that produced this result." }), + "flagged": Schema.Boolean.annotate({ + "description": "A boolean indicating whether the content was flagged by any category." + }), + "categories": Schema.Record(Schema.String, Schema.Boolean).annotate({ + "description": + "A dictionary of moderation categories to booleans, True if the input is flagged under this category." + }), + "category_scores": Schema.Record(Schema.String, Schema.Finite).annotate({ + "description": "A dictionary of moderation categories to scores." + }), + "category_applied_input_types": Schema.Record(Schema.String, Schema.Array(BetaModerationInputType)).annotate({ + "description": "Which modalities of input are reflected by the score for each category." + }) +}).annotate({ + "title": "Moderation result", + "description": "A moderation result produced for the response input or output.", + "identifier": "BetaModerationResultBody" +}) +export type BetaResponseShellCallOutputContentDeltaStreamingEvent = { + readonly "type": "response.shell_call_output_content.delta" + readonly "sequence_number": number + readonly "agent"?: BetaAgentTag + readonly "item_id": string + readonly "output_index": number + readonly "command_index": number + readonly "delta": BetaShellCallOutputDelta +} +export const BetaResponseShellCallOutputContentDeltaStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_output_content.delta").annotate({ + "description": "The type of the event, always `response.shell_call_output_content.delta`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "item_id": Schema.String.annotate({ "description": "The ID of the output item that was updated." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that produced output." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.suspend((): Schema.Codec => BetaShellCallOutputDelta).annotate({ + "description": "The stdout/stderr delta that was emitted." + }) +}).annotate({ + "title": "Response shell call output content delta event", + "description": "A streaming event that indicated shell call output was incrementally added.", + "identifier": "BetaResponseShellCallOutputContentDeltaStreamingEvent" +}) +export type BetaResponseTextDeltaEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.output_text.delta" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "delta": string + readonly "sequence_number": number + readonly "logprobs": ReadonlyArray +} +export const BetaResponseTextDeltaEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.output_text.delta").annotate({ + "description": "The type of the event. Always `response.output_text.delta`.\n" + }), + "item_id": Schema.String.annotate({ "description": "The ID of the output item that the text delta was added to.\n" }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the text delta was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the text delta was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "delta": Schema.String.annotate({ "description": "The text delta that was added.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "logprobs": Schema.Array(BetaResponseLogProb).annotate({ + "description": "The log probabilities of the tokens in the delta.\n" + }) +}).annotate({ + "description": "Emitted when there is an additional text delta.", + "identifier": "BetaResponseTextDeltaEvent" +}) +export type BetaResponseTextDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.output_text.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "text": string + readonly "sequence_number": number + readonly "logprobs": ReadonlyArray +} +export const BetaResponseTextDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.output_text.done").annotate({ + "description": "The type of the event. Always `response.output_text.done`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the text content is finalized.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the text content is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ + "description": "The index of the content part that the text content is finalized.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "text": Schema.String.annotate({ "description": "The text content that is finalized.\n" }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "logprobs": Schema.Array(BetaResponseLogProb).annotate({ + "description": "The log probabilities of the tokens in the delta.\n" + }) +}).annotate({ "description": "Emitted when text content is finalized.", "identifier": "BetaResponseTextDoneEvent" }) +export type AssistantToolsFileSearch = { + readonly "type": "file_search" + readonly "file_search"?: { + readonly "max_num_results"?: number + readonly "ranking_options"?: FileSearchRankingOptions + } +} +export const AssistantToolsFileSearch = Schema.Struct({ + "type": Schema.Literal("file_search").annotate({ "description": "The type of tool being defined: `file_search`" }), + "file_search": Schema.optionalKey( + Schema.Struct({ + "max_num_results": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The maximum number of results the file search tool should output. The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number should be between 1 and 50 inclusive.\n\nNote that the file search tool may output fewer than `max_num_results` results. See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ), + "ranking_options": Schema.optionalKey(FileSearchRankingOptions) + }).annotate({ "description": "Overrides for the file search tool." }) + ) +}).annotate({ "title": "FileSearch tool", "identifier": "AssistantToolsFileSearch" }) +export type RunStepDetailsToolCallsFileSearchObject = { + readonly "id": string + readonly "type": "file_search" + readonly "file_search": { + readonly "ranking_options"?: RunStepDetailsToolCallsFileSearchRankingOptionsObject + readonly "results"?: ReadonlyArray + } +} +export const RunStepDetailsToolCallsFileSearchObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the tool call object." }), + "type": Schema.Literal("file_search").annotate({ + "description": "The type of tool call. This is always going to be `file_search` for this type of tool call." + }), + "file_search": Schema.Struct({ + "ranking_options": Schema.optionalKey(RunStepDetailsToolCallsFileSearchRankingOptionsObject), + "results": Schema.optionalKey( + Schema.Array(RunStepDetailsToolCallsFileSearchResultObject).annotate({ + "description": "The results of the file search." + }) + ) + }).annotate({ "description": "For now, this is always going to be an empty object." }) +}).annotate({ "title": "File search tool call", "identifier": "RunStepDetailsToolCallsFileSearchObject" }) +export type AssistantToolsFunction = { readonly "type": "function"; readonly "function": FunctionObject } +export const AssistantToolsFunction = Schema.Struct({ + "type": Schema.Literal("function").annotate({ "description": "The type of tool being defined: `function`" }), + "function": FunctionObject +}).annotate({ "title": "Function tool", "identifier": "AssistantToolsFunction" }) +export type ChatCompletionTool = { readonly "type": "function"; readonly "function": FunctionObject } +export const ChatCompletionTool = Schema.Struct({ + "type": Schema.Literal("function").annotate({ + "description": "The type of the tool. Currently, only `function` is supported." + }), + "function": FunctionObject +}).annotate({ + "title": "Function tool", + "description": "A function tool that can be used to generate a response.\n", + "identifier": "ChatCompletionTool" +}) +export type ThreadStreamEvent = { + readonly "enabled"?: boolean + readonly "event": "thread.created" + readonly "data": ThreadObject +} +export const ThreadStreamEvent = Schema.Union([ + Schema.Struct({ + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether to enable input audio transcription." }) + ), + "event": Schema.Literal("thread.created"), + "data": ThreadObject + }).annotate({ + "description": "Occurs when a new [thread](https://developers.openai.com/api/docs/assistants/migration) is created." + }) +], { mode: "oneOf" }).annotate({ "identifier": "ThreadStreamEvent" }) +export type AssistantsApiResponseFormatOption = + | "auto" + | ResponseFormatText + | ResponseFormatJsonObject + | ResponseFormatJsonSchema +export const AssistantsApiResponseFormatOption = Schema.Union([ + Schema.Literal("auto").annotate({ "description": "`auto` is the default value\n" }), + ResponseFormatText, + ResponseFormatJsonObject, + ResponseFormatJsonSchema +], { mode: "oneOf" }).annotate({ + "description": + "Specifies the format that the model must output. Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.\n", + "identifier": "AssistantsApiResponseFormatOption" +}) +export type TextResponseFormatConfiguration = + | ResponseFormatText + | TextResponseFormatJsonSchema + | ResponseFormatJsonObject +export const TextResponseFormatConfiguration = Schema.Union([ + ResponseFormatText, + TextResponseFormatJsonSchema, + ResponseFormatJsonObject +], { mode: "oneOf" }).annotate({ + "description": + "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n", + "identifier": "TextResponseFormatConfiguration" +}) +export type Error = { + readonly "code": string | null + readonly "message": string + readonly "param": string | null + readonly "type": string + readonly "misalignment"?: MisalignmentErrorDetailsResource +} +export const Error = Schema.Struct({ + "code": Schema.Union([Schema.String, Schema.Null]), + "message": Schema.String, + "param": Schema.Union([Schema.String, Schema.Null]), + "type": Schema.String, + "misalignment": Schema.optionalKey(MisalignmentErrorDetailsResource) +}).annotate({ "identifier": "Error" }) +export type ResponseError = { + readonly "code": ResponseErrorCode + readonly "message": string + readonly "misalignment"?: MisalignmentErrorDetailsResource +} | null +export const ResponseError = Schema.Union([ + Schema.Struct({ + "code": ResponseErrorCode, + "message": Schema.String.annotate({ "description": "A human-readable description of the error.\n" }), + "misalignment": Schema.optionalKey(MisalignmentErrorDetailsResource) + }).annotate({ "description": "An error object returned when the model fails to generate a Response.\n" }), + Schema.Null +]).annotate({ "identifier": "ResponseError" }) +export type Error_2 = { + readonly "code": string + readonly "message": string + readonly "headers"?: { readonly [x: string]: string } + readonly "misalignment"?: MisalignmentErrorDetailsResource +} +export const Error_2 = Schema.Struct({ + "code": Schema.String.annotate({ "description": "A machine-readable error code that was returned." }), + "message": Schema.String.annotate({ "description": "A human-readable description of the error that was returned." }), + "headers": Schema.optionalKey( + Schema.Record(Schema.String, Schema.String.annotate({ "description": "The retry header value." })).annotate({ + "description": "The Retry-After and Retry-After-Ms headers returned with the original error, if any." + }) + ), + "misalignment": Schema.optionalKey(MisalignmentErrorDetailsResource) +}).annotate({ + "title": "Error", + "description": "An error that occurred while generating the response.", + "identifier": "Error-2" +}) +export type CreateVoiceRequestMultipart = CreateVoiceFromConsentRequestMultipart +export const CreateVoiceRequestMultipart = Schema.Union([CreateVoiceFromConsentRequestMultipart], { mode: "oneOf" }) + .annotate({ "identifier": "CreateVoiceRequestMultipart" }) +export type CreateTranscriptionResponseStreamEvent = + | TranscriptTextSegmentEvent + | TranscriptTextDeltaEvent + | TranscriptTextDoneEvent +export const CreateTranscriptionResponseStreamEvent = Schema.Union([ + TranscriptTextSegmentEvent, + TranscriptTextDeltaEvent, + TranscriptTextDoneEvent +]).annotate({ "identifier": "CreateTranscriptionResponseStreamEvent" }) +export type ListBatchesResponse = { + readonly "data": ReadonlyArray + readonly "first_id"?: string + readonly "last_id"?: string + readonly "has_more": boolean + readonly "object": "list" +} +export const ListBatchesResponse = Schema.Struct({ + "data": Schema.Array(Batch), + "first_id": Schema.optionalKey(Schema.String), + "last_id": Schema.optionalKey(Schema.String), + "has_more": Schema.Boolean, + "object": Schema.Literal("list") +}).annotate({ "identifier": "ListBatchesResponse" }) +export type ChatCompletionResponseMessage = { + readonly "content": string | null + readonly "refusal"?: string | null + readonly "tool_calls"?: ChatCompletionMessageToolCalls | null + readonly "annotations"?: ReadonlyArray< + { + readonly "type": "url_citation" + readonly "url_citation": { + readonly "end_index": number + readonly "start_index": number + readonly "url": string + readonly "title": string + } + } + > + readonly "role": "assistant" + readonly "function_call"?: + | { readonly "arguments": string; readonly "name": string } & { readonly [x: string]: Schema.Json } + | null + readonly "audio"?: { + readonly "id": string + readonly "expires_at": number + readonly "data": string + readonly "transcript": string + } | null +} +export const ChatCompletionResponseMessage = Schema.Struct({ + "content": Schema.Union([Schema.String.annotate({ "description": "The contents of the message." }), Schema.Null]), + "refusal": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The refusal message generated by the model." }), + Schema.Null + ]) + ), + "tool_calls": Schema.optionalKey(Schema.Union([ChatCompletionMessageToolCalls, Schema.Null])), + "annotations": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "type": Schema.Literal("url_citation").annotate({ + "description": "The type of the URL citation. Always `url_citation`." + }), + "url_citation": Schema.Struct({ + "end_index": Schema.Number.annotate({ + "description": "The index of the last character of the URL citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "start_index": Schema.Number.annotate({ + "description": "The index of the first character of the URL citation in the message." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "url": Schema.String.annotate({ "description": "The URL of the web resource.", "format": "uri" }), + "title": Schema.String.annotate({ "description": "The title of the web resource." }) + }).annotate({ "description": "A URL citation when using web search." }) + }).annotate({ "description": "A URL citation when using web search.\n" }) + ).annotate({ + "description": + "Annotations for the message, when applicable, as when using the\n[web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).\n" + }) + ), + "role": Schema.Literal("assistant").annotate({ "description": "The role of the author of this message." }), + "function_call": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "arguments": Schema.String.annotate({ + "description": + "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + }), + "name": Schema.String.annotate({ "description": "The name of the function to call." }) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": + "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model." + }) + ), + "audio": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "id": Schema.String.annotate({ "description": "Unique identifier for this audio response." }), + "expires_at": Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when this audio response will\nno longer be accessible on the server for use in multi-turn\nconversations.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "data": Schema.String.annotate({ + "description": "Base64 encoded audio bytes generated by the model, in the format\nspecified in the request.\n" + }), + "transcript": Schema.String.annotate({ "description": "Transcript of the audio generated by the model." }) + }).annotate({ + "description": + "If the audio output modality is requested, this object contains data\nabout the audio response from the model. [Learn more](https://developers.openai.com/api/docs/guides/audio).\n" + }), + Schema.Null + ])) +}).annotate({ + "description": "A chat completion message generated by the model.", + "identifier": "ChatCompletionResponseMessage" +}) +export type ChatCompletionModerationResults = { + readonly "type": "moderation_results" + readonly "model": string + readonly "results": ReadonlyArray +} +export const ChatCompletionModerationResults = Schema.Struct({ + "type": Schema.Literal("moderation_results").annotate({ + "description": "The object type, which is always `moderation_results`." + }), + "model": Schema.String.annotate({ "description": "The moderation model used to generate the results." }), + "results": Schema.Array(ModerationResultBody).annotate({ "description": "A list of moderation results." }) +}).annotate({ + "description": "Successful moderation results for the request input or generated output.", + "identifier": "ChatCompletionModerationResults" +}) +export type Moderation = { + readonly "input": ModerationResultBody | ModerationErrorBody + readonly "output": ModerationResultBody | ModerationErrorBody +} +export const Moderation = Schema.Struct({ + "input": Schema.Union([ModerationResultBody, ModerationErrorBody], { mode: "oneOf" }).annotate({ + "description": "Moderation for the response input." + }), + "output": Schema.Union([ModerationResultBody, ModerationErrorBody], { mode: "oneOf" }).annotate({ + "description": "Moderation for the response output." + }) +}).annotate({ + "title": "Moderation", + "description": "Moderation results or errors for the response input and output.", + "identifier": "Moderation" +}) +export type ChatCompletionRequestDeveloperMessage = { + readonly "content": string | ReadonlyArray + readonly "role": "developer" + readonly "name"?: string +} +export const ChatCompletionRequestDeveloperMessage = Schema.Struct({ + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text content", "description": "The contents of the developer message." }), + Schema.Array(ChatCompletionRequestMessageContentPartText).annotate({ + "title": "Array of content parts", + "description": + "An array of content parts with a defined type. For developer messages, only type `text` is supported." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }).annotate({ "description": "The contents of the developer message." }), + "role": Schema.Literal("developer").annotate({ + "description": "The role of the messages author, in this case `developer`." + }), + "name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + }) + ) +}).annotate({ + "title": "Developer message", + "description": + "Developer-provided instructions that the model should follow, regardless of\nmessages sent by the user. With o1 models and newer, `developer` messages\nreplace the previous `system` messages.\n", + "identifier": "ChatCompletionRequestDeveloperMessage" +}) +export type ChatCompletionRequestSystemMessageContentPart = ChatCompletionRequestMessageContentPartText +export const ChatCompletionRequestSystemMessageContentPart = Schema.Union( + [ChatCompletionRequestMessageContentPartText], + { mode: "oneOf" } +).annotate({ "identifier": "ChatCompletionRequestSystemMessageContentPart" }) +export type ChatCompletionRequestAssistantMessageContentPart = + | ChatCompletionRequestMessageContentPartText + | ChatCompletionRequestMessageContentPartRefusal +export const ChatCompletionRequestAssistantMessageContentPart = Schema.Union([ + ChatCompletionRequestMessageContentPartText, + ChatCompletionRequestMessageContentPartRefusal +], { mode: "oneOf" }).annotate({ "identifier": "ChatCompletionRequestAssistantMessageContentPart" }) +export type ChatCompletionRequestToolMessageContentPart = ChatCompletionRequestMessageContentPartText +export const ChatCompletionRequestToolMessageContentPart = Schema.Union([ChatCompletionRequestMessageContentPartText], { + mode: "oneOf" +}).annotate({ "identifier": "ChatCompletionRequestToolMessageContentPart" }) +export type PredictionContent = { + readonly "type": "content" + readonly "content": string | ReadonlyArray +} +export const PredictionContent = Schema.Struct({ + "type": Schema.Literal("content").annotate({ + "description": "The type of the predicted content you want to provide. This type is\ncurrently always `content`.\n" + }), + "content": Schema.Union([ + Schema.String.annotate({ + "title": "Text content", + "description": + "The content used for a Predicted Output. This is often the\ntext of a file you are regenerating with minor changes.\n" + }), + Schema.Array(ChatCompletionRequestMessageContentPartText).annotate({ + "title": "Array of content parts", + "description": + "An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text inputs." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The content that should be matched when generating a model response.\nIf generated tokens would match this content, the entire model response\ncan be returned much more quickly.\n" + }) +}).annotate({ + "title": "Static Content", + "description": "Static predicted output content, such as the content of a text file that is\nbeing regenerated.\n", + "identifier": "PredictionContent" +}) +export type ChatCompletionRequestUserMessageContentPart = + | ChatCompletionRequestMessageContentPartText + | ChatCompletionRequestMessageContentPartImage + | ChatCompletionRequestMessageContentPartAudio + | ChatCompletionRequestMessageContentPartFile +export const ChatCompletionRequestUserMessageContentPart = Schema.Union([ + ChatCompletionRequestMessageContentPartText, + ChatCompletionRequestMessageContentPartImage, + ChatCompletionRequestMessageContentPartAudio, + ChatCompletionRequestMessageContentPartFile +], { mode: "oneOf" }).annotate({ "identifier": "ChatCompletionRequestUserMessageContentPart" }) +export type ModelIdsCompaction = ModelIdsResponses | string | null +export const ModelIdsCompaction = Schema.Union([ModelIdsResponses, Schema.String, Schema.Null]).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.", + "identifier": "ModelIdsCompaction" +}) +export type ModerationPolicyParam = { + readonly "input"?: ModerationConfigParam | null + readonly "output"?: ModerationConfigParam | null +} +export const ModerationPolicyParam = Schema.Struct({ + "input": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ModerationConfigParam).annotate({ + "description": "The moderation policy for the response input." + }), + Schema.Null + ]) + ), + "output": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ModerationConfigParam).annotate({ + "description": "The moderation policy for the response output." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The policy to apply to moderated response input and output.", + "identifier": "ModerationPolicyParam" +}) +export type ChatCompletionToolChoiceOption = + | "none" + | "auto" + | "required" + | ChatCompletionAllowedToolsChoice + | ChatCompletionNamedToolChoice + | ChatCompletionNamedToolChoiceCustom +export const ChatCompletionToolChoiceOption = Schema.Union([ + Schema.Literals(["none", "auto", "required"]).annotate({ + "title": "Tool choice mode", + "description": + "`none` means the model will not call any tool and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools.\n" + }), + ChatCompletionAllowedToolsChoice, + ChatCompletionNamedToolChoice, + ChatCompletionNamedToolChoiceCustom +], { mode: "oneOf" }).annotate({ + "description": + "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tool and instead generates a message.\n`auto` means the model can pick between generating a message or calling one or more tools.\n`required` means the model must call one or more tools.\nSpecifying a particular tool via `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n\n`none` is the default when no tools are present. `auto` is the default if tools are present.\n", + "identifier": "ChatCompletionToolChoiceOption" +}) +export type CreateContainerBody = { + readonly "name": string + readonly "file_ids"?: ReadonlyArray + readonly "expires_after"?: { readonly "anchor": "last_active_at"; readonly "minutes": number } + readonly "skills"?: ReadonlyArray + readonly "memory_limit"?: "1g" | "4g" | "16g" | "64g" + readonly "network_policy"?: ContainerNetworkPolicyDisabledParam | ContainerNetworkPolicyAllowlistParam +} +export const CreateContainerBody = Schema.Struct({ + "name": Schema.String.annotate({ "description": "Name of the container to create." }), + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "description": "IDs of files to copy to the container." }) + ), + "expires_after": Schema.optionalKey( + Schema.Struct({ + "anchor": Schema.Literal("last_active_at").annotate({ + "description": "Time anchor for the expiration time. Currently only 'last_active_at' is supported." + }), + "minutes": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ "description": "Container expiration time in seconds relative to the 'anchor' time." }) + ), + "skills": Schema.optionalKey( + Schema.Array(Schema.Union([SkillReferenceParam, InlineSkillParam], { mode: "oneOf" })).annotate({ + "description": "An optional list of skills referenced by id or inline data." + }) + ), + "memory_limit": Schema.optionalKey( + Schema.Literals(["1g", "4g", "16g", "64g"]).annotate({ + "description": "Optional memory limit for the container. Defaults to \"1g\"." + }) + ), + "network_policy": Schema.optionalKey( + Schema.Union([ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam], { mode: "oneOf" }) + .annotate({ "description": "Network access policy for the container." }) + ) +}).annotate({ "identifier": "CreateContainerBody" }) +export type AutoCodeInterpreterToolParam = { + readonly "type": "auto" + readonly "file_ids"?: ReadonlyArray + readonly "memory_limit"?: ContainerMemoryLimit | null + readonly "network_policy"?: ContainerNetworkPolicyDisabledParam | ContainerNetworkPolicyAllowlistParam +} +export const AutoCodeInterpreterToolParam = Schema.Struct({ + "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }), + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "An optional list of uploaded files to make available to your code." + }).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })) + ), + "memory_limit": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ContainerMemoryLimit).annotate({ + "description": "The memory limit for the code interpreter container." + }), + Schema.Null + ]) + ), + "network_policy": Schema.optionalKey( + Schema.Union([ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam], { mode: "oneOf" }) + .annotate({ "description": "Network access policy for the container." }) + ) +}).annotate({ + "title": "CodeInterpreterToolAuto", + "description": + "Configuration for a code interpreter container. Optionally specify the IDs of the files to run the code on.", + "identifier": "AutoCodeInterpreterToolParam" +}) +export type ContainerAutoParam = { + readonly "type": "container_auto" + readonly "file_ids"?: ReadonlyArray + readonly "memory_limit"?: ContainerMemoryLimit | null + readonly "network_policy"?: ContainerNetworkPolicyDisabledParam | ContainerNetworkPolicyAllowlistParam + readonly "skills"?: ReadonlyArray +} +export const ContainerAutoParam = Schema.Struct({ + "type": Schema.Literal("container_auto").annotate({ + "description": "Automatically creates a container for this request" + }), + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "An optional list of uploaded files to make available to your code." + }).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })) + ), + "memory_limit": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ContainerMemoryLimit).annotate({ + "description": "The memory limit for the container." + }), + Schema.Null + ]) + ), + "network_policy": Schema.optionalKey( + Schema.Union([ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam], { mode: "oneOf" }) + .annotate({ "description": "Network access policy for the container." }) + ), + "skills": Schema.optionalKey( + Schema.Array(Schema.Union([SkillReferenceParam, InlineSkillParam], { mode: "oneOf" })).annotate({ + "description": "An optional list of skills referenced by id or inline data." + }).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })) + ) +}).annotate({ "identifier": "ContainerAutoParam" }) +export type EvalItemContentItem = + | EvalItemContentText + | InputTextContent + | EvalItemContentOutputText + | EvalItemInputImage + | InputAudio +export const EvalItemContentItem = Schema.Union([ + EvalItemContentText, + InputTextContent, + EvalItemContentOutputText, + EvalItemInputImage, + InputAudio +], { mode: "oneOf" }).annotate({ + "title": "Eval content item", + "description": "A single content item: input text, output text, input image, or input audio.\n", + "identifier": "EvalItemContentItem" +}) +export type ResponseOutputTextAnnotationAddedEvent = { + readonly "type": "response.output_text.annotation.added" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "annotation_index": number + readonly "sequence_number": number + readonly "annotation": Annotation | null +} +export const ResponseOutputTextAnnotationAddedEvent = Schema.Struct({ + "type": Schema.Literal("response.output_text.annotation.added").annotate({ + "description": "The type of the event. Always 'response.output_text.annotation.added'." + }), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the item to which the annotation is being added." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the content part within the output item." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "annotation_index": Schema.Number.annotate({ "description": "The index of the annotation within the content part." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "annotation": Schema.Union([Annotation, Schema.Null]).annotate({ + "description": "The annotation object being added. (See annotation schema for details.)" + }) +}).annotate({ + "title": "ResponseOutputTextAnnotationAddedEvent", + "description": "Emitted when an annotation is added to output text content.\n", + "identifier": "ResponseOutputTextAnnotationAddedEvent" +}) +export type OutputTextContent = { + readonly "type": "output_text" + readonly "text": string + readonly "annotations": ReadonlyArray + readonly "logprobs": ReadonlyArray +} +export const OutputTextContent = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ + "description": "The type of the output text. Always `output_text`." + }), + "text": Schema.String.annotate({ "description": "The text output from the model." }), + "annotations": Schema.Array(Annotation).annotate({ "description": "The annotations of the text output." }), + "logprobs": Schema.Array(LogProb) +}).annotate({ + "title": "Output text", + "description": "A text output from the model.", + "identifier": "OutputTextContent" +}) +export type FunctionAndCustomToolCallOutput = InputTextContent | InputImageContent | InputFileContent +export const FunctionAndCustomToolCallOutput = Schema.Union([InputTextContent, InputImageContent, InputFileContent], { + mode: "oneOf" +}).annotate({ "identifier": "FunctionAndCustomToolCallOutput" }) +export type InputContent = InputTextContent | InputImageContent | InputFileContent +export const InputContent = Schema.Union([InputTextContent, InputImageContent, InputFileContent], { mode: "oneOf" }) + .annotate({ "identifier": "InputContent" }) +export type ResponsePromptVariables = { + readonly [x: string]: string | InputTextContent | InputImageContent | InputFileContent +} | null +export const ResponsePromptVariables = Schema.Union([ + Schema.Record( + Schema.String, + Schema.Union([Schema.String, InputTextContent, InputImageContent, InputFileContent], { mode: "oneOf" }) + ).annotate({ + "title": "Prompt Variables", + "description": + "Optional map of values to substitute in for variables in your\nprompt. The substitution values can either be strings, or other\nResponse input types like images or files.\n" + }), + Schema.Null +]).annotate({ "identifier": "ResponsePromptVariables" }) +export type FunctionToolCallResource = { + readonly "id": string + readonly "type": "function_call" + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "arguments": string + readonly "status": "in_progress" | "completed" | "incomplete" + readonly "async"?: boolean + readonly "created_by"?: string +} +export const FunctionToolCallResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" }), + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the function tool call. Always `function_call`.\n" + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the function tool call generated by the model.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey(Schema.String.annotate({ "description": "The namespace of the function to run.\n" })), + "name": Schema.String.annotate({ "description": "The name of the function to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments to pass to the function.\n" }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the function tool call runs asynchronously.\n" }) + ), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "identifier": "FunctionToolCallResource" }) +export type FunctionShellCall = { + readonly "type": "shell_call" + readonly "id": string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "action": FunctionShellAction + readonly "status": FunctionShellCallStatus + readonly "environment": LocalEnvironmentResource | ContainerReferenceResource | null + readonly "created_by"?: string +} +export const FunctionShellCall = Schema.Struct({ + "type": Schema.Literal("shell_call").annotate({ "description": "The type of the item. Always `shell_call`." }), + "id": Schema.String.annotate({ + "description": "The unique ID of the shell tool call. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ "description": "The unique ID of the shell tool call generated by the model." }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "action": Schema.suspend((): Schema.Codec => FunctionShellAction).annotate({ + "description": "The shell commands and limits that describe how to run the tool call." + }), + "status": Schema.suspend((): Schema.Codec => FunctionShellCallStatus).annotate({ + "description": "The status of the shell call. One of `in_progress`, `completed`, or `incomplete`." + }), + "environment": Schema.Union([ + Schema.Union([LocalEnvironmentResource, ContainerReferenceResource], { mode: "oneOf" }), + Schema.Null + ]), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the entity that created this tool call." }) + ) +}).annotate({ + "title": "Shell tool call", + "description": "A tool call that executes one or more shell commands in a managed environment.", + "identifier": "FunctionShellCall" +}) +export type ApplyPatchToolCall = { + readonly "type": "apply_patch_call" + readonly "id": string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "status": ApplyPatchCallStatus + readonly "operation": ApplyPatchCreateFileOperation | ApplyPatchDeleteFileOperation | ApplyPatchUpdateFileOperation + readonly "created_by"?: string +} +export const ApplyPatchToolCall = Schema.Struct({ + "type": Schema.Literal("apply_patch_call").annotate({ + "description": "The type of the item. Always `apply_patch_call`." + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => ApplyPatchCallStatus).annotate({ + "description": "The status of the apply patch tool call. One of `in_progress` or `completed`." + }), + "operation": Schema.Union([ + ApplyPatchCreateFileOperation, + ApplyPatchDeleteFileOperation, + ApplyPatchUpdateFileOperation + ], { mode: "oneOf" }).annotate({ + "title": "Apply patch operation", + "description": "One of the create_file, delete_file, or update_file operations applied via apply_patch." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the entity that created this tool call." }) + ) +}).annotate({ + "title": "Apply patch tool call", + "description": "A tool call that applies file diffs by creating, deleting, or updating files.", + "identifier": "ApplyPatchToolCall" +}) +export type ApplyPatchToolCallOutput = { + readonly "type": "apply_patch_call_output" + readonly "id": string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "status": ApplyPatchCallOutputStatus + readonly "output"?: string | null + readonly "created_by"?: string +} +export const ApplyPatchToolCallOutput = Schema.Struct({ + "type": Schema.Literal("apply_patch_call_output").annotate({ + "description": "The type of the item. Always `apply_patch_call_output`." + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call output. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => ApplyPatchCallOutputStatus).annotate({ + "description": "The status of the apply patch tool call output. One of `completed` or `failed`." + }), + "output": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Optional textual output returned by the apply patch tool." }), + Schema.Null + ]) + ), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the entity that created this tool call output." }) + ) +}).annotate({ + "title": "Apply patch tool call output", + "description": "The output emitted by an apply patch tool call.", + "identifier": "ApplyPatchToolCallOutput" +}) +export type CustomToolCall = { + readonly "type": "custom_tool_call" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "input": string + readonly "async"?: boolean +} +export const CustomToolCall = Schema.Struct({ + "type": Schema.Literal("custom_tool_call").annotate({ + "description": "The type of the custom tool call. Always `custom_tool_call`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the custom tool call in the OpenAI platform.\n" }) + ), + "call_id": Schema.String.annotate({ + "description": "An identifier used to map this custom tool call to a tool call output.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the custom tool being called.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the custom tool being called.\n" }), + "input": Schema.String.annotate({ "description": "The input for the custom tool call generated by the model.\n" }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the custom tool call runs asynchronously.\n" }) + ) +}).annotate({ + "title": "Custom tool call", + "description": "A call to a custom tool created by the model.\n", + "identifier": "CustomToolCall" +}) +export type FunctionToolCall = { + readonly "id"?: string + readonly "type": "function_call" + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "arguments": string + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "async"?: boolean +} +export const FunctionToolCall = Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" })), + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the function tool call. Always `function_call`.\n" + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the function tool call generated by the model.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey(Schema.String.annotate({ "description": "The namespace of the function to run.\n" })), + "name": Schema.String.annotate({ "description": "The name of the function to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments to pass to the function.\n" }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the function tool call runs asynchronously.\n" }) + ) +}).annotate({ + "title": "Function tool call", + "description": + "A tool call to run a function. See the\n[function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.\n", + "identifier": "FunctionToolCall" +}) +export type CustomToolCallResource = { + readonly "type": "custom_tool_call" + readonly "id": string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "input": string + readonly "async"?: boolean + readonly "status": FunctionCallStatus + readonly "created_by"?: string +} +export const CustomToolCallResource = Schema.Struct({ + "type": Schema.Literal("custom_tool_call").annotate({ + "description": "The type of the custom tool call. Always `custom_tool_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the custom tool call item.\n" }), + "call_id": Schema.String.annotate({ + "description": "An identifier used to map this custom tool call to a tool call output.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the custom tool being called.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the custom tool being called.\n" }), + "input": Schema.String.annotate({ "description": "The input for the custom tool call generated by the model.\n" }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the custom tool call runs asynchronously.\n" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatus).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "title": "ResponseCustomToolCallItem", "identifier": "CustomToolCallResource" }) +export type ComputerAction = + | ClickParam + | DoubleClickAction + | DragParam + | KeyPressAction + | MoveParam + | ScreenshotParam + | ScrollParam + | TypeParam + | WaitParam +export const ComputerAction = Schema.Union([ + ClickParam, + DoubleClickAction, + DragParam, + KeyPressAction, + MoveParam, + ScreenshotParam, + ScrollParam, + TypeParam, + WaitParam +], { mode: "oneOf" }).annotate({ "identifier": "ComputerAction" }) +export type FileSearchTool = { + readonly "type": "file_search" + readonly "vector_store_ids": ReadonlyArray + readonly "max_num_results"?: number + readonly "ranking_options"?: RankingOptions + readonly "filters"?: Filters | null +} +export const FileSearchTool = Schema.Struct({ + "type": Schema.Literal("file_search").annotate({ + "description": "The type of the file search tool. Always `file_search`." + }), + "vector_store_ids": Schema.Array(Schema.String).annotate({ + "description": "The IDs of the vector stores to search." + }), + "max_num_results": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum number of results to return. This number should be between 1 and 50 inclusive." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "ranking_options": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RankingOptions).annotate({ + "description": "Ranking options for search." + }) + ), + "filters": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Filters).annotate({ "description": "A filter to apply." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "File search", + "description": + "A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).", + "identifier": "FileSearchTool" +}) +export type CustomToolParam = { + readonly "type": "custom" + readonly "name": string + readonly "async"?: boolean + readonly "description"?: string + readonly "format"?: CustomTextFormatParam | CustomGrammarFormatParam + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const CustomToolParam = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "The type of the custom tool. Always `custom`." }), + "name": Schema.String.annotate({ "description": "The name of the custom tool, used to identify it in tool calls." }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the tool response can be returned asynchronously versus immediately returned on next response creation." + }) + ), + "description": Schema.optionalKey( + Schema.String.annotate({ "description": "Optional description of the custom tool, used to provide more context." }) + ), + "format": Schema.optionalKey( + Schema.Union([CustomTextFormatParam, CustomGrammarFormatParam], { mode: "oneOf" }).annotate({ + "description": "The input format for the custom tool. Default is unconstrained text." + }) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this tool should be deferred and discovered via tool search." }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Custom tool", + "description": + "A custom tool that processes input using a specified format. Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)", + "identifier": "CustomToolParam" +}) +export type FunctionShellCallOutput = { + readonly "type": "shell_call_output" + readonly "id": string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "status": FunctionShellCallOutputStatusEnum + readonly "output": ReadonlyArray + readonly "max_output_length": number | null + readonly "created_by"?: string +} +export const FunctionShellCallOutput = Schema.Struct({ + "type": Schema.Literal("shell_call_output").annotate({ + "description": "The type of the shell call output. Always `shell_call_output`." + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the shell call output. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ "description": "The unique ID of the shell tool call generated by the model." }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => FunctionShellCallOutputStatusEnum) + .annotate({ + "description": "The status of the shell call output. One of `in_progress`, `completed`, or `incomplete`." + }), + "output": Schema.Array(FunctionShellCallOutputContent).annotate({ + "description": "An array of shell call output contents" + }), + "max_output_length": Schema.Union([ + Schema.Number.annotate({ + "description": + "The maximum length of the shell command output. This is generated by the model and should be passed back with the raw output." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ + "title": "Shell call output", + "description": "The output of a shell tool call that was emitted.", + "identifier": "FunctionShellCallOutput" +}) +export type ResponseShellCallOutputContentDoneStreamingEvent = { + readonly "type": "response.shell_call_output_content.done" + readonly "sequence_number": number + readonly "item_id": string + readonly "output_index": number + readonly "command_index": number + readonly "output": ReadonlyArray +} +export const ResponseShellCallOutputContentDoneStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_output_content.done").annotate({ + "description": "The type of the event, always `response.shell_call_output_content.done`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "item_id": Schema.String.annotate({ "description": "The ID of the output item that was updated." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that produced output." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output": Schema.Array(FunctionShellCallOutputContent).annotate({ + "description": "The output contents emitted for the shell command." + }) +}).annotate({ + "title": "Response shell call output content done event", + "description": "A streaming event that indicated shell call output was completed.", + "identifier": "ResponseShellCallOutputContentDoneStreamingEvent" +}) +export type MCPToolCall = { + readonly "type": "mcp_call" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string + readonly "output"?: string | null + readonly "error"?: MCPToolCallError | null + readonly "status"?: MCPToolCallStatus + readonly "approval_request_id"?: string | null +} +export const MCPToolCall = Schema.Struct({ + "type": Schema.Literal("mcp_call").annotate({ "description": "The type of the item. Always `mcp_call`.\n" }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool call.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server running the tool.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool that was run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments passed to the tool.\n" }), + "output": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The output from the tool call.\n" }), Schema.Null]) + ), + "error": Schema.optionalKey( + Schema.Union([MCPToolCallError, Schema.Null]).annotate({ "description": "The error from the tool call, if any." }) + ), + "status": Schema.optionalKey( + Schema.suspend((): Schema.Codec => MCPToolCallStatus).annotate({ + "description": + "The status of the tool call. One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`.\n" + }) + ), + "approval_request_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Unique identifier for the MCP tool call approval request.\nInclude this value in a subsequent `mcp_approval_response` input to approve or reject the corresponding tool call.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP tool call", + "description": "An invocation of a tool on an MCP server.\n", + "identifier": "MCPToolCall" +}) +export type FunctionShellCallOutputContentParam = { + readonly "stdout": string + readonly "stderr": string + readonly "outcome": FunctionShellCallOutputOutcomeParam +} +export const FunctionShellCallOutputContentParam = Schema.Struct({ + "stdout": Schema.String.annotate({ "description": "Captured stdout output for the shell call." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "stderr": Schema.String.annotate({ "description": "Captured stderr output for the shell call." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "outcome": Schema.suspend((): Schema.Codec => + FunctionShellCallOutputOutcomeParam + ).annotate({ "description": "The exit or timeout outcome associated with this shell call." }) +}).annotate({ + "title": "Shell output content", + "description": "Captured stdout and stderr for a portion of a shell tool call output.", + "identifier": "FunctionShellCallOutputContentParam" +}) +export type EvalRunOutputItemList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const EvalRunOutputItemList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ + "description": "The type of this object. It is always set to \"list\".\n" + }), + "data": Schema.Array(EvalRunOutputItem).annotate({ "description": "An array of eval run output item objects.\n" }), + "first_id": Schema.String.annotate({ + "description": "The identifier of the first eval run output item in the data array." + }), + "last_id": Schema.String.annotate({ + "description": "The identifier of the last eval run output item in the data array." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Indicates whether there are more eval run output items available." + }) +}).annotate({ + "title": "EvalRunOutputItemList", + "description": "An object representing a list of output items for an evaluation run.\n", + "identifier": "EvalRunOutputItemList" +}) +export type ImagesResponse = { + readonly "created": number + readonly "data"?: ReadonlyArray + readonly "background"?: "transparent" | "opaque" + readonly "output_format"?: "png" | "webp" | "jpeg" + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" + readonly "usage"?: ImageGenUsage +} +export const ImagesResponse = Schema.Struct({ + "created": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the image was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "data": Schema.optionalKey(Schema.Array(Image).annotate({ "description": "The list of generated images." })), + "background": Schema.optionalKey( + Schema.Literals(["transparent", "opaque"]).annotate({ + "description": "The background parameter used for the image generation. Either `transparent` or `opaque`." + }) + ), + "output_format": Schema.optionalKey( + Schema.Literals(["png", "webp", "jpeg"]).annotate({ + "description": "The output format of the image generation. Either `png`, `webp`, or `jpeg`." + }) + ), + "size": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }) + ), + "quality": Schema.optionalKey( + Schema.Literals(["low", "medium", "high", "xhigh", "max"]).annotate({ + "description": "The quality of the image generated. One of `low`, `medium`, `high`, `xhigh`, or `max`." + }) + ), + "usage": Schema.optionalKey(ImageGenUsage) +}).annotate({ + "title": "Image generation response", + "description": "The response from the image generation endpoint.", + "identifier": "ImagesResponse" +}) +export type ImageEditStreamEvent = ImageEditPartialImageEvent | ImageEditCompletedEvent +export const ImageEditStreamEvent = Schema.Union([ImageEditPartialImageEvent, ImageEditCompletedEvent]).annotate({ + "identifier": "ImageEditStreamEvent" +}) +export type ImageGenStreamEvent = ImageGenPartialImageEvent | ImageGenCompletedEvent +export const ImageGenStreamEvent = Schema.Union([ImageGenPartialImageEvent, ImageGenCompletedEvent]).annotate({ + "identifier": "ImageGenStreamEvent" +}) +export type LiveMediaSessionAudioParam = { readonly "output"?: LiveInitialSessionAudioOutputParam } +export const LiveMediaSessionAudioParam = Schema.Struct({ + "output": Schema.optionalKey(LiveInitialSessionAudioOutputParam) +}).annotate({ + "description": + "Startup audio output configuration. WebRTC and SIP negotiate the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.", + "identifier": "LiveMediaSessionAudioParam" +}) +export type LiveHostedShellContainerAutoParam = { + readonly "type": "container_auto" + readonly "file_ids"?: ReadonlyArray | null + readonly "memory_limit"?: LiveContainerMemoryLimit | null + readonly "network_policy"?: + | LiveContainerNetworkPolicyDisabledParam + | LiveHostedShellNetworkPolicyAllowlistParam + | null + readonly "skills"?: ReadonlyArray | null +} +export const LiveHostedShellContainerAutoParam = Schema.Struct({ + "type": Schema.Literal("container_auto").annotate({ + "description": "Automatically creates a container for this request" + }), + "file_ids": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.String).annotate({ + "description": "An optional list of uploaded files to make available to your code." + }).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })), + Schema.Null + ]) + ), + "memory_limit": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveContainerMemoryLimit).annotate({ + "description": "The memory limit for the container." + }), + Schema.Null + ]) + ), + "network_policy": Schema.optionalKey( + Schema.Union([ + Schema.Union([LiveContainerNetworkPolicyDisabledParam, LiveHostedShellNetworkPolicyAllowlistParam], { + mode: "oneOf" + }).annotate({ "description": "Network access policy for the container." }), + Schema.Null + ]) + ), + "skills": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.Union([LiveSkillReferenceParam, LiveInlineSkillParam], { mode: "oneOf" })).annotate({ + "description": "An optional list of skills referenced by id or inline data." + }).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })), + Schema.Null + ]) + ) +}).annotate({ "identifier": "LiveHostedShellContainerAutoParam" }) +export type LiveInitialItem = + | LiveInitialDeveloperMessageItemParam + | LiveInitialUserMessageItemParam + | LiveInitialAssistantMessageItemParam +export const LiveInitialItem = Schema.Union([ + LiveInitialDeveloperMessageItemParam, + LiveInitialUserMessageItemParam, + LiveInitialAssistantMessageItemParam +], { mode: "oneOf" }).annotate({ + "description": "A developer, user, or assistant message supplied as text history before the Live session starts.", + "identifier": "LiveInitialItem" +}) +export type LiveClientConfigParam = { readonly "data_channel": LiveDataChannelConfigParam } +export const LiveClientConfigParam = Schema.Struct({ + "data_channel": Schema.suspend((): Schema.Codec => LiveDataChannelConfigParam).annotate({ + "description": "Client and server event permissions for the WebRTC frontend data channel." + }) +}).annotate({ + "description": + "Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.", + "identifier": "LiveClientConfigParam" +}) +export type LiveSIPTransport = { + readonly "type": "sip" + readonly "destination": string + readonly "trunk": LiveSIPTrunk +} +export const LiveSIPTransport = Schema.Struct({ + "type": Schema.Literal("sip").annotate({ "description": "The transport used for the Live session. Always `sip`." }), + "destination": Schema.String.annotate({ + "description": "Phone number to call in E.164 format. SIP URI destinations are not supported." + }).check( + Schema.isPattern(new RegExp("^\\+[1-9][0-9]{1,14}$", "u")).annotate({ + "expected": "a string matching the RegExp ^\\+[1-9][0-9]{1,14}$" + }) + ), + "trunk": LiveSIPTrunk +}).annotate({ + "description": + "Place an outbound SIP call using your provider's trunk. Outbound SIP calling must be enabled for your organization. The trunk must support TLS signaling, Opus audio, and SDES-SRTP media. See [Telephony and SIP](https://developers.openai.com/api/docs/guides/voice-sip?api=live#place-an-outbound-call).", + "identifier": "LiveSIPTransport" +}) +export type AuditLogActor = { + readonly "type"?: "session" | "api_key" + readonly "session"?: AuditLogActorSession + readonly "api_key"?: AuditLogActorApiKey +} +export const AuditLogActor = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literals(["session", "api_key"]).annotate({ + "description": "The type of actor. Is either `session` or `api_key`." + }) + ), + "session": Schema.optionalKey(AuditLogActorSession), + "api_key": Schema.optionalKey(AuditLogActorApiKey) +}).annotate({ "description": "The actor who performed the audit logged action.", "identifier": "AuditLogActor" }) +export type UsageResponse = { + readonly "object": "page" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next_page": string | null +} +export const UsageResponse = Schema.Struct({ + "object": Schema.Literal("page"), + "data": Schema.Array(UsageTimeBucket), + "has_more": Schema.Boolean, + "next_page": Schema.Union([Schema.String, Schema.Null]) +}).annotate({ "identifier": "UsageResponse" }) +export type ProjectListResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ProjectListResponse = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(Project), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ProjectListResponse" }) +export type ProjectApiKeyListResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ProjectApiKeyListResponse = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(ProjectApiKey), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ProjectApiKeyListResponse" }) +export type ProjectSpendAlertListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ProjectSpendAlertListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(ProjectSpendAlert).annotate({ "description": "Spend alerts returned in the current page." }), + "first_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The ID of the first spend alert in this page." + }), + "last_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The ID of the last spend alert in this page." + }), + "has_more": Schema.Boolean.annotate({ "description": "Whether more spend alerts are available when paginating." }) +}).annotate({ "description": "Paginated list of project spend alerts.", "identifier": "ProjectSpendAlertListResource" }) +export type OrganizationSpendAlertListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const OrganizationSpendAlertListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "Always `list`." }), + "data": Schema.Array(OrganizationSpendAlert).annotate({ + "description": "Spend alerts returned in the current page." + }), + "first_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The ID of the first spend alert in this page." + }), + "last_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The ID of the last spend alert in this page." + }), + "has_more": Schema.Boolean.annotate({ "description": "Whether more spend alerts are available when paginating." }) +}).annotate({ + "description": "Paginated list of organization spend alerts.", + "identifier": "OrganizationSpendAlertListResource" +}) +export type RealtimeTranslationClientSecretCreateRequest = { + readonly "expires_after"?: { readonly "anchor"?: "created_at"; readonly "seconds"?: number } + readonly "session": RealtimeTranslationSessionCreateRequest +} +export const RealtimeTranslationClientSecretCreateRequest = Schema.Struct({ + "expires_after": Schema.optionalKey( + Schema.Struct({ + "anchor": Schema.optionalKey( + Schema.Literal("created_at").annotate({ + "description": + "The anchor point for the client secret expiration, meaning that `seconds` will be added to the `created_at` time of the client secret to produce an expiration timestamp. Only `created_at` is currently supported.\n" + }) + ), + "seconds": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The number of seconds from the anchor point to the expiration. Select a value between `10` and `7200` (2 hours). This default to 600 seconds (10 minutes) if not specified.\n", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(10).annotate({ "expected": "a value greater than or equal to 10" }) + ).check(Schema.isLessThanOrEqualTo(7200).annotate({ "expected": "a value less than or equal to 7200" })) + ) + }).annotate({ + "title": "Client secret expiration", + "description": + "Configuration for the client secret expiration. Expiration refers to the time after which\na client secret will no longer be valid for creating sessions. The session itself may\ncontinue after that time once started. A secret can be used to create multiple sessions\nuntil it expires.\n" + }) + ), + "session": RealtimeTranslationSessionCreateRequest +}).annotate({ + "title": "Realtime translation client secret creation request", + "description": "Create a translation session and client secret for the Realtime API.\n", + "identifier": "RealtimeTranslationClientSecretCreateRequest" +}) +export type RealtimeTranslationClientSecretCreateResponse = { + readonly "value": string + readonly "expires_at": number + readonly "session": RealtimeTranslationSession +} +export const RealtimeTranslationClientSecretCreateResponse = Schema.Struct({ + "value": Schema.String.annotate({ "description": "The generated client secret value." }), + "expires_at": Schema.Number.annotate({ + "description": "Expiration timestamp for the client secret, in seconds since epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "session": RealtimeTranslationSession +}).annotate({ + "title": "Realtime translation session and client secret", + "description": "Response from creating a translation session and client secret for the Realtime API.\n", + "identifier": "RealtimeTranslationClientSecretCreateResponse" +}) +export type PromptCacheDiagnostics = + | PromptCacheMissDiagnosticsBody + | PromptCacheHitDiagnosticsBody + | PromptCacheComparisonResponseNotFoundDiagnosticsBody + | PromptCacheUnavailableDiagnosticsBody +export const PromptCacheDiagnostics = Schema.Union([ + PromptCacheMissDiagnosticsBody, + PromptCacheHitDiagnosticsBody, + PromptCacheComparisonResponseNotFoundDiagnosticsBody, + PromptCacheUnavailableDiagnosticsBody +], { mode: "oneOf" }).annotate({ + "description": "Prompt cache diagnostics requested for this response.", + "identifier": "PromptCacheDiagnostics" +}) +export type CreateThreadRequest = { + readonly "messages"?: ReadonlyArray + readonly "tool_resources"?: { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { + readonly "vector_store_ids": ReadonlyArray + readonly "vector_stores"?: ReadonlyArray< + { + readonly "file_ids"?: ReadonlyArray + readonly "chunking_strategy"?: { readonly "type": "auto" } | { + readonly "type": "static" + readonly "static": { readonly "max_chunk_size_tokens": number; readonly "chunk_overlap_tokens": number } + } + readonly "metadata"?: Metadata + } + > + } | { + readonly "vector_store_ids"?: ReadonlyArray + readonly "vector_stores": ReadonlyArray< + { + readonly "file_ids"?: ReadonlyArray + readonly "chunking_strategy"?: { readonly "type": "auto" } | { + readonly "type": "static" + readonly "static": { readonly "max_chunk_size_tokens": number; readonly "chunk_overlap_tokens": number } + } + readonly "metadata"?: Metadata + } + > + } + } | null + readonly "metadata"?: Metadata +} +export const CreateThreadRequest = Schema.Struct({ + "messages": Schema.optionalKey( + Schema.Array(CreateMessageRequest).annotate({ + "description": + "A list of [messages](https://developers.openai.com/api/docs/assistants/migration) to start the thread with." + }) + ), + "tool_resources": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "vector_store_ids": Schema.Array(Schema.String).annotate({ + "description": + "The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })), + "vector_stores": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.\n" + }).check( + Schema.isMaxLength(100000000).annotate({ "expected": "a value with a length of at most 100000000" }) + ) + ), + "chunking_strategy": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }) }) + .annotate({ + "title": "Auto Chunking Strategy", + "description": + "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`." + }), + Schema.Struct({ + "type": Schema.Literal("static").annotate({ "description": "Always `static`." }), + "static": Schema.Struct({ + "max_chunk_size_tokens": Schema.Number.annotate({ + "description": + "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(100).annotate({ + "expected": "a value greater than or equal to 100" + }) + ).check( + Schema.isLessThanOrEqualTo(4096).annotate({ "expected": "a value less than or equal to 4096" }) + ), + "chunk_overlap_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }) + }).annotate({ "title": "Static Chunking Strategy" }) + ], { mode: "oneOf" }).annotate({ + "description": + "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy." + }) + ), + "metadata": Schema.optionalKey(Metadata) + })).annotate({ + "description": + "A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + }), + Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ), + "vector_stores": Schema.Array(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.\n" + }).check( + Schema.isMaxLength(100000000).annotate({ "expected": "a value with a length of at most 100000000" }) + ) + ), + "chunking_strategy": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }) }) + .annotate({ + "title": "Auto Chunking Strategy", + "description": + "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`." + }), + Schema.Struct({ + "type": Schema.Literal("static").annotate({ "description": "Always `static`." }), + "static": Schema.Struct({ + "max_chunk_size_tokens": Schema.Number.annotate({ + "description": + "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(100).annotate({ + "expected": "a value greater than or equal to 100" + }) + ).check( + Schema.isLessThanOrEqualTo(4096).annotate({ "expected": "a value less than or equal to 4096" }) + ), + "chunk_overlap_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }) + }).annotate({ "title": "Static Chunking Strategy" }) + ], { mode: "oneOf" }).annotate({ + "description": + "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy." + }) + ), + "metadata": Schema.optionalKey(Metadata) + })).annotate({ + "description": + "A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + }) + ], { mode: "oneOf" })) + }).annotate({ + "description": + "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }), + Schema.Null + ])), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ + "description": + "Options to create a new thread. If no thread is provided when running a\nrequest, an empty thread will be created.\n", + "identifier": "CreateThreadRequest" +}) +export type RunStepDeltaStepDetailsToolCallsObject = { + readonly "type": "tool_calls" + readonly "tool_calls"?: ReadonlyArray< + | RunStepDeltaStepDetailsToolCallsCodeObject + | RunStepDeltaStepDetailsToolCallsFileSearchObject + | RunStepDeltaStepDetailsToolCallsFunctionObject + > +} +export const RunStepDeltaStepDetailsToolCallsObject = Schema.Struct({ + "type": Schema.Literal("tool_calls").annotate({ "description": "Always `tool_calls`." }), + "tool_calls": Schema.optionalKey( + Schema.Array( + Schema.Union([ + RunStepDeltaStepDetailsToolCallsCodeObject, + RunStepDeltaStepDetailsToolCallsFileSearchObject, + RunStepDeltaStepDetailsToolCallsFunctionObject + ], { mode: "oneOf" }) + ).annotate({ + "description": + "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.\n" + }) + ) +}).annotate({ + "title": "Tool calls", + "description": "Details of the tool call.", + "identifier": "RunStepDeltaStepDetailsToolCallsObject" +}) +export type MessageObject = { + readonly "id": string + readonly "object": "thread.message" + readonly "created_at": number + readonly "thread_id": string + readonly "status": "in_progress" | "incomplete" | "completed" + readonly "incomplete_details": { + readonly "reason": "content_filter" | "max_tokens" | "run_cancelled" | "run_expired" | "run_failed" + } | null + readonly "completed_at": number | null + readonly "incomplete_at": number | null + readonly "role": "user" | "assistant" + readonly "content": ReadonlyArray< + | MessageContentImageFileObject + | MessageContentImageUrlObject + | MessageContentTextObject + | MessageContentRefusalObject + > + readonly "assistant_id": string | null + readonly "run_id": string | null + readonly "attachments": + | ReadonlyArray< + { + readonly "file_id"?: string + readonly "tools"?: ReadonlyArray + } + > + | null + readonly "metadata": Metadata +} +export const MessageObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("thread.message").annotate({ + "description": "The object type, which is always `thread.message`." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the message was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ + "description": + "The [thread](https://developers.openai.com/api/docs/assistants/migration) ID that this message belongs to." + }), + "status": Schema.Literals(["in_progress", "incomplete", "completed"]).annotate({ + "description": "The status of the message, which can be either `in_progress`, `incomplete`, or `completed`." + }), + "incomplete_details": Schema.Union([ + Schema.Struct({ + "reason": Schema.Literals(["content_filter", "max_tokens", "run_cancelled", "run_expired", "run_failed"]) + .annotate({ "description": "The reason the message is incomplete." }) + }).annotate({ "description": "On an incomplete message, details about why the message is incomplete." }), + Schema.Null + ]), + "completed_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the message was completed.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "incomplete_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the message was marked as incomplete.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "role": Schema.Literals(["user", "assistant"]).annotate({ + "description": "The entity that produced the message. One of `user` or `assistant`." + }), + "content": Schema.Array( + Schema.Union([ + MessageContentImageFileObject, + MessageContentImageUrlObject, + MessageContentTextObject, + MessageContentRefusalObject + ], { mode: "oneOf" }) + ).annotate({ "description": "The content of the message in array of text and/or images." }), + "assistant_id": Schema.Union([ + Schema.String.annotate({ + "description": + "If applicable, the ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) that authored this message." + }), + Schema.Null + ]), + "run_id": Schema.Union([ + Schema.String.annotate({ + "description": + "The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) associated with the creation of this message. Value is `null` when messages are created manually using the create message or create thread endpoints." + }), + Schema.Null + ]), + "attachments": Schema.Union([ + Schema.Array(Schema.Struct({ + "file_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the file to attach to the message." }) + ), + "tools": Schema.optionalKey( + Schema.Array(Schema.Union([AssistantToolsCode, AssistantToolsFileSearchTypeOnly], { mode: "oneOf" })).annotate({ + "description": "The tools to add this file to." + }) + ) + })).annotate({ "description": "A list of files attached to the message, and the tools they were added to." }), + Schema.Null + ]), + "metadata": Metadata +}).annotate({ + "title": "The message object", + "description": "Represents a message within a [thread](https://developers.openai.com/api/docs/assistants/migration).", + "identifier": "MessageObject" +}) +export type MessageDeltaObject = { + readonly "id": string + readonly "object": "thread.message.delta" + readonly "delta": { + readonly "role"?: "user" | "assistant" + readonly "content"?: ReadonlyArray< + | MessageDeltaContentImageFileObject + | MessageDeltaContentTextObject + | MessageDeltaContentRefusalObject + | MessageDeltaContentImageUrlObject + > + } +} +export const MessageDeltaObject = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The identifier of the message, which can be referenced in API endpoints." + }), + "object": Schema.Literal("thread.message.delta").annotate({ + "description": "The object type, which is always `thread.message.delta`." + }), + "delta": Schema.Struct({ + "role": Schema.optionalKey( + Schema.Literals(["user", "assistant"]).annotate({ + "description": "The entity that produced the message. One of `user` or `assistant`." + }) + ), + "content": Schema.optionalKey( + Schema.Array( + Schema.Union([ + MessageDeltaContentImageFileObject, + MessageDeltaContentTextObject, + MessageDeltaContentRefusalObject, + MessageDeltaContentImageUrlObject + ], { mode: "oneOf" }) + ).annotate({ "description": "The content of the message in array of text and/or images." }) + ) + }).annotate({ "description": "The delta containing the fields that have changed on the Message." }) +}).annotate({ + "title": "Message delta object", + "description": "Represents a message delta i.e. any changed fields on a message during streaming.\n", + "identifier": "MessageDeltaObject" +}) +export type ListVectorStoresResponse = + | null + | string + | number + | boolean + | { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean + } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const ListVectorStoresResponse = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(VectorStoreObject), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]).annotate({ "identifier": "ListVectorStoresResponse" }) +export type CreateVectorStoreRequest = { + readonly "file_ids"?: ReadonlyArray + readonly "name"?: string + readonly "description"?: string + readonly "expires_after"?: VectorStoreExpirationAfter + readonly "chunking_strategy"?: AutoChunkingStrategyRequestParam | StaticChunkingStrategyRequestParam + readonly "metadata"?: Metadata +} +export const CreateVectorStoreRequest = Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files." + }).check(Schema.isMaxLength(500).annotate({ "expected": "a value with a length of at most 500" })) + ), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the vector store." })), + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": "A description for the vector store. Can be used to describe the vector store's purpose." + }) + ), + "expires_after": Schema.optionalKey(VectorStoreExpirationAfter), + "chunking_strategy": Schema.optionalKey( + Schema.Union([AutoChunkingStrategyRequestParam, StaticChunkingStrategyRequestParam], { mode: "oneOf" }).annotate({ + "description": + "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty." + }) + ), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ "identifier": "CreateVectorStoreRequest" }) +export type ChunkingStrategyRequestParam = AutoChunkingStrategyRequestParam | StaticChunkingStrategyRequestParam +export const ChunkingStrategyRequestParam = Schema.Union([ + AutoChunkingStrategyRequestParam, + StaticChunkingStrategyRequestParam +], { mode: "oneOf" }).annotate({ + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "identifier": "ChunkingStrategyRequestParam" +}) +export type VectorStoreFileObject = { + readonly "id": string + readonly "object": "vector_store.file" + readonly "usage_bytes": number + readonly "created_at": number + readonly "vector_store_id": string + readonly "status": "in_progress" | "completed" | "cancelled" | "failed" + readonly "last_error": { + readonly "code": "server_error" | "unsupported_file" | "invalid_file" + readonly "message": string + } | null + readonly "chunking_strategy"?: StaticChunkingStrategyResponseParam | OtherChunkingStrategyResponseParam + readonly "attributes"?: VectorStoreFileAttributes +} +export const VectorStoreFileObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("vector_store.file").annotate({ + "description": "The object type, which is always `vector_store.file`." + }), + "usage_bytes": Schema.Number.annotate({ + "description": "The total vector store usage in bytes. Note that this may be different from the original file size." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the vector store file was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "vector_store_id": Schema.String.annotate({ + "description": + "The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to." + }), + "status": Schema.Literals(["in_progress", "completed", "cancelled", "failed"]).annotate({ + "description": + "The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use." + }), + "last_error": Schema.Union([ + Schema.Struct({ + "code": Schema.Literals(["server_error", "unsupported_file", "invalid_file"]).annotate({ + "description": "One of `server_error`, `unsupported_file`, or `invalid_file`." + }), + "message": Schema.String.annotate({ "description": "A human-readable description of the error." }) + }).annotate({ + "description": "The last error associated with this vector store file. Will be `null` if there are no errors." + }), + Schema.Null + ]), + "chunking_strategy": Schema.optionalKey( + Schema.Union([StaticChunkingStrategyResponseParam, OtherChunkingStrategyResponseParam], { mode: "oneOf" }).annotate( + { "description": "The strategy used to chunk the file." } + ) + ), + "attributes": Schema.optionalKey(VectorStoreFileAttributes) +}).annotate({ + "title": "Vector store files", + "description": "A list of files attached to a vector store.", + "identifier": "VectorStoreFileObject" +}) +export type VectorStoreSearchResultsPage = { + readonly "object": "vector_store.search_results.page" + readonly "search_query": ReadonlyArray + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "next_page": string | null +} +export const VectorStoreSearchResultsPage = Schema.Struct({ + "object": Schema.Literal("vector_store.search_results.page").annotate({ + "description": "The object type, which is always `vector_store.search_results.page`" + }), + "search_query": Schema.Array(Schema.String.annotate({ "description": "The query used for this search." })), + "data": Schema.Array(VectorStoreSearchResultItem).annotate({ "description": "The list of search result items." }), + "has_more": Schema.Boolean.annotate({ "description": "Indicates if there are more results to fetch." }), + "next_page": Schema.Union([ + Schema.String.annotate({ "description": "The token for the next page, if any." }), + Schema.Null + ]) +}).annotate({ "identifier": "VectorStoreSearchResultsPage" }) +export type SafetyCaseResource = { + readonly "id": string + readonly "object": "safety.case" + readonly "created_at": number + readonly "entity_identifier": string + readonly "reason": string | null + readonly "notice": SafetyCaseNotice +} +export const SafetyCaseResource = Schema.Struct({ + "id": Schema.String, + "object": Schema.Literal("safety.case"), + "created_at": Schema.Number.annotate({ "format": "unixtime" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "entity_identifier": Schema.String, + "reason": Schema.Union([Schema.String, Schema.Null]), + "notice": SafetyCaseNotice +}).annotate({ "identifier": "SafetyCaseResource" }) +export type DecisionsUsageBucket = { + readonly "object": "bucket" + readonly "start_time": number + readonly "end_time": number + readonly "results": ReadonlyArray +} +export const DecisionsUsageBucket = Schema.Struct({ + "object": Schema.Literal("bucket"), + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "results": Schema.Array(DecisionsUsageResult) +}).annotate({ "identifier": "DecisionsUsageBucket" }) +export type ExternalStorageListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ExternalStorageListResource = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(ExternalStorageResponse), + "first_id": Schema.Union([Schema.String, Schema.Null]), + "last_id": Schema.Union([Schema.String, Schema.Null]), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ExternalStorageListResource" }) +export type OrganizationSpendLimitResource = { + readonly "object": "organization.spend_limit" + readonly "threshold_amount": number + readonly "currency": SpendLimitCurrency + readonly "interval": SpendLimitInterval + readonly "enforcement": SpendLimitEnforcement +} +export const OrganizationSpendLimitResource = Schema.Struct({ + "object": Schema.Literal("organization.spend_limit").annotate({ + "description": "The object type, which is always `organization.spend_limit`." + }), + "threshold_amount": Schema.Number.annotate({ "description": "The hard spend limit amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "currency": Schema.suspend((): Schema.Codec => SpendLimitCurrency).annotate({ + "description": "The currency for the threshold amount. Currently, only `USD` is supported." + }), + "interval": Schema.suspend((): Schema.Codec => SpendLimitInterval).annotate({ + "description": "The time interval for evaluating spend against the threshold. Currently, only `month` is supported." + }), + "enforcement": Schema.suspend((): Schema.Codec => SpendLimitEnforcement).annotate({ + "description": "The current enforcement state of the hard spend limit." + }) +}).annotate({ + "description": "Represents a hard spend limit configured at the organization level.", + "identifier": "OrganizationSpendLimitResource" +}) +export type ProjectSpendLimitResource = { + readonly "object": "project.spend_limit" + readonly "threshold_amount": number + readonly "currency": SpendLimitCurrency + readonly "interval": SpendLimitInterval + readonly "enforcement": SpendLimitEnforcement +} +export const ProjectSpendLimitResource = Schema.Struct({ + "object": Schema.Literal("project.spend_limit").annotate({ + "description": "The object type, which is always `project.spend_limit`." + }), + "threshold_amount": Schema.Number.annotate({ "description": "The hard spend limit amount, in cents." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "currency": Schema.suspend((): Schema.Codec => SpendLimitCurrency).annotate({ + "description": "The currency for the threshold amount. Currently, only `USD` is supported." + }), + "interval": Schema.suspend((): Schema.Codec => SpendLimitInterval).annotate({ + "description": "The time interval for evaluating spend against the threshold. Currently, only `month` is supported." + }), + "enforcement": Schema.suspend((): Schema.Codec => SpendLimitEnforcement).annotate({ + "description": "The current enforcement state of the hard spend limit." + }) +}).annotate({ + "description": "Represents a hard spend limit configured at the project level.", + "identifier": "ProjectSpendLimitResource" +}) +export type ProvenanceResource = { + readonly "object": ProvenanceCheckObject + readonly "created_at": number + readonly "results": ReadonlyArray +} +export const ProvenanceResource = Schema.Struct({ + "object": Schema.suspend((): Schema.Codec => ProvenanceCheckObject).annotate({ + "description": "The object type. Always `content_provenance_check` for this endpoint." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the provenance check was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "results": Schema.Array(Schema.Union([C2PAProvenanceResult, SynthIDProvenanceResult], { mode: "oneOf" })).annotate({ + "description": + "The provenance results that apply to the uploaded file. Image results include C2PA and SynthID; audio results include SynthID." + }) +}).annotate({ "identifier": "ProvenanceResource" }) +export type ChatSessionResource = { + readonly "id": string + readonly "object": "chatkit.session" + readonly "expires_at": number + readonly "client_secret": string + readonly "workflow": ChatkitWorkflow + readonly "user": string + readonly "rate_limits": ChatSessionRateLimits + readonly "max_requests_per_1_minute": number + readonly "status": ChatSessionStatus + readonly "chatkit_configuration": ChatSessionChatkitConfiguration +} +export const ChatSessionResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier for the ChatKit session." }), + "object": Schema.Literal("chatkit.session").annotate({ + "description": "Type discriminator that is always `chatkit.session`." + }), + "expires_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the session expires.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "client_secret": Schema.String.annotate({ + "description": "Ephemeral client secret that authenticates session requests." + }), + "workflow": Schema.suspend((): Schema.Codec => ChatkitWorkflow).annotate({ + "description": "Workflow metadata for the session." + }), + "user": Schema.String.annotate({ "description": "User identifier associated with the session." }), + "rate_limits": Schema.suspend((): Schema.Codec => ChatSessionRateLimits).annotate({ + "description": "Resolved rate limit values." + }), + "max_requests_per_1_minute": Schema.Number.annotate({ + "description": "Convenience copy of the per-minute request limit." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "status": Schema.suspend((): Schema.Codec => ChatSessionStatus).annotate({ + "description": "Current lifecycle state of the session." + }), + "chatkit_configuration": Schema.suspend((): Schema.Codec => + ChatSessionChatkitConfiguration + ).annotate({ "description": "Resolved ChatKit feature configuration for the session." }) +}).annotate({ + "title": "The chat session object", + "description": "Represents a ChatKit session and its resolved configuration.", + "identifier": "ChatSessionResource" +}) +export type CreateChatSessionBody = { + readonly "workflow": WorkflowParam + readonly "user": string + readonly "expires_after"?: ExpiresAfterParam + readonly "rate_limits"?: RateLimitsParam + readonly "chatkit_configuration"?: ChatkitConfigurationParam +} +export const CreateChatSessionBody = Schema.Struct({ + "workflow": Schema.suspend((): Schema.Codec => WorkflowParam).annotate({ + "description": "Workflow that powers the session." + }), + "user": Schema.String.annotate({ + "description": + "A free-form string that identifies your end user; ensures this Session can access other objects that have the same `user` scope." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "expires_after": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ExpiresAfterParam).annotate({ + "description": "Optional override for session expiration timing in seconds from creation. Defaults to 10 minutes." + }) + ), + "rate_limits": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RateLimitsParam).annotate({ + "description": "Optional override for per-minute request limits. When omitted, defaults to 10." + }) + ), + "chatkit_configuration": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ChatkitConfigurationParam).annotate({ + "description": "Optional overrides for ChatKit runtime configuration features" + }) + ) +}).annotate({ + "title": "Create chat session request", + "description": "Parameters for provisioning a new ChatKit session.", + "identifier": "CreateChatSessionBody" +}) +export type UserMessageItem = { + readonly "id": string + readonly "object": "chatkit.thread_item" + readonly "created_at": number + readonly "thread_id": string + readonly "type": "chatkit.user_message" + readonly "content": ReadonlyArray + readonly "attachments": ReadonlyArray + readonly "inference_options": InferenceOptions | null +} +export const UserMessageItem = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread item." }), + "object": Schema.Literal("chatkit.thread_item").annotate({ + "description": "Type discriminator that is always `chatkit.thread_item`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the item was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ "description": "Identifier of the parent thread." }), + "type": Schema.Literal("chatkit.user_message"), + "content": Schema.Array( + Schema.Union([UserMessageInputText, UserMessageQuotedText], { mode: "oneOf" }).annotate({ + "description": "Content blocks that comprise a user message." + }) + ).annotate({ "description": "Ordered content elements supplied by the user." }), + "attachments": Schema.Array(Attachment).annotate({ + "description": "Attachments associated with the user message. Defaults to an empty list." + }), + "inference_options": Schema.Union([ + Schema.suspend((): Schema.Codec => InferenceOptions).annotate({ + "description": "Inference overrides applied to the message. Defaults to null when unset." + }), + Schema.Null + ]) +}).annotate({ + "title": "User Message Item", + "description": "User-authored messages within a thread.", + "identifier": "UserMessageItem" +}) +export type ResponseOutputText = { + readonly "type": "output_text" + readonly "text": string + readonly "annotations": ReadonlyArray +} +export const ResponseOutputText = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ "description": "Type discriminator that is always `output_text`." }), + "text": Schema.String.annotate({ "description": "Assistant generated text." }), + "annotations": Schema.Array( + Schema.Union([FileAnnotation, UrlAnnotation], { mode: "oneOf" }).annotate({ + "description": "Annotation object describing a cited source." + }) + ).annotate({ "description": "Ordered list of annotations attached to the response text." }) +}).annotate({ + "title": "Assistant message content", + "description": "Assistant response text accompanied by optional annotations.", + "identifier": "ResponseOutputText" +}) +export type TaskGroupItem = { + readonly "id": string + readonly "object": "chatkit.thread_item" + readonly "created_at": number + readonly "thread_id": string + readonly "type": "chatkit.task_group" + readonly "tasks": ReadonlyArray +} +export const TaskGroupItem = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread item." }), + "object": Schema.Literal("chatkit.thread_item").annotate({ + "description": "Type discriminator that is always `chatkit.thread_item`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the item was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ "description": "Identifier of the parent thread." }), + "type": Schema.Literal("chatkit.task_group").annotate({ + "description": "Type discriminator that is always `chatkit.task_group`." + }), + "tasks": Schema.Array(TaskGroupTask).annotate({ "description": "Tasks included in the group." }) +}).annotate({ + "title": "Task group", + "description": "Collection of workflow tasks grouped together in the thread.", + "identifier": "TaskGroupItem" +}) +export type ThreadListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ThreadListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(ThreadResource).annotate({ "description": "A list of items" }), + "first_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the first item in the list." }), + Schema.Null + ]), + "last_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the last item in the list." }), + Schema.Null + ]), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }) +}).annotate({ + "title": "Threads", + "description": "A paginated list of ChatKit threads.", + "identifier": "ThreadListResource" +}) +export type DecisionInputContentParts = ReadonlyArray +export const DecisionInputContentParts = Schema.Array( + Schema.Union([DecisionInputText, DecisionInputImage], { mode: "oneOf" }) +).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ + "expected": "a value with a length of at most 16384", + "identifier": "DecisionInputContentParts" + }) +) +export type QuestionParamChoice = { + readonly "type": "choice" + readonly "name"?: string + readonly "instructions": string + readonly "choices": ReadonlyArray +} +export const QuestionParamChoice = Schema.Struct({ + "type": Schema.Literal("choice").annotate({ "description": "The type of the object. Always `choice`." }), + "name": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "instructions": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "choices": Schema.Array(ChoiceOptionParam).annotate({ + "description": "Provide between 2 and 255 choices. Each choice must be unique." + }).check(Schema.isMinLength(2).annotate({ "expected": "a value with a length of at least 2" })).check( + Schema.isMaxLength(255).annotate({ "expected": "a value with a length of at most 255" }) + ) +}).annotate({ + "description": "Choose from the supplied options based on the input.", + "identifier": "QuestionParamChoice" +}) +export type AnswerResourceChoice = { + readonly "type": "choice" + readonly "name": string | null + readonly "choice": ChoiceValueResource + readonly "probabilities": ReadonlyArray + readonly "confidence": number +} +export const AnswerResourceChoice = Schema.Struct({ + "type": Schema.Literal("choice").annotate({ "description": "The type of the object. Always `choice`." }), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]), + "choice": ChoiceValueResource, + "probabilities": Schema.Array(ChoiceProbabilityResource).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "confidence": Schema.Finite +}).annotate({ "identifier": "AnswerResourceChoice" }) +export type PublicEnvironmentResource = { + readonly "type": EnvironmentTypeResource + readonly "id": string + readonly "object": "agent.environment" + readonly "status": EnvironmentStatusResource + readonly "files": ReadonlyArray + readonly "skills": ReadonlyArray + readonly "plugins": ReadonlyArray +} +export const PublicEnvironmentResource = Schema.Struct({ + "type": Schema.suspend((): Schema.Codec => EnvironmentTypeResource).annotate({ + "description": "Whether the environment is hosted by OpenAI or by the application." + }), + "id": Schema.String.annotate({ "description": "The ID of the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.environment").annotate({ + "description": "The object type. Always `agent.environment`." + }), + "status": Schema.suspend((): Schema.Codec => EnvironmentStatusResource).annotate({ + "description": "The current environment connection status." + }), + "files": Schema.Array(HostedEnvironmentFileResource).annotate({ + "description": "Files installed in the environment, without their contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "skills": Schema.Array(HostedSkillResource).annotate({ + "description": "Skills installed in the environment, without their archive contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "plugins": Schema.Array(HostedPluginResource).annotate({ + "description": "Plugins installed in the environment, without their archive contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) +}).annotate({ + "description": "Safe metadata for a first-class execution environment.", + "identifier": "PublicEnvironmentResource" +}) +export type HostedSkillParamInline = { + readonly "type": "inline" + readonly "name": string + readonly "description": string + readonly "source": InlineCapabilitySourceParam +} +export const HostedSkillParamInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "name": Schema.String.annotate({ "description": "The skill name declared in `SKILL.md`." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "description": Schema.String.annotate({ "description": "The skill description declared in `SKILL.md`." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "source": Schema.suspend((): Schema.Codec => InlineCapabilitySourceParam).annotate({ + "description": "The inline ZIP archive." + }) +}).annotate({ + "description": "Supplies a skill ZIP directly in the session request.", + "identifier": "HostedSkillParamInline" +}) +export type HostedPluginParamInline = { + readonly "type": "inline" + readonly "name": string + readonly "description": string + readonly "source": InlineCapabilitySourceParam +} +export const HostedPluginParamInline = Schema.Struct({ + "type": Schema.Literal("inline").annotate({ "description": "The type of the object. Always `inline`." }), + "name": Schema.String.annotate({ "description": "The plugin name declared in `.codex-plugin/plugin.json`." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "description": Schema.String.annotate({ + "description": "The plugin description declared in `.codex-plugin/plugin.json`." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "source": Schema.suspend((): Schema.Codec => InlineCapabilitySourceParam).annotate({ + "description": "The inline ZIP archive." + }) +}).annotate({ + "description": "Supplies a plugin ZIP directly in the session request.", + "identifier": "HostedPluginParamInline" +}) +export type SubagentResource = { + readonly "id": string + readonly "object": SubagentObjectResource + readonly "session_id": string + readonly "name": string | null + readonly "instructions": ReadonlyArray | null + readonly "parent_agent_id": string + readonly "status": SubagentStatusResource + readonly "opened_at": number + readonly "closed_at": number | null +} +export const SubagentResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the subagent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.suspend((): Schema.Codec => SubagentObjectResource).annotate({ + "description": "The object type. Always `agent.session.subagent`." + }), + "session_id": Schema.String.annotate({ "description": "The ID of the session that owns the subagent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The runner-assigned nickname, or null when unavailable." }), + "instructions": Schema.Union([ + Schema.Array(AgentContentResource).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + Schema.Null + ]).annotate({ + "description": + "Initial task content, or null when unavailable. Text may contain placeholders for images or audio when only a preview is available." + }), + "parent_agent_id": Schema.String.annotate({ "description": "The ID of the agent that created this subagent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => SubagentStatusResource).annotate({ + "description": "The current status of the subagent." + }), + "opened_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the subagent was first opened. Resuming does not change it.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "closed_at": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ + "description": + "The Unix timestamp, in seconds, when the subagent was closed. Null while active, including after resume.", + "format": "int64" + }) +}).annotate({ "description": "A subagent created within a session.", "identifier": "SubagentResource" }) +export type AgentMessageItemResource = { + readonly "id": string + readonly "turn_id": string + readonly "type": "agent_message" + readonly "sender_agent_id": string + readonly "recipient_agent_id": string + readonly "content": ReadonlyArray +} +export const AgentMessageItemResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the message." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "type": Schema.Literal("agent_message").annotate({ "description": "The item type. Always `agent_message`." }), + "sender_agent_id": Schema.String.annotate({ "description": "The ID or name of the sending agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "recipient_agent_id": Schema.String.annotate({ "description": "The ID or name of the receiving agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "content": Schema.Array(AgentContentResource).annotate({ "description": "The content exchanged between the agents." }) + .check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) +}).annotate({ "description": "A message exchanged between agent threads.", "identifier": "AgentMessageItemResource" }) +export type CreateSubagentCallItemResource = { + readonly "type": "create_subagent_call" + readonly "id": string + readonly "turn_id": string + readonly "status": FunctionCallStatusResource + readonly "agent_id": string + readonly "content": ReadonlyArray + readonly "model": string | null + readonly "reasoning_effort": string | null +} +export const CreateSubagentCallItemResource = Schema.Struct({ + "type": Schema.Literal("create_subagent_call").annotate({ + "description": "The item type. Always `create_subagent_call`." + }), + "id": Schema.String.annotate({ "description": "The ID of the tool call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the tool call." + }), + "agent_id": Schema.String.annotate({ "description": "The ID of the agent that requested the subagent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "content": Schema.Array(AgentContentResource).annotate({ "description": "The task given to the spawned agent." }) + .check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "model": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The model requested for the spawned agent." }), + "reasoning_effort": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The reasoning effort requested for the spawned agent." }) +}).annotate({ "description": "A request to spawn a subagent.", "identifier": "CreateSubagentCallItemResource" }) +export type SendSubagentInputCallItemResource = { + readonly "type": "send_subagent_input_call" + readonly "id": string + readonly "turn_id": string + readonly "status": FunctionCallStatusResource + readonly "sender_agent_id": string + readonly "recipient_agent_id": string + readonly "content": ReadonlyArray +} +export const SendSubagentInputCallItemResource = Schema.Struct({ + "type": Schema.Literal("send_subagent_input_call").annotate({ + "description": "The item type. Always `send_subagent_input_call`." + }), + "id": Schema.String.annotate({ "description": "The ID of the tool call item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the tool call." + }), + "sender_agent_id": Schema.String.annotate({ "description": "The ID of the agent sending the input." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "recipient_agent_id": Schema.String.annotate({ "description": "The ID of the agent receiving the input." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "content": Schema.Array(AgentContentResource).annotate({ "description": "The input sent to the receiving agent." }) + .check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) +}).annotate({ + "description": "A request to send input to another agent.", + "identifier": "SendSubagentInputCallItemResource" +}) +export type MessageItemResource = { + readonly "type": "message" + readonly "id": string | null + readonly "turn_id": string + readonly "role": SessionMessageRoleResource + readonly "content": ReadonlyArray + readonly "status": OutputItemStatusResource + readonly "phase": MessagePhaseResource | null +} +export const MessageItemResource = Schema.Struct({ + "type": Schema.Literal("message").annotate({ "description": "The item type. Always `message`." }), + "id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of this item, or null for legacy user messages whose ID was not recorded." }), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "role": Schema.suspend((): Schema.Codec => SessionMessageRoleResource).annotate({ + "description": "The role of the message author." + }), + "content": Schema.Array(MessageContentResource).annotate({ + "description": + "The content of the message. User messages contain input text or images; assistant messages contain output text." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "status": Schema.suspend((): Schema.Codec => OutputItemStatusResource).annotate({ + "description": "The status of the message. User messages are always `completed`." + }), + "phase": Schema.Union([MessagePhaseResource, Schema.Null]).annotate({ + "description": "The phase of an assistant message. Null for user messages." + }) +}).annotate({ + "description": "A user or assistant message recorded in a session.", + "identifier": "MessageItemResource" +}) +export type FunctionCallOutputResource = string | ReadonlyArray +export const FunctionCallOutputResource = Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Array(InputContentResource).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })) +], { mode: "oneOf" }).annotate({ + "description": "The text or model-input content supplied as a function result.", + "identifier": "FunctionCallOutputResource" +}) +export type BrowserAuthenticationHistoryRequestKindResource = + BrowserAuthenticationHistoryRequestKindResourceBrowserAuthentication +export const BrowserAuthenticationHistoryRequestKindResource = Schema.Union([ + BrowserAuthenticationHistoryRequestKindResourceBrowserAuthentication +], { mode: "oneOf" }).annotate({ "identifier": "BrowserAuthenticationHistoryRequestKindResource" }) +export type ComputerUseApprovalRequestKindResource = + | ComputerUseApprovalRequestKindResourceBrowserAuthentication + | ComputerUseApprovalRequestKindResourceBrowserOriginAccess +export const ComputerUseApprovalRequestKindResource = Schema.Union([ + ComputerUseApprovalRequestKindResourceBrowserAuthentication, + ComputerUseApprovalRequestKindResourceBrowserOriginAccess +], { mode: "oneOf" }).annotate({ "identifier": "ComputerUseApprovalRequestKindResource" }) +export type ComputerUseApprovalRequestResultItemResource = { + readonly "id": string + readonly "type": "computer_use_approval_request_result" + readonly "turn_id": string + readonly "request_id": string + readonly "response": ComputerUseApprovalResponseKindResource +} +export const ComputerUseApprovalRequestResultItemResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The stable history item ID." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "type": Schema.Literal("computer_use_approval_request_result"), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "request_id": Schema.String.annotate({ "description": "The registered request answered by this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "response": Schema.suspend((): Schema.Codec => + ComputerUseApprovalResponseKindResource + ).annotate({ "description": "The admitted response, without submitted credential values." }) +}).annotate({ + "description": "A credential-free record of an admitted response, not proof of completion.", + "identifier": "ComputerUseApprovalRequestResultItemResource" +}) +export type WebSearchCallItemResource = { + readonly "type": "web_search_call" + readonly "id": string + readonly "turn_id": string + readonly "status": OutputItemStatusResource + readonly "action": WebSearchActionResource | null +} +export const WebSearchCallItemResource = Schema.Struct({ + "type": Schema.Literal("web_search_call").annotate({ "description": "The item type. Always `web_search_call`." }), + "id": Schema.String.annotate({ "description": "The ID of the web search call." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => OutputItemStatusResource).annotate({ + "description": "The status of the web search call." + }), + "action": Schema.Union([WebSearchActionResource, Schema.Null]).annotate({ + "description": "The action performed by the web search tool." + }) +}).annotate({ "description": "A web search call produced by the agent.", "identifier": "WebSearchCallItemResource" }) +export type TurnResource = { + readonly "id": string + readonly "object": TurnObjectResource + readonly "session_id": string + readonly "agent_id": string + readonly "subagent_id": string | null + readonly "status": TurnStatusResource + readonly "created_at": number + readonly "started_at": number | null + readonly "completed_at": number | null + readonly "error": SessionTurnErrorResource | null + readonly "usage": TokenUsageResource | null +} +export const TurnResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the turn." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.suspend((): Schema.Codec => TurnObjectResource).annotate({ + "description": "The object type. Always `agent.session.turn`." + }), + "session_id": Schema.String.annotate({ "description": "The ID of the session that owns the turn." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "agent_id": Schema.String.annotate({ "description": "The ID of the agent that ran the turn." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "subagent_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the subagent that ran the turn, if applicable." }), + "status": Schema.suspend((): Schema.Codec => TurnStatusResource).annotate({ + "description": "The current status of the turn." + }), + "created_at": Schema.Number.annotate({ + "description": + "The Unix timestamp, in seconds, used to order the turn by creation time. Subagent turns use their start time, falling back to completion time or the subagent opening time when the preceding timestamps are unavailable.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "started_at": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The Unix timestamp, in seconds, when the turn started.", "format": "int64" }), + "completed_at": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ + "description": "The Unix timestamp, in seconds, when the turn reached a terminal state.", + "format": "int64" + }), + "error": Schema.Union([SessionTurnErrorResource, Schema.Null]).annotate({ + "description": "A customer-safe error. Non-null only for a failed turn." + }), + "usage": Schema.Union([TokenUsageResource, Schema.Null]).annotate({ + "description": "Best-effort token usage for the turn, or null if unknown. Recorded usage may change." + }) +}).annotate({ "description": "The canonical public representation of a session turn.", "identifier": "TurnResource" }) +export type TextResource = { readonly "format": TextFormatResource; readonly "verbosity": VerbosityResource } +export const TextResource = Schema.Struct({ + "format": Schema.suspend((): Schema.Codec => TextFormatResource).annotate({ + "description": "The effective output format. Defaults to ordinary text." + }), + "verbosity": Schema.suspend((): Schema.Codec => VerbosityResource).annotate({ + "description": "The amount of text produced by the agent. Defaults to `medium`." + }) +}).annotate({ "description": "The text configuration used by an agent.", "identifier": "TextResource" }) +export type PersistedAgentToolResourceMcp = { + readonly "type": "mcp" + readonly "server_label": string + readonly "credential_id": string | null + readonly "transport": PersistedMcpTransportResource + readonly "request_metadata": { readonly [x: string]: Schema.Json } + readonly "allowed_tools": ReadonlyArray | null + readonly "required": boolean + readonly "connection_origin": McpConnectionOriginResource +} +export const PersistedAgentToolResourceMcp = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The type of the object. Always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "A label used to identify the MCP server in tool calls." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "credential_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The vault credential selected for this MCP server, if any." }), + "transport": Schema.suspend((): Schema.Codec => PersistedMcpTransportResource) + .annotate({ "description": "The credential-free transport used to connect to the MCP server." }), + "request_metadata": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Metadata included with requests to this MCP server." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "allowed_tools": Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + Schema.Null + ]).annotate({ "description": "The MCP tools the agent may call, or null when all server tools are allowed." }), + "required": Schema.Boolean.annotate({ + "description": "Whether this MCP server must initialize before the first turn." + }), + "connection_origin": Schema.suspend((): Schema.Codec => McpConnectionOriginResource) + .annotate({ "description": "Where outbound MCP HTTP connections originate." }) +}).annotate({ + "description": "Tools provided by a remote MCP server without stored credentials.", + "identifier": "PersistedAgentToolResourceMcp" +}) +export type UpdateSessionAgentParam = { + readonly "model"?: string + readonly "reasoning"?: UpdateSessionReasoningParam + readonly "service_tier"?: ServiceTierParam | null +} +export const UpdateSessionAgentParam = Schema.Struct({ + "model": Schema.optionalKey( + Schema.String.annotate({ "description": "The model for subsequent turns. Omit to keep the current model." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "reasoning": Schema.optionalKey( + Schema.suspend((): Schema.Codec => UpdateSessionReasoningParam).annotate({ + "description": "Reasoning settings to update. Omit to keep the current effort." + }) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ServiceTierParam, Schema.Null]).annotate({ + "description": "Omit to keep the current tier. Null resets it to auto." + }) + ) +}).annotate({ + "description": "Model settings that can change after session creation.", + "identifier": "UpdateSessionAgentParam" +}) +export type TextParam = { readonly "format"?: TextFormatParam | null; readonly "verbosity"?: VerbosityParam | null } +export const TextParam = Schema.Struct({ + "format": Schema.optionalKey( + Schema.Union([TextFormatParam, Schema.Null]).annotate({ + "description": "The output format. Omission uses ordinary text (`{\"type\": \"text\"}`)." + }) + ), + "verbosity": Schema.optionalKey( + Schema.Union([VerbosityParam, Schema.Null]).annotate({ + "description": "The amount of text the model should produce. Defaults to `medium`, matching Responses." + }) + ) +}).annotate({ "description": "Configuration for text generated by the agent.", "identifier": "TextParam" }) +export type PersistedAgentToolConfigParamMcp = { + readonly "type": "mcp" + readonly "server_label": string + readonly "credential_id"?: string | null + readonly "transport": PersistedMcpTransportConfigParam + readonly "request_metadata"?: { readonly [x: string]: Schema.Json } | null + readonly "allowed_tools"?: ReadonlyArray | null + readonly "required"?: boolean + readonly "connection_origin"?: McpConnectionOriginParam | null +} +export const PersistedAgentToolConfigParamMcp = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The type of the object. Always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "A label used to identify the MCP server in tool calls." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "credential_id": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "The vault credential selected for this MCP server. Optional when exactly one attached credential matches the server URL." + }) + ), + "transport": Schema.suspend((): Schema.Codec => PersistedMcpTransportConfigParam) + .annotate({ "description": "The credential-free transport used to connect to the MCP server." }), + "request_metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).check( + Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" }) + ).check(Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Metadata included with requests to this MCP server." }) + ), + "allowed_tools": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "The MCP tools the agent may call. All server tools are allowed when omitted." }) + ), + "required": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this MCP server must initialize before the first turn. Defaults to `false`." + }) + ), + "connection_origin": Schema.optionalKey( + Schema.Union([McpConnectionOriginParam, Schema.Null]).annotate({ + "description": "Selects where outbound MCP HTTP connections originate." + }) + ) +}).annotate({ + "description": "Tools provided by a remote MCP server without stored credentials.", + "identifier": "PersistedAgentToolConfigParamMcp" +}) +export type EnvironmentResourceOpenaiHosted = { + readonly "type": "openai_hosted" + readonly "id": string + readonly "container_size"?: ContainerSizeResource | null + readonly "packages": EnvironmentPackagesResource + readonly "network": NetworkPolicyResource + readonly "desktop": DesktopResource + readonly "capability_directories": ReadonlyArray + readonly "skills": ReadonlyArray + readonly "plugins": ReadonlyArray + readonly "files": ReadonlyArray +} +export const EnvironmentResourceOpenaiHosted = Schema.Struct({ + "type": Schema.Literal("openai_hosted").annotate({ + "description": "The type of the object. Always `openai_hosted`." + }), + "id": Schema.String.annotate({ "description": "The public ID of the environment." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "container_size": Schema.optionalKey( + Schema.Union([ContainerSizeResource, Schema.Null]).annotate({ + "description": "The effective CPU and memory tier, or null when unknown or outside the public tiers." + }) + ), + "packages": Schema.suspend((): Schema.Codec => EnvironmentPackagesResource).annotate({ + "description": "Packages installed in the environment." + }), + "network": Schema.suspend((): Schema.Codec => NetworkPolicyResource).annotate({ + "description": "The effective network access policy for the environment." + }), + "desktop": Schema.suspend((): Schema.Codec => DesktopResource).annotate({ + "description": "The effective desktop configuration." + }), + "capability_directories": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Directories that contain capabilities exposed to the agent." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "skills": Schema.Array(HostedSkillResource).annotate({ + "description": "Skills installed in the environment, excluding their archive contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "plugins": Schema.Array(HostedPluginResource).annotate({ + "description": "Plugins installed in the environment, excluding their archive contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "files": Schema.Array(HostedEnvironmentFileResource).annotate({ + "description": "Files available in the environment, excluding their contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" }) + ) +}).annotate({ "description": "An environment hosted by OpenAI.", "identifier": "EnvironmentResourceOpenaiHosted" }) +export type EnvironmentTemplateResource = { + readonly "id": string + readonly "name": string | null + readonly "object": "agent.environment.template" + readonly "created_at": number + readonly "updated_at": number + readonly "packages": EnvironmentPackagesResource + readonly "network": NetworkPolicyResource + readonly "desktop": DesktopResource + readonly "capability_directories": ReadonlyArray + readonly "skills": ReadonlyArray + readonly "plugins": ReadonlyArray + readonly "files": ReadonlyArray +} +export const EnvironmentTemplateResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the reusable environment template." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "An optional human-readable display name for the template." }), + "object": Schema.Literal("agent.environment.template").annotate({ + "description": "The object type. Always `agent.environment.template`." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the template was created.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "updated_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the template was last updated.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "packages": Schema.suspend((): Schema.Codec => EnvironmentPackagesResource).annotate({ + "description": "Packages installed in each fresh OpenAI-hosted environment." + }), + "network": Schema.suspend((): Schema.Codec => NetworkPolicyResource).annotate({ + "description": "Runtime network access for each OpenAI-hosted environment." + }), + "desktop": Schema.suspend((): Schema.Codec => DesktopResource).annotate({ + "description": "Desktop configuration for each OpenAI-hosted environment." + }), + "capability_directories": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Directories that expose capabilities to the agent." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "skills": Schema.Array(HostedTemplateSkillResource).annotate({ + "description": "Safe skill metadata, preserving unresolved version selectors." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" }) + ), + "plugins": Schema.Array(HostedPluginResource).annotate({ + "description": "Safe plugin metadata, excluding inline archive contents." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(32).annotate({ "expected": "a value with a length of at most 32" }) + ), + "files": Schema.Array(HostedTemplateFileResource).annotate({ + "description": "Safe file metadata, excluding contents and session-scoped file IDs." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" }) + ) +}).annotate({ + "description": "Reusable configuration that provisions a fresh OpenAI-hosted environment for each session.", + "identifier": "EnvironmentTemplateResource" +}) +export type AgentToolResourceMcp = { + readonly "type": "mcp" + readonly "server_label": string + readonly "credential_id": string | null + readonly "transport": McpTransportResource + readonly "request_metadata": { readonly [x: string]: Schema.Json } + readonly "allowed_tools": ReadonlyArray | null + readonly "required": boolean + readonly "connection_origin": McpConnectionOriginResource +} +export const AgentToolResourceMcp = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The type of the object. Always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "A label used to identify the MCP server in tool calls." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "credential_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The attached vault credential selected for this MCP server, if any. Optional when exactly one attached credential matches the server URL." + }), + "transport": Schema.suspend((): Schema.Codec => McpTransportResource).annotate({ + "description": "The transport used to connect to the MCP server." + }), + "request_metadata": Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Metadata included with requests to this MCP server." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "allowed_tools": Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + Schema.Null + ]).annotate({ "description": "The MCP tools the agent may call." }), + "required": Schema.Boolean.annotate({ + "description": "Whether this MCP server must initialize before the first turn." + }), + "connection_origin": Schema.suspend((): Schema.Codec => McpConnectionOriginResource) + .annotate({ "description": "Where outbound MCP HTTP connections originate." }) +}).annotate({ "description": "Tools provided by a remote MCP server.", "identifier": "AgentToolResourceMcp" }) +export type AgentToolConfigParamMcp = { + readonly "type": "mcp" + readonly "server_label": string + readonly "credential_id"?: string | null + readonly "transport": McpTransportConfigParam + readonly "request_metadata"?: { readonly [x: string]: Schema.Json } | null + readonly "allowed_tools"?: ReadonlyArray | null + readonly "required"?: boolean + readonly "connection_origin"?: McpConnectionOriginParam | null +} +export const AgentToolConfigParamMcp = Schema.Struct({ + "type": Schema.Literal("mcp").annotate({ "description": "The type of the object. Always `mcp`." }), + "server_label": Schema.String.annotate({ "description": "A label used to identify the MCP server in tool calls." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "credential_id": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "The attached vault credential used to authenticate this MCP server. Optional when exactly one attached credential matches the server URL." + }) + ), + "transport": Schema.suspend((): Schema.Codec => McpTransportConfigParam).annotate({ + "description": "The transport used to connect to the MCP server." + }), + "request_metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).check( + Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" }) + ).check(Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" })).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Metadata included with requests to this MCP server." }) + ), + "allowed_tools": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "The MCP tools the agent may call. All server tools are allowed when omitted." }) + ), + "required": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether this MCP server must initialize before the first turn. Defaults to `false`." + }) + ), + "connection_origin": Schema.optionalKey( + Schema.Union([McpConnectionOriginParam, Schema.Null]).annotate({ + "description": + "Selects where outbound MCP HTTP connections originate. Omitted or `service` uses the Managed Agents service network; `environment` uses the session's selected environment." + }) + ) +}).annotate({ "description": "Tools provided by a remote MCP server.", "identifier": "AgentToolConfigParamMcp" }) +export type InputMessageParam = { + readonly "type"?: "message" + readonly "role": "user" + readonly "content": ReadonlyArray +} +export const InputMessageParam = Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The type of the input item. Always `message`." }) + ), + "role": Schema.Literal("user").annotate({ "description": "The role of the message author. Always `user`." }), + "content": Schema.Array(InputContentParam).annotate({ "description": "The content of the message." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })) +}).annotate({ "description": "A user message submitted to a session.", "identifier": "InputMessageParam" }) +export type FunctionCallOutputParam = string | ReadonlyArray +export const FunctionCallOutputParam = Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + Schema.Array(InputContentParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })) +], { mode: "oneOf" }).annotate({ + "description": "A function result represented as text or supported model-input content.", + "identifier": "FunctionCallOutputParam" +}) +export type SessionEventAgentSessionEnvironmentReady = { + readonly "type": "agent.session.environment.ready" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentReady = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.ready").annotate({ + "description": "The type of the object. Always `agent.session.environment.ready`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted when a hosted session environment is ready to connect.", + "identifier": "SessionEventAgentSessionEnvironmentReady" +}) +export type SessionEventAgentSessionEnvironmentSuspended = { + readonly "type": "agent.session.environment.suspended" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentSuspended = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.suspended").annotate({ + "description": "The type of the object. Always `agent.session.environment.suspended`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted after an idle hosted session environment is checkpointed and stopped.", + "identifier": "SessionEventAgentSessionEnvironmentSuspended" +}) +export type SessionEventAgentSessionEnvironmentExpired = { + readonly "type": "agent.session.environment.expired" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentExpired = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.expired").annotate({ + "description": "The type of the object. Always `agent.session.environment.expired`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted after a suspended hosted session environment and its checkpoint expire.", + "identifier": "SessionEventAgentSessionEnvironmentExpired" +}) +export type SessionEventAgentSessionEnvironmentPending = { + readonly "type": "agent.session.environment.pending" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentPending = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.pending").annotate({ + "description": "The type of the object. Always `agent.session.environment.pending`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted while a session environment is being prepared.", + "identifier": "SessionEventAgentSessionEnvironmentPending" +}) +export type SessionEventAgentSessionEnvironmentConnected = { + readonly "type": "agent.session.environment.connected" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentConnected = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.connected").annotate({ + "description": "The type of the object. Always `agent.session.environment.connected`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted when a session environment connects.", + "identifier": "SessionEventAgentSessionEnvironmentConnected" +}) +export type SessionEventAgentSessionEnvironmentDisconnected = { + readonly "type": "agent.session.environment.disconnected" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentDisconnected = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.disconnected").annotate({ + "description": "The type of the object. Always `agent.session.environment.disconnected`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted when a session environment disconnects.", + "identifier": "SessionEventAgentSessionEnvironmentDisconnected" +}) +export type SessionEventAgentSessionEnvironmentFailed = { + readonly "type": "agent.session.environment.failed" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "environment": SessionEnvironmentStateResource +} +export const SessionEventAgentSessionEnvironmentFailed = Schema.Struct({ + "type": Schema.Literal("agent.session.environment.failed").annotate({ + "description": "The type of the object. Always `agent.session.environment.failed`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "environment": Schema.suspend((): Schema.Codec => SessionEnvironmentStateResource) + .annotate({ "description": "The current environment state." }) +}).annotate({ + "description": "Emitted when a session environment fails.", + "identifier": "SessionEventAgentSessionEnvironmentFailed" +}) +export type ComputerUseApprovalResponseParam = + | ComputerUseApprovalResponseParamBrowserAuthentication + | ComputerUseApprovalResponseParamBrowserOriginAccessParam +export const ComputerUseApprovalResponseParam = Schema.Union([ + ComputerUseApprovalResponseParamBrowserAuthentication, + ComputerUseApprovalResponseParamBrowserOriginAccessParam +], { mode: "oneOf" }).annotate({ "identifier": "ComputerUseApprovalResponseParam" }) +export type McpOauthRefreshResource = { + readonly "token_endpoint": string + readonly "client_id": string + readonly "resource": string | null + readonly "scope": string | null + readonly "token_endpoint_auth": McpOauthTokenEndpointAuthResource +} +export const McpOauthRefreshResource = Schema.Struct({ + "token_endpoint": Schema.String.annotate({ "description": "The HTTPS OAuth token endpoint used for refresh." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "client_id": Schema.String.annotate({ "description": "The OAuth client ID used when requesting a new access token." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "resource": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The resource URI sent to the OAuth token endpoint during refresh, if configured." }), + "scope": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "Space-separated OAuth scopes requested during refresh, if configured." }), + "token_endpoint_auth": Schema.suspend((): Schema.Codec => + McpOauthTokenEndpointAuthResource + ).annotate({ + "description": "How the OAuth client authenticates to the token endpoint, excluding its client secret." + }) +}).annotate({ + "description": "Configuration used to refresh an MCP OAuth access token, excluding secret values.", + "identifier": "McpOauthRefreshResource" +}) +export type VaultCredentialAuthResourceEnvironmentVariable = { + readonly "type": "environment_variable" + readonly "secret_name": string + readonly "networking": VaultCredentialNetworkingResource +} +export const VaultCredentialAuthResourceEnvironmentVariable = Schema.Struct({ + "type": Schema.Literal("environment_variable").annotate({ + "description": "The type of the object. Always `environment_variable`." + }), + "secret_name": Schema.String.annotate({ + "description": "The environment variable name that receives the placeholder in the sandbox." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "networking": Schema.suspend((): Schema.Codec => VaultCredentialNetworkingResource) + .annotate({ + "description": + "The destinations where the proxy can substitute the secret, subject to the environment network policy." + }) +}).annotate({ + "description": + "Metadata for an HTTP credential used only in OpenAI-hosted environments. Sandbox code receives a placeholder. The proxy substitutes the secret for allowed HTTPS destinations on ports 443 and 8443. The real secret is not available to sandbox code for local computation and is never returned in this resource.", + "identifier": "VaultCredentialAuthResourceEnvironmentVariable" +}) +export type CreateMcpOauthRefreshParam = { + readonly "token_endpoint": string + readonly "client_id": string + readonly "resource"?: string | null + readonly "scope"?: string | null + readonly "refresh_token": string + readonly "token_endpoint_auth": CreateMcpOauthTokenEndpointAuthParam +} +export const CreateMcpOauthRefreshParam = Schema.Struct({ + "token_endpoint": Schema.String.annotate({ + "description": "The HTTPS OAuth token endpoint used to exchange the refresh token for a new access token." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "client_id": Schema.String.annotate({ "description": "The OAuth client ID used when requesting a new access token." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "resource": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The resource URI to send to the OAuth token endpoint during refresh, if required." }) + ), + "scope": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "Space-separated OAuth scopes to request during refresh, if required." }) + ), + "refresh_token": Schema.String.annotate({ + "description": "The refresh token to store. This secret is never returned in credential resources." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "token_endpoint_auth": Schema.suspend((): Schema.Codec => + CreateMcpOauthTokenEndpointAuthParam + ).annotate({ "description": "How the OAuth client authenticates to the token endpoint." }) +}).annotate({ + "description": "Configuration for refreshing the access token of an MCP OAuth credential.", + "identifier": "CreateMcpOauthRefreshParam" +}) +export type CreateVaultCredentialAuthParamEnvironmentVariable = { + readonly "type": "environment_variable" + readonly "secret_name": string + readonly "secret_value": string + readonly "networking": VaultCredentialNetworkingParam +} +export const CreateVaultCredentialAuthParamEnvironmentVariable = Schema.Struct({ + "type": Schema.Literal("environment_variable").annotate({ + "description": "The type of the object. Always `environment_variable`." + }), + "secret_name": Schema.String.annotate({ + "description": + "The environment variable name that receives the placeholder, such as `SERVICE_API_KEY`. Use ASCII letters, digits, and underscores, starting with a letter or underscore. Names starting with `CODEX_` and managed proxy or certificate variable names are reserved." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "secret_value": Schema.String.annotate({ + "description": + "The write-only secret to store. Never returned in credential resources or supplied directly to sandbox code. Must be nonempty and must not contain carriage returns, newlines, or NUL bytes." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "networking": Schema.suspend((): Schema.Codec => VaultCredentialNetworkingParam) + .annotate({ + "description": + "The destinations where the proxy can substitute this secret. The environment network policy must also allow them." + }) +}).annotate({ + "description": + "An HTTP credential for OpenAI-hosted environments only. The sandbox receives an environment variable containing a placeholder, not the secret. Use the placeholder unchanged in outgoing requests. The egress proxy replaces the placeholder with the secret for allowed HTTPS destinations on ports 443 and 8443. Sandbox code cannot read the real secret or use it for local computation, such as signing a request.", + "identifier": "CreateVaultCredentialAuthParamEnvironmentVariable" +}) +export type RotateMcpOauthRefreshParam = { + readonly "refresh_token"?: string | null + readonly "scope"?: string | null + readonly "token_endpoint_auth"?: RotateMcpOauthTokenEndpointAuthParam | null +} +export const RotateMcpOauthRefreshParam = Schema.Struct({ + "refresh_token": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "The replacement refresh token. Omit or pass `null` to keep the stored token. This secret is never returned in resources." + }) + ), + "scope": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "Replacement space-separated OAuth scopes for refresh requests. Omit to keep the scopes, or pass `null` to stop sending a scope parameter." + }) + ), + "token_endpoint_auth": Schema.optionalKey( + Schema.Union([RotateMcpOauthTokenEndpointAuthParam, Schema.Null]).annotate({ + "description": "Client-secret updates for the existing token endpoint authentication method." + }) + ) +}).annotate({ + "description": "Updates to an MCP credential's existing OAuth refresh configuration.", + "identifier": "RotateMcpOauthRefreshParam" +}) +export type BetaModelIdsCompaction = BetaModelIdsResponses | string | null +export const BetaModelIdsCompaction = Schema.Union([BetaModelIdsResponses, Schema.String, Schema.Null]).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.", + "identifier": "BetaModelIdsCompaction" +}) +export type BetaTextResponseFormatConfiguration = + | BetaResponseFormatText + | BetaTextResponseFormatJsonSchema + | BetaResponseFormatJsonObject +export const BetaTextResponseFormatConfiguration = Schema.Union([ + BetaResponseFormatText, + BetaTextResponseFormatJsonSchema, + BetaResponseFormatJsonObject +], { mode: "oneOf" }).annotate({ + "description": + "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n", + "identifier": "BetaTextResponseFormatConfiguration" +}) +export type BetaFileSearchTool = { + readonly "type": "file_search" + readonly "vector_store_ids": ReadonlyArray + readonly "max_num_results"?: number + readonly "ranking_options"?: BetaRankingOptions + readonly "filters"?: BetaFilters | null +} +export const BetaFileSearchTool = Schema.Struct({ + "type": Schema.Literal("file_search").annotate({ + "description": "The type of the file search tool. Always `file_search`." + }), + "vector_store_ids": Schema.Array(Schema.String).annotate({ + "description": "The IDs of the vector stores to search." + }), + "max_num_results": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum number of results to return. This number should be between 1 and 50 inclusive." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "ranking_options": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaRankingOptions).annotate({ + "description": "Ranking options for search." + }) + ), + "filters": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFilters).annotate({ "description": "A filter to apply." }), + Schema.Null + ]) + ) +}).annotate({ + "title": "File search", + "description": + "A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).", + "identifier": "BetaFileSearchTool" +}) +export type BetaAutoCodeInterpreterToolParam = { + readonly "type": "auto" + readonly "file_ids"?: ReadonlyArray + readonly "memory_limit"?: BetaContainerMemoryLimit | null + readonly "network_policy"?: BetaContainerNetworkPolicyDisabledParam | BetaContainerNetworkPolicyAllowlistParam +} +export const BetaAutoCodeInterpreterToolParam = Schema.Struct({ + "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }), + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "An optional list of uploaded files to make available to your code." + }).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })) + ), + "memory_limit": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaContainerMemoryLimit).annotate({ + "description": "The memory limit for the code interpreter container." + }), + Schema.Null + ]) + ), + "network_policy": Schema.optionalKey( + Schema.Union([BetaContainerNetworkPolicyDisabledParam, BetaContainerNetworkPolicyAllowlistParam], { mode: "oneOf" }) + .annotate({ "description": "Network access policy for the container." }) + ) +}).annotate({ + "title": "CodeInterpreterToolAuto", + "description": + "Configuration for a code interpreter container. Optionally specify the IDs of the files to run the code on.", + "identifier": "BetaAutoCodeInterpreterToolParam" +}) +export type BetaContainerAutoParam = { + readonly "type": "container_auto" + readonly "file_ids"?: ReadonlyArray + readonly "memory_limit"?: BetaContainerMemoryLimit | null + readonly "network_policy"?: BetaContainerNetworkPolicyDisabledParam | BetaContainerNetworkPolicyAllowlistParam + readonly "skills"?: ReadonlyArray +} +export const BetaContainerAutoParam = Schema.Struct({ + "type": Schema.Literal("container_auto").annotate({ + "description": "Automatically creates a container for this request" + }), + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "An optional list of uploaded files to make available to your code." + }).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })) + ), + "memory_limit": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaContainerMemoryLimit).annotate({ + "description": "The memory limit for the container." + }), + Schema.Null + ]) + ), + "network_policy": Schema.optionalKey( + Schema.Union([BetaContainerNetworkPolicyDisabledParam, BetaContainerNetworkPolicyAllowlistParam], { mode: "oneOf" }) + .annotate({ "description": "Network access policy for the container." }) + ), + "skills": Schema.optionalKey( + Schema.Array(Schema.Union([BetaSkillReferenceParam, BetaInlineSkillParam], { mode: "oneOf" })).annotate({ + "description": "An optional list of skills referenced by id or inline data." + }).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })) + ) +}).annotate({ "identifier": "BetaContainerAutoParam" }) +export type BetaCustomToolParam = { + readonly "type": "custom" + readonly "name": string + readonly "async"?: boolean + readonly "description"?: string + readonly "format"?: BetaCustomTextFormatParam | BetaCustomGrammarFormatParam + readonly "defer_loading"?: boolean + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaCustomToolParam = Schema.Struct({ + "type": Schema.Literal("custom").annotate({ "description": "The type of the custom tool. Always `custom`." }), + "name": Schema.String.annotate({ "description": "The name of the custom tool, used to identify it in tool calls." }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the tool response can be returned asynchronously versus immediately returned on next response creation." + }) + ), + "description": Schema.optionalKey( + Schema.String.annotate({ "description": "Optional description of the custom tool, used to provide more context." }) + ), + "format": Schema.optionalKey( + Schema.Union([BetaCustomTextFormatParam, BetaCustomGrammarFormatParam], { mode: "oneOf" }).annotate({ + "description": "The input format for the custom tool. Default is unconstrained text." + }) + ), + "defer_loading": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether this tool should be deferred and discovered via tool search." }) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Custom tool", + "description": + "A custom tool that processes input using a specified format. Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)", + "identifier": "BetaCustomToolParam" +}) +export type BetaResponsePromptVariables = { + readonly [x: string]: string | BetaInputTextContent | BetaInputImageContent | BetaInputFileContent +} | null +export const BetaResponsePromptVariables = Schema.Union([ + Schema.Record( + Schema.String, + Schema.Union([Schema.String, BetaInputTextContent, BetaInputImageContent, BetaInputFileContent], { mode: "oneOf" }) + ).annotate({ + "title": "Prompt Variables", + "description": + "Optional map of values to substitute in for variables in your\nprompt. The substitution values can either be strings, or other\nResponse input types like images or files.\n" + }), + Schema.Null +]).annotate({ "identifier": "BetaResponsePromptVariables" }) +export type BetaInputContent = BetaInputTextContent | BetaInputImageContent | BetaInputFileContent +export const BetaInputContent = Schema.Union([BetaInputTextContent, BetaInputImageContent, BetaInputFileContent], { + mode: "oneOf" +}).annotate({ "identifier": "BetaInputContent" }) +export type BetaFunctionAndCustomToolCallOutput = BetaInputTextContent | BetaInputImageContent | BetaInputFileContent +export const BetaFunctionAndCustomToolCallOutput = Schema.Union([ + BetaInputTextContent, + BetaInputImageContent, + BetaInputFileContent +], { mode: "oneOf" }).annotate({ "identifier": "BetaFunctionAndCustomToolCallOutput" }) +export type BetaResponseOutputTextAnnotationAddedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.output_text.annotation.added" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "annotation_index": number + readonly "sequence_number": number + readonly "annotation": BetaAnnotation | null +} +export const BetaResponseOutputTextAnnotationAddedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.output_text.annotation.added").annotate({ + "description": "The type of the event. Always 'response.output_text.annotation.added'." + }), + "item_id": Schema.String.annotate({ + "description": "The unique identifier of the item to which the annotation is being added." + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the response's output array." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the content part within the output item." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "annotation_index": Schema.Number.annotate({ "description": "The index of the annotation within the content part." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "annotation": Schema.Union([BetaAnnotation, Schema.Null]).annotate({ + "description": "The annotation object being added. (See annotation schema for details.)" + }) +}).annotate({ + "title": "ResponseOutputTextAnnotationAddedEvent", + "description": "Emitted when an annotation is added to output text content.\n", + "identifier": "BetaResponseOutputTextAnnotationAddedEvent" +}) +export type BetaOutputTextContent = { + readonly "type": "output_text" + readonly "text": string + readonly "annotations": ReadonlyArray + readonly "logprobs": ReadonlyArray +} +export const BetaOutputTextContent = Schema.Struct({ + "type": Schema.Literal("output_text").annotate({ + "description": "The type of the output text. Always `output_text`." + }), + "text": Schema.String.annotate({ "description": "The text output from the model." }), + "annotations": Schema.Array(BetaAnnotation).annotate({ "description": "The annotations of the text output." }), + "logprobs": Schema.Array(BetaLogProb) +}).annotate({ + "title": "Output text", + "description": "A text output from the model.", + "identifier": "BetaOutputTextContent" +}) +export type BetaComputerAction = + | BetaClickParam + | BetaDoubleClickAction + | BetaDragParam + | BetaKeyPressAction + | BetaMoveParam + | BetaScreenshotParam + | BetaScrollParam + | BetaTypeParam + | BetaWaitParam +export const BetaComputerAction = Schema.Union([ + BetaClickParam, + BetaDoubleClickAction, + BetaDragParam, + BetaKeyPressAction, + BetaMoveParam, + BetaScreenshotParam, + BetaScrollParam, + BetaTypeParam, + BetaWaitParam +], { mode: "oneOf" }).annotate({ "identifier": "BetaComputerAction" }) +export type BetaFunctionToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "id"?: string + readonly "type": "function_call" + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "arguments": string + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "async"?: boolean +} +export const BetaFunctionToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" })), + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the function tool call. Always `function_call`.\n" + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the function tool call generated by the model.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey(Schema.String.annotate({ "description": "The namespace of the function to run.\n" })), + "name": Schema.String.annotate({ "description": "The name of the function to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments to pass to the function.\n" }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the function tool call runs asynchronously.\n" }) + ) +}).annotate({ + "title": "Function tool call", + "description": + "A tool call to run a function. See the\n[function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.\n", + "identifier": "BetaFunctionToolCall" +}) +export type BetaFunctionShellCall = { + readonly "agent"?: BetaAgentTag + readonly "type": "shell_call" + readonly "id": string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "action": BetaFunctionShellAction + readonly "status": BetaFunctionShellCallStatus + readonly "environment": BetaLocalEnvironmentResource | BetaContainerReferenceResource | null + readonly "created_by"?: string +} +export const BetaFunctionShellCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("shell_call").annotate({ "description": "The type of the item. Always `shell_call`." }), + "id": Schema.String.annotate({ + "description": "The unique ID of the shell tool call. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ "description": "The unique ID of the shell tool call generated by the model." }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "action": Schema.suspend((): Schema.Codec => BetaFunctionShellAction).annotate({ + "description": "The shell commands and limits that describe how to run the tool call." + }), + "status": Schema.suspend((): Schema.Codec => BetaFunctionShellCallStatus).annotate({ + "description": "The status of the shell call. One of `in_progress`, `completed`, or `incomplete`." + }), + "environment": Schema.Union([ + Schema.Union([BetaLocalEnvironmentResource, BetaContainerReferenceResource], { mode: "oneOf" }), + Schema.Null + ]), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the entity that created this tool call." }) + ) +}).annotate({ + "title": "Shell tool call", + "description": "A tool call that executes one or more shell commands in a managed environment.", + "identifier": "BetaFunctionShellCall" +}) +export type BetaApplyPatchToolCall = { + readonly "agent"?: BetaAgentTag + readonly "type": "apply_patch_call" + readonly "id": string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "status": BetaApplyPatchCallStatus + readonly "operation": + | BetaApplyPatchCreateFileOperation + | BetaApplyPatchDeleteFileOperation + | BetaApplyPatchUpdateFileOperation + readonly "created_by"?: string +} +export const BetaApplyPatchToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("apply_patch_call").annotate({ + "description": "The type of the item. Always `apply_patch_call`." + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => BetaApplyPatchCallStatus).annotate({ + "description": "The status of the apply patch tool call. One of `in_progress` or `completed`." + }), + "operation": Schema.Union([ + BetaApplyPatchCreateFileOperation, + BetaApplyPatchDeleteFileOperation, + BetaApplyPatchUpdateFileOperation + ], { mode: "oneOf" }).annotate({ + "title": "Apply patch operation", + "description": "One of the create_file, delete_file, or update_file operations applied via apply_patch." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the entity that created this tool call." }) + ) +}).annotate({ + "title": "Apply patch tool call", + "description": "A tool call that applies file diffs by creating, deleting, or updating files.", + "identifier": "BetaApplyPatchToolCall" +}) +export type BetaApplyPatchToolCallOutput = { + readonly "agent"?: BetaAgentTag + readonly "type": "apply_patch_call_output" + readonly "id": string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "status": BetaApplyPatchCallOutputStatus + readonly "output"?: string | null + readonly "created_by"?: string +} +export const BetaApplyPatchToolCallOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("apply_patch_call_output").annotate({ + "description": "The type of the item. Always `apply_patch_call_output`." + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call output. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => BetaApplyPatchCallOutputStatus).annotate( + { "description": "The status of the apply patch tool call output. One of `completed` or `failed`." } + ), + "output": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Optional textual output returned by the apply patch tool." }), + Schema.Null + ]) + ), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the entity that created this tool call output." }) + ) +}).annotate({ + "title": "Apply patch tool call output", + "description": "The output emitted by an apply patch tool call.", + "identifier": "BetaApplyPatchToolCallOutput" +}) +export type BetaCustomToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "custom_tool_call" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "input": string + readonly "async"?: boolean +} +export const BetaCustomToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("custom_tool_call").annotate({ + "description": "The type of the custom tool call. Always `custom_tool_call`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the custom tool call in the OpenAI platform.\n" }) + ), + "call_id": Schema.String.annotate({ + "description": "An identifier used to map this custom tool call to a tool call output.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the custom tool being called.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the custom tool being called.\n" }), + "input": Schema.String.annotate({ "description": "The input for the custom tool call generated by the model.\n" }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the custom tool call runs asynchronously.\n" }) + ) +}).annotate({ + "title": "Custom tool call", + "description": "A call to a custom tool created by the model.\n", + "identifier": "BetaCustomToolCall" +}) +export type BetaFunctionToolCallResource = { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "function_call" + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "arguments": string + readonly "status": "in_progress" | "completed" | "incomplete" + readonly "async"?: boolean + readonly "created_by"?: string +} +export const BetaFunctionToolCallResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" }), + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the function tool call. Always `function_call`.\n" + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the function tool call generated by the model.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey(Schema.String.annotate({ "description": "The namespace of the function to run.\n" })), + "name": Schema.String.annotate({ "description": "The name of the function to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments to pass to the function.\n" }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the function tool call runs asynchronously.\n" }) + ), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "identifier": "BetaFunctionToolCallResource" }) +export type BetaCustomToolCallResource = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "custom_tool_call" + readonly "id": string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "input": string + readonly "async"?: boolean + readonly "status": BetaFunctionCallStatus + readonly "created_by"?: string +} +export const BetaCustomToolCallResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("custom_tool_call").annotate({ + "description": "The type of the custom tool call. Always `custom_tool_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the custom tool call item.\n" }), + "call_id": Schema.String.annotate({ + "description": "An identifier used to map this custom tool call to a tool call output.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the custom tool being called.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the custom tool being called.\n" }), + "input": Schema.String.annotate({ "description": "The input for the custom tool call generated by the model.\n" }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the custom tool call runs asynchronously.\n" }) + ), + "status": Schema.suspend((): Schema.Codec => BetaFunctionCallStatus).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "title": "ResponseCustomToolCallItem", "identifier": "BetaCustomToolCallResource" }) +export type BetaFunctionShellCallOutputContentParam = { + readonly "stdout": string + readonly "stderr": string + readonly "outcome": BetaFunctionShellCallOutputOutcomeParam +} +export const BetaFunctionShellCallOutputContentParam = Schema.Struct({ + "stdout": Schema.String.annotate({ "description": "Captured stdout output for the shell call." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "stderr": Schema.String.annotate({ "description": "Captured stderr output for the shell call." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + "outcome": Schema.suspend((): Schema.Codec => + BetaFunctionShellCallOutputOutcomeParam + ).annotate({ "description": "The exit or timeout outcome associated with this shell call." }) +}).annotate({ + "title": "Shell output content", + "description": "Captured stdout and stderr for a portion of a shell tool call output.", + "identifier": "BetaFunctionShellCallOutputContentParam" +}) +export type BetaMCPToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_call" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string + readonly "output"?: string | null + readonly "error"?: BetaMCPToolCallError | null + readonly "status"?: BetaMCPToolCallStatus + readonly "approval_request_id"?: string | null +} +export const BetaMCPToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_call").annotate({ "description": "The type of the item. Always `mcp_call`.\n" }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool call.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server running the tool.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool that was run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments passed to the tool.\n" }), + "output": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The output from the tool call.\n" }), Schema.Null]) + ), + "error": Schema.optionalKey( + Schema.Union([BetaMCPToolCallError, Schema.Null]).annotate({ + "description": "The error from the tool call, if any." + }) + ), + "status": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaMCPToolCallStatus).annotate({ + "description": + "The status of the tool call. One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`.\n" + }) + ), + "approval_request_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Unique identifier for the MCP tool call approval request.\nInclude this value in a subsequent `mcp_approval_response` input to approve or reject the corresponding tool call.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "title": "MCP tool call", + "description": "An invocation of a tool on an MCP server.\n", + "identifier": "BetaMCPToolCall" +}) +export type BetaModerationPolicyParam = { + readonly "input"?: BetaModerationConfigParam | null + readonly "output"?: BetaModerationConfigParam | null +} +export const BetaModerationPolicyParam = Schema.Struct({ + "input": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaModerationConfigParam).annotate({ + "description": "The moderation policy for the response input." + }), + Schema.Null + ]) + ), + "output": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaModerationConfigParam).annotate({ + "description": "The moderation policy for the response output." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "The policy to apply to moderated response input and output.", + "identifier": "BetaModerationPolicyParam" +}) +export type BetaResponseError = { + readonly "code": BetaResponseErrorCode + readonly "message": string + readonly "misalignment"?: BetaMisalignmentErrorDetailsResource +} | null +export const BetaResponseError = Schema.Union([ + Schema.Struct({ + "code": BetaResponseErrorCode, + "message": Schema.String.annotate({ "description": "A human-readable description of the error.\n" }), + "misalignment": Schema.optionalKey(BetaMisalignmentErrorDetailsResource) + }).annotate({ "description": "An error object returned when the model fails to generate a Response.\n" }), + Schema.Null +]).annotate({ "identifier": "BetaResponseError" }) +export type BetaError = { + readonly "code": string | null + readonly "message": string + readonly "param": string | null + readonly "type": string + readonly "misalignment"?: BetaMisalignmentErrorDetailsResource +} +export const BetaError = Schema.Struct({ + "code": Schema.Union([Schema.String, Schema.Null]), + "message": Schema.String, + "param": Schema.Union([Schema.String, Schema.Null]), + "type": Schema.String, + "misalignment": Schema.optionalKey(BetaMisalignmentErrorDetailsResource) +}).annotate({ "identifier": "BetaError" }) +export type BetaFunctionShellCallOutput = { + readonly "agent"?: BetaAgentTag + readonly "type": "shell_call_output" + readonly "id": string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "status": BetaFunctionShellCallOutputStatusEnum + readonly "output": ReadonlyArray + readonly "max_output_length": number | null + readonly "created_by"?: string +} +export const BetaFunctionShellCallOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("shell_call_output").annotate({ + "description": "The type of the shell call output. Always `shell_call_output`." + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the shell call output. Populated when this item is returned via API." + }), + "call_id": Schema.String.annotate({ "description": "The unique ID of the shell tool call generated by the model." }), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCaller).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => + BetaFunctionShellCallOutputStatusEnum + ).annotate({ + "description": "The status of the shell call output. One of `in_progress`, `completed`, or `incomplete`." + }), + "output": Schema.Array(BetaFunctionShellCallOutputContent).annotate({ + "description": "An array of shell call output contents" + }), + "max_output_length": Schema.Union([ + Schema.Number.annotate({ + "description": + "The maximum length of the shell command output. This is generated by the model and should be passed back with the raw output." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ + "title": "Shell call output", + "description": "The output of a shell tool call that was emitted.", + "identifier": "BetaFunctionShellCallOutput" +}) +export type BetaResponseShellCallOutputContentDoneStreamingEvent = { + readonly "type": "response.shell_call_output_content.done" + readonly "sequence_number": number + readonly "agent"?: BetaAgentTag + readonly "item_id": string + readonly "output_index": number + readonly "command_index": number + readonly "output": ReadonlyArray +} +export const BetaResponseShellCallOutputContentDoneStreamingEvent = Schema.Struct({ + "type": Schema.Literal("response.shell_call_output_content.done").annotate({ + "description": "The type of the event, always `response.shell_call_output_content.done`." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of the event that was emitted." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "item_id": Schema.String.annotate({ "description": "The ID of the output item that was updated." }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was updated." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "command_index": Schema.Number.annotate({ "description": "The index of the shell command that produced output." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "output": Schema.Array(BetaFunctionShellCallOutputContent).annotate({ + "description": "The output contents emitted for the shell command." + }) +}).annotate({ + "title": "Response shell call output content done event", + "description": "A streaming event that indicated shell call output was completed.", + "identifier": "BetaResponseShellCallOutputContentDoneStreamingEvent" +}) +export type BetaPromptCacheDiagnostics = + | BetaPromptCacheMissDiagnosticsBody + | BetaPromptCacheHitDiagnosticsBody + | BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody + | BetaPromptCacheUnavailableDiagnosticsBody +export const BetaPromptCacheDiagnostics = Schema.Union([ + BetaPromptCacheMissDiagnosticsBody, + BetaPromptCacheHitDiagnosticsBody, + BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody, + BetaPromptCacheUnavailableDiagnosticsBody +], { mode: "oneOf" }).annotate({ + "description": "Prompt cache diagnostics requested for this response.", + "identifier": "BetaPromptCacheDiagnostics" +}) +export type BetaModeration = { + readonly "input": BetaModerationResultBody | BetaModerationErrorBody + readonly "output": BetaModerationResultBody | BetaModerationErrorBody +} +export const BetaModeration = Schema.Struct({ + "input": Schema.Union([BetaModerationResultBody, BetaModerationErrorBody], { mode: "oneOf" }).annotate({ + "description": "Moderation for the response input." + }), + "output": Schema.Union([BetaModerationResultBody, BetaModerationErrorBody], { mode: "oneOf" }).annotate({ + "description": "Moderation for the response output." + }) +}).annotate({ + "title": "Moderation", + "description": "Moderation results or errors for the response input and output.", + "identifier": "BetaModeration" +}) +export type RunStepDetailsToolCallsObject = { + readonly "type": "tool_calls" + readonly "tool_calls": ReadonlyArray< + RunStepDetailsToolCallsCodeObject | RunStepDetailsToolCallsFileSearchObject | RunStepDetailsToolCallsFunctionObject + > +} +export const RunStepDetailsToolCallsObject = Schema.Struct({ + "type": Schema.Literal("tool_calls").annotate({ "description": "Always `tool_calls`." }), + "tool_calls": Schema.Array( + Schema.Union([ + RunStepDetailsToolCallsCodeObject, + RunStepDetailsToolCallsFileSearchObject, + RunStepDetailsToolCallsFunctionObject + ], { mode: "oneOf" }) + ).annotate({ + "description": + "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.\n" + }) +}).annotate({ + "title": "Tool calls", + "description": "Details of the tool call.", + "identifier": "RunStepDetailsToolCallsObject" +}) +export type AssistantObject = { + readonly "id": string + readonly "object": "assistant" + readonly "created_at": number + readonly "name": string | null + readonly "description": string | null + readonly "model": string + readonly "instructions": string | null + readonly "tools": ReadonlyArray + readonly "tool_resources"?: { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { readonly "vector_store_ids"?: ReadonlyArray } + } | null + readonly "metadata": Metadata + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "response_format"?: AssistantsApiResponseFormatOption | null +} +export const AssistantObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("assistant").annotate({ "description": "The object type, which is always `assistant`." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the assistant was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "name": Schema.Union([ + Schema.String.annotate({ "description": "The name of the assistant. The maximum length is 256 characters.\n" }) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })), + Schema.Null + ]), + "description": Schema.Union([ + Schema.String.annotate({ + "description": "The description of the assistant. The maximum length is 512 characters.\n" + }).check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })), + Schema.Null + ]), + "model": Schema.String.annotate({ + "description": + "ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.\n" + }), + "instructions": Schema.Union([ + Schema.String.annotate({ + "description": "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + }).check(Schema.isMaxCodePoints(256000).annotate({ "expected": "a string with at most 256000 code points" })), + Schema.Null + ]), + "tools": Schema.Array( + Schema.Union([AssistantToolsCode, AssistantToolsFileSearch, AssistantToolsFunction], { mode: "oneOf" }) + ).annotate({ + "description": + "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + }).check(Schema.isMaxLength(128).annotate({ "expected": "a value with a length of at most 128" })), + "tool_resources": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + })) + }).annotate({ + "description": + "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }), + Schema.Null + ])), + "metadata": Metadata, + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ])), + "response_format": Schema.optionalKey(Schema.Union([AssistantsApiResponseFormatOption, Schema.Null])) +}).annotate({ + "title": "Assistant", + "description": "Represents an `assistant` that can call the model and use tools.", + "identifier": "AssistantObject" +}) +export type CreateAssistantRequest = { + readonly "model": string | AssistantSupportedModels + readonly "name"?: string | null + readonly "description"?: string | null + readonly "instructions"?: string | null + readonly "reasoning_effort"?: ReasoningEffort + readonly "tools"?: ReadonlyArray + readonly "tool_resources"?: { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { + readonly "vector_store_ids": ReadonlyArray + readonly "vector_stores"?: ReadonlyArray< + { + readonly "file_ids"?: ReadonlyArray + readonly "chunking_strategy"?: { readonly "type": "auto" } | { + readonly "type": "static" + readonly "static": { readonly "max_chunk_size_tokens": number; readonly "chunk_overlap_tokens": number } + } + readonly "metadata"?: Metadata + } + > + } | { + readonly "vector_store_ids"?: ReadonlyArray + readonly "vector_stores": ReadonlyArray< + { + readonly "file_ids"?: ReadonlyArray + readonly "chunking_strategy"?: { readonly "type": "auto" } | { + readonly "type": "static" + readonly "static": { readonly "max_chunk_size_tokens": number; readonly "chunk_overlap_tokens": number } + } + readonly "metadata"?: Metadata + } + > + } + } | null + readonly "metadata"?: Metadata + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "response_format"?: AssistantsApiResponseFormatOption | null +} +export const CreateAssistantRequest = Schema.Struct({ + "model": Schema.Union([Schema.String, AssistantSupportedModels]).annotate({ + "description": + "ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.\n" + }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the assistant. The maximum length is 256 characters.\n" }) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })), + Schema.Null + ]) + ), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The description of the assistant. The maximum length is 512 characters.\n" + }).check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })), + Schema.Null + ]) + ), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + }).check(Schema.isMaxCodePoints(256000).annotate({ "expected": "a string with at most 256000 code points" })), + Schema.Null + ]) + ), + "reasoning_effort": Schema.optionalKey(ReasoningEffort), + "tools": Schema.optionalKey( + Schema.Array( + Schema.Union([AssistantToolsCode, AssistantToolsFileSearch, AssistantToolsFunction], { mode: "oneOf" }) + ).annotate({ + "description": + "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + }).check(Schema.isMaxLength(128).annotate({ "expected": "a value with a length of at most 128" })) + ), + "tool_resources": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "vector_store_ids": Schema.Array(Schema.String).annotate({ + "description": + "The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })), + "vector_stores": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.\n" + }).check( + Schema.isMaxLength(100000000).annotate({ "expected": "a value with a length of at most 100000000" }) + ) + ), + "chunking_strategy": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }) }) + .annotate({ + "title": "Auto Chunking Strategy", + "description": + "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`." + }), + Schema.Struct({ + "type": Schema.Literal("static").annotate({ "description": "Always `static`." }), + "static": Schema.Struct({ + "max_chunk_size_tokens": Schema.Number.annotate({ + "description": + "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(100).annotate({ + "expected": "a value greater than or equal to 100" + }) + ).check( + Schema.isLessThanOrEqualTo(4096).annotate({ "expected": "a value less than or equal to 4096" }) + ), + "chunk_overlap_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }) + }).annotate({ "title": "Static Chunking Strategy" }) + ], { mode: "oneOf" }).annotate({ + "description": + "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy." + }) + ), + "metadata": Schema.optionalKey(Metadata) + })).annotate({ + "description": + "A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + }), + Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ), + "vector_stores": Schema.Array(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.\n" + }).check( + Schema.isMaxLength(100000000).annotate({ "expected": "a value with a length of at most 100000000" }) + ) + ), + "chunking_strategy": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ "type": Schema.Literal("auto").annotate({ "description": "Always `auto`." }) }) + .annotate({ + "title": "Auto Chunking Strategy", + "description": + "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`." + }), + Schema.Struct({ + "type": Schema.Literal("static").annotate({ "description": "Always `static`." }), + "static": Schema.Struct({ + "max_chunk_size_tokens": Schema.Number.annotate({ + "description": + "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(100).annotate({ + "expected": "a value greater than or equal to 100" + }) + ).check( + Schema.isLessThanOrEqualTo(4096).annotate({ "expected": "a value less than or equal to 4096" }) + ), + "chunk_overlap_tokens": Schema.Number.annotate({ + "description": + "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }) + }).annotate({ "title": "Static Chunking Strategy" }) + ], { mode: "oneOf" }).annotate({ + "description": + "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy." + }) + ), + "metadata": Schema.optionalKey(Metadata) + })).annotate({ + "description": + "A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + }) + ], { mode: "oneOf" })) + }).annotate({ + "description": + "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }), + Schema.Null + ])), + "metadata": Schema.optionalKey(Metadata), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ])), + "response_format": Schema.optionalKey(Schema.Union([AssistantsApiResponseFormatOption, Schema.Null])) +}).annotate({ "identifier": "CreateAssistantRequest" }) +export type ModifyAssistantRequest = { + readonly "model"?: string | AssistantSupportedModels + readonly "reasoning_effort"?: ReasoningEffort + readonly "name"?: string | null + readonly "description"?: string | null + readonly "instructions"?: string | null + readonly "tools"?: ReadonlyArray + readonly "tool_resources"?: { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { readonly "vector_store_ids"?: ReadonlyArray } + } | null + readonly "metadata"?: Metadata + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "response_format"?: AssistantsApiResponseFormatOption | null +} +export const ModifyAssistantRequest = Schema.Struct({ + "model": Schema.optionalKey( + Schema.Union([Schema.String, AssistantSupportedModels]).annotate({ + "description": + "ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.\n" + }) + ), + "reasoning_effort": Schema.optionalKey(ReasoningEffort), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the assistant. The maximum length is 256 characters.\n" }) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })), + Schema.Null + ]) + ), + "description": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The description of the assistant. The maximum length is 512 characters.\n" + }).check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })), + Schema.Null + ]) + ), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + }).check(Schema.isMaxCodePoints(256000).annotate({ "expected": "a string with at most 256000 code points" })), + Schema.Null + ]) + ), + "tools": Schema.optionalKey( + Schema.Array( + Schema.Union([AssistantToolsCode, AssistantToolsFileSearch, AssistantToolsFunction], { mode: "oneOf" }) + ).annotate({ + "description": + "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + }).check(Schema.isMaxLength(128).annotate({ "expected": "a value with a length of at most 128" })) + ), + "tool_resources": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Overrides the list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "Overrides the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + })) + }).annotate({ + "description": + "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }), + Schema.Null + ])), + "metadata": Schema.optionalKey(Metadata), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ])), + "response_format": Schema.optionalKey(Schema.Union([AssistantsApiResponseFormatOption, Schema.Null])) +}).annotate({ "identifier": "ModifyAssistantRequest" }) +export type RunObject = { + readonly "id": string + readonly "object": "thread.run" + readonly "created_at": number + readonly "thread_id": string + readonly "assistant_id": string + readonly "status": + | "queued" + | "in_progress" + | "requires_action" + | "cancelling" + | "cancelled" + | "failed" + | "completed" + | "incomplete" + | "expired" + readonly "required_action": + | { + readonly "type": "submit_tool_outputs" + readonly "submit_tool_outputs": { readonly "tool_calls": ReadonlyArray } + } & { readonly [x: string]: Schema.Json } + | null + readonly "last_error": + | { readonly "code": "server_error" | "rate_limit_exceeded" | "invalid_prompt"; readonly "message": string } & { + readonly [x: string]: Schema.Json + } + | null + readonly "expires_at": number | null + readonly "started_at": number | null + readonly "cancelled_at": number | null + readonly "failed_at": number | null + readonly "completed_at": number | null + readonly "incomplete_details": + | { readonly "reason"?: "max_completion_tokens" | "max_prompt_tokens" } & { readonly [x: string]: Schema.Json } + | null + readonly "model": string + readonly "instructions": string + readonly "tools": ReadonlyArray + readonly "metadata": Metadata + readonly "usage": RunCompletionUsage + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "max_prompt_tokens": number | null + readonly "max_completion_tokens": number | null + readonly "truncation_strategy": TruncationObject | null + readonly "tool_choice": AssistantsApiToolChoiceOption | null + readonly "parallel_tool_calls": ParallelToolCalls + readonly "response_format": AssistantsApiResponseFormatOption | null +} +export const RunObject = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The identifier, which can be referenced in API endpoints." }), + "object": Schema.Literal("thread.run").annotate({ "description": "The object type, which is always `thread.run`." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the run was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ + "description": + "The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was executed on as a part of this run." + }), + "assistant_id": Schema.String.annotate({ + "description": + "The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for execution of this run." + }), + "status": Schema.Literals([ + "queued", + "in_progress", + "requires_action", + "cancelling", + "cancelled", + "failed", + "completed", + "incomplete", + "expired" + ]).annotate({ + "description": + "The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, `incomplete`, or `expired`." + }), + "required_action": Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "type": Schema.Literal("submit_tool_outputs").annotate({ + "description": "For now, this is always `submit_tool_outputs`." + }), + "submit_tool_outputs": Schema.Struct({ + "tool_calls": Schema.Array(RunToolCallObject).annotate({ + "description": "A list of the relevant tool calls." + }) + }).annotate({ "description": "Details on the tool outputs needed for this run to continue." }) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": "Details on the action required to continue the run. Will be `null` if no action is required." + }), + "last_error": Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "code": Schema.Literals(["server_error", "rate_limit_exceeded", "invalid_prompt"]).annotate({ + "description": "One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`." + }), + "message": Schema.String.annotate({ "description": "A human-readable description of the error." }) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ "description": "The last error associated with this run. Will be `null` if there are no errors." }), + "expires_at": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The Unix timestamp (in seconds) for when the run will expire.", "format": "unixtime" }), + "started_at": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The Unix timestamp (in seconds) for when the run was started.", "format": "unixtime" }), + "cancelled_at": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ + "description": "The Unix timestamp (in seconds) for when the run was cancelled.", + "format": "unixtime" + }), + "failed_at": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The Unix timestamp (in seconds) for when the run failed.", "format": "unixtime" }), + "completed_at": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]).annotate({ + "description": "The Unix timestamp (in seconds) for when the run was completed.", + "format": "unixtime" + }), + "incomplete_details": Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "reason": Schema.optionalKey( + Schema.Literals(["max_completion_tokens", "max_prompt_tokens"]).annotate({ + "description": + "The reason why the run is incomplete. This will point to which specific token limit was reached over the course of the run." + }) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ "description": "Details on why the run is incomplete. Will be `null` if the run is not incomplete." }), + "model": Schema.String.annotate({ + "description": + "The model that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run." + }), + "instructions": Schema.String.annotate({ + "description": + "The instructions that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run." + }), + "tools": Schema.Array( + Schema.Union([AssistantToolsCode, AssistantToolsFileSearch, AssistantToolsFunction], { mode: "oneOf" }) + ).annotate({ + "description": + "The list of tools that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run." + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })), + "metadata": Metadata, + "usage": RunCompletionUsage, + "temperature": Schema.optionalKey( + Schema.Union([Schema.Finite, Schema.Null]).annotate({ + "description": "The sampling temperature used for this run. If not set, defaults to 1." + }) + ), + "top_p": Schema.optionalKey( + Schema.Union([Schema.Finite, Schema.Null]).annotate({ + "description": "The nucleus sampling value used for this run. If not set, defaults to 1." + }) + ), + "max_prompt_tokens": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(256).annotate({ "expected": "a value greater than or equal to 256" }) + ), + Schema.Null + ]).annotate({ + "description": "The maximum number of prompt tokens specified to have been used over the course of the run.\n" + }), + "max_completion_tokens": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(256).annotate({ "expected": "a value greater than or equal to 256" }) + ), + Schema.Null + ]).annotate({ + "description": "The maximum number of completion tokens specified to have been used over the course of the run.\n" + }), + "truncation_strategy": Schema.Union([TruncationObject, Schema.Null]), + "tool_choice": Schema.Union([AssistantsApiToolChoiceOption, Schema.Null]), + "parallel_tool_calls": ParallelToolCalls, + "response_format": Schema.Union([AssistantsApiResponseFormatOption, Schema.Null]) +}).annotate({ + "title": "A run on a thread", + "description": + "Represents an execution run on a [thread](https://developers.openai.com/api/docs/assistants/migration).", + "identifier": "RunObject" +}) +export type CreateRunRequest = { + readonly "assistant_id": string + readonly "model"?: string | AssistantSupportedModels | null + readonly "reasoning_effort"?: ReasoningEffort + readonly "instructions"?: string | null + readonly "additional_instructions"?: string | null + readonly "additional_messages"?: ReadonlyArray | null + readonly "tools"?: ReadonlyArray | null + readonly "metadata"?: Metadata + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "stream"?: boolean | null + readonly "max_prompt_tokens"?: number | null + readonly "max_completion_tokens"?: number | null + readonly "truncation_strategy"?: TruncationObject | null + readonly "tool_choice"?: AssistantsApiToolChoiceOption | null + readonly "parallel_tool_calls"?: ParallelToolCalls + readonly "response_format"?: AssistantsApiResponseFormatOption | null +} +export const CreateRunRequest = Schema.Struct({ + "assistant_id": Schema.String.annotate({ + "description": + "The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run." + }), + "model": Schema.optionalKey( + Schema.Union([Schema.String, AssistantSupportedModels, Schema.Null]).annotate({ + "description": + "The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used." + }) + ), + "reasoning_effort": Schema.optionalKey(ReasoningEffort), + "instructions": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "Overrides the [instructions](https://developers.openai.com/api/docs/assistants/migration) of the assistant. This is useful for modifying the behavior on a per-run basis." + }) + ), + "additional_instructions": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions." + }) + ), + "additional_messages": Schema.optionalKey( + Schema.Union([Schema.Array(CreateMessageRequest), Schema.Null]).annotate({ + "description": "Adds additional messages to the thread before creating the run." + }) + ), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.Union([AssistantToolsCode, AssistantToolsFileSearch, AssistantToolsFunction], { mode: "oneOf" }) + ).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })), + Schema.Null + ]).annotate({ + "description": + "Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis." + }) + ), + "metadata": Schema.optionalKey(Metadata), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }) + ), + "top_p": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ]).annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }) + ), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + }) + ), + "max_prompt_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(256).annotate({ "expected": "a value greater than or equal to 256" }) + ), + Schema.Null + ]).annotate({ + "description": + "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + }) + ), + "max_completion_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(256).annotate({ "expected": "a value greater than or equal to 256" }) + ), + Schema.Null + ]).annotate({ + "description": + "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + }) + ), + "truncation_strategy": Schema.optionalKey(Schema.Union([TruncationObject, Schema.Null])), + "tool_choice": Schema.optionalKey(Schema.Union([AssistantsApiToolChoiceOption, Schema.Null])), + "parallel_tool_calls": Schema.optionalKey(ParallelToolCalls), + "response_format": Schema.optionalKey(Schema.Union([AssistantsApiResponseFormatOption, Schema.Null])) +}).annotate({ "identifier": "CreateRunRequest" }) +export type ResponseTextParam = { + readonly "format"?: TextResponseFormatConfiguration + readonly "verbosity"?: Verbosity +} +export const ResponseTextParam = Schema.Struct({ + "format": Schema.optionalKey(TextResponseFormatConfiguration), + "verbosity": Schema.optionalKey(Verbosity) +}).annotate({ + "description": + "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)\n- [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)\n", + "identifier": "ResponseTextParam" +}) +export type ErrorResponse = { readonly "error": Error } +export const ErrorResponse = Schema.Struct({ "error": Error }).annotate({ "identifier": "ErrorResponse" }) +export type PermissionErrorResponse = { readonly "error": Error | string } +export const PermissionErrorResponse = Schema.Struct({ + "error": Schema.Union([ + Error, + Schema.String.annotate({ "description": "An explanation of the missing API permissions." }) + ]).annotate({ "description": "Details about the permission error." }) +}).annotate({ "identifier": "PermissionErrorResponse" }) +export type ErrorEvent = { readonly "event": "error"; readonly "data": Error } +export const ErrorEvent = Schema.Struct({ "event": Schema.Literal("error"), "data": Error }).annotate({ + "description": + "Occurs when an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.", + "identifier": "ErrorEvent" +}) +export type VideoResource = { + readonly "id": string + readonly "object": "video" + readonly "model": VideoModel + readonly "status": VideoStatus + readonly "progress": number + readonly "created_at": number + readonly "completed_at": number | null + readonly "expires_at": number | null + readonly "prompt": string | null + readonly "size": VideoSize + readonly "seconds": string + readonly "remixed_from_video_id": string | null + readonly "error": Error_2 | null +} +export const VideoResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Unique identifier for the video job." }), + "object": Schema.Literal("video").annotate({ "description": "The object type, which is always `video`." }), + "model": Schema.suspend((): Schema.Codec => VideoModel).annotate({ + "description": "The video generation model that produced the job." + }), + "status": Schema.suspend((): Schema.Codec => VideoStatus).annotate({ + "description": "Current lifecycle status of the video job." + }), + "progress": Schema.Number.annotate({ "description": "Approximate completion percentage for the generation task." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (seconds) for when the job was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "completed_at": Schema.Union([ + Schema.Number.annotate({ + "description": "Unix timestamp (seconds) for when the job completed, if finished.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "expires_at": Schema.Union([ + Schema.Number.annotate({ + "description": "Unix timestamp (seconds) for when the downloadable assets expire, if set.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "prompt": Schema.Union([ + Schema.String.annotate({ "description": "The prompt that was used to generate the video." }), + Schema.Null + ]), + "size": Schema.suspend((): Schema.Codec => VideoSize).annotate({ + "description": "The resolution of the generated video." + }), + "seconds": Schema.String.annotate({ + "description": "Duration of the generated clip in seconds. For extensions, this is the stitched total duration." + }), + "remixed_from_video_id": Schema.Union([ + Schema.String.annotate({ "description": "Identifier of the source video if this video is a remix." }), + Schema.Null + ]), + "error": Schema.Union([ + Schema.suspend((): Schema.Codec => Error_2).annotate({ + "description": "Error payload that explains why generation failed, if applicable." + }), + Schema.Null + ]) +}).annotate({ + "title": "Video job", + "description": "Structured information describing a generated video job.", + "identifier": "VideoResource" +}) +export type ChatCompletionModeration = { + readonly "input": ChatCompletionModerationResults | ChatCompletionModerationError + readonly "output": ChatCompletionModerationResults | ChatCompletionModerationError +} +export const ChatCompletionModeration = Schema.Struct({ + "input": Schema.Union([ChatCompletionModerationResults, ChatCompletionModerationError], { mode: "oneOf" }).annotate({ + "description": "Moderation for the request input." + }), + "output": Schema.Union([ChatCompletionModerationResults, ChatCompletionModerationError], { mode: "oneOf" }).annotate({ + "description": "Moderation for the generated output." + }) +}).annotate({ + "description": "Moderation results or errors for the request input and generated output.", + "identifier": "ChatCompletionModeration" +}) +export type ChatCompletionRequestSystemMessage = { + readonly "content": string | ReadonlyArray + readonly "role": "system" + readonly "name"?: string +} +export const ChatCompletionRequestSystemMessage = Schema.Struct({ + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text content", "description": "The contents of the system message." }), + Schema.Array(ChatCompletionRequestSystemMessageContentPart).annotate({ + "title": "Array of content parts", + "description": + "An array of content parts with a defined type. For system messages, only type `text` is supported." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }).annotate({ "description": "The contents of the system message." }), + "role": Schema.Literal("system").annotate({ + "description": "The role of the messages author, in this case `system`." + }), + "name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + }) + ) +}).annotate({ + "title": "System message", + "description": + "Developer-provided instructions that the model should follow, regardless of\nmessages sent by the user. With o1 models and newer, use `developer` messages\nfor this purpose instead.\n", + "identifier": "ChatCompletionRequestSystemMessage" +}) +export type ChatCompletionRequestAssistantMessage = { + readonly "content"?: string | ReadonlyArray | null + readonly "refusal"?: string | null + readonly "role": "assistant" + readonly "name"?: string + readonly "audio"?: { readonly "id": string } | null + readonly "tool_calls"?: ChatCompletionMessageToolCalls + readonly "function_call"?: { readonly "arguments": string; readonly "name": string } | null +} +export const ChatCompletionRequestAssistantMessage = Schema.Struct({ + "content": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.String.annotate({ "title": "Text content", "description": "The contents of the assistant message." }), + Schema.Array(ChatCompletionRequestAssistantMessageContentPart).annotate({ + "title": "Array of content parts", + "description": + "An array of content parts with a defined type. Can be one or more of type `text`, or exactly one of type `refusal`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified.\n" + }), + Schema.Null + ]) + ), + "refusal": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The refusal message by the assistant." }), Schema.Null]) + ), + "role": Schema.Literal("assistant").annotate({ + "description": "The role of the messages author, in this case `assistant`." + }), + "name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + }) + ), + "audio": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "id": Schema.String.annotate({ + "description": "Unique identifier for a previous audio response from the model.\n" + }) + }).annotate({ + "description": + "Data about a previous audio response from the model.\n[Learn more](https://developers.openai.com/api/docs/guides/audio).\n" + }), + Schema.Null + ]) + ), + "tool_calls": Schema.optionalKey(ChatCompletionMessageToolCalls), + "function_call": Schema.optionalKey(Schema.Union([ + Schema.Struct({ + "arguments": Schema.String.annotate({ + "description": + "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + }), + "name": Schema.String.annotate({ "description": "The name of the function to call." }) + }).annotate({ + "description": + "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model." + }), + Schema.Null + ])) +}).annotate({ + "title": "Assistant message", + "description": "Messages sent by the model in response to user messages.\n", + "identifier": "ChatCompletionRequestAssistantMessage" +}) +export type ChatCompletionRequestToolMessage = { + readonly "role": "tool" + readonly "content": string | ReadonlyArray + readonly "tool_call_id": string +} +export const ChatCompletionRequestToolMessage = Schema.Struct({ + "role": Schema.Literal("tool").annotate({ "description": "The role of the messages author, in this case `tool`." }), + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text content", "description": "The contents of the tool message." }), + Schema.Array(ChatCompletionRequestToolMessageContentPart).annotate({ + "title": "Array of content parts", + "description": "An array of content parts with a defined type. For tool messages, only type `text` is supported." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }).annotate({ "description": "The contents of the tool message." }), + "tool_call_id": Schema.String.annotate({ "description": "Tool call that this message is responding to." }) +}).annotate({ "title": "Tool message", "identifier": "ChatCompletionRequestToolMessage" }) +export type ChatCompletionRequestUserMessage = { + readonly "content": string | ReadonlyArray + readonly "role": "user" + readonly "name"?: string +} +export const ChatCompletionRequestUserMessage = Schema.Struct({ + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text content", "description": "The text contents of the message." }), + Schema.Array(ChatCompletionRequestUserMessageContentPart).annotate({ + "title": "Array of content parts", + "description": + "An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text, image, or audio inputs." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + ], { mode: "oneOf" }).annotate({ "description": "The contents of the user message.\n" }), + "role": Schema.Literal("user").annotate({ "description": "The role of the messages author, in this case `user`." }), + "name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + }) + ) +}).annotate({ + "title": "User message", + "description": "Messages sent by an end user, containing prompts or additional context\ninformation.\n", + "identifier": "ChatCompletionRequestUserMessage" +}) +export type ModerationParam = { readonly "model": string; readonly "policy"?: ModerationPolicyParam | null } +export const ModerationParam = Schema.Struct({ + "model": Schema.String.annotate({ + "description": "The moderation model to use for moderated completions, e.g. 'omni-moderation-latest'." + }), + "policy": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ModerationPolicyParam).annotate({ + "description": "The policy to apply to moderated response input and output." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "Configuration for running moderation on the input and output of this response.", + "identifier": "ModerationParam" +}) +export type CodeInterpreterTool = { + readonly "type": "code_interpreter" + readonly "container": string | AutoCodeInterpreterToolParam + readonly "allowed_callers"?: ReadonlyArray | null +} +export const CodeInterpreterTool = Schema.Struct({ + "type": Schema.Literal("code_interpreter").annotate({ + "description": "The type of the code interpreter tool. Always `code_interpreter`.\n" + }), + "container": Schema.Union([ + Schema.String.annotate({ "description": "The container ID." }), + AutoCodeInterpreterToolParam + ], { mode: "oneOf" }).annotate({ + "description": + "The code interpreter container. Can be a container ID or an object that\nspecifies uploaded file IDs to make available to your code, along with an\noptional `memory_limit` setting.\n" + }), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Code interpreter", + "description": "A tool that runs Python code to help generate a response to a prompt.\n", + "identifier": "CodeInterpreterTool" +}) +export type FunctionShellToolParam = { + readonly "type": "shell" + readonly "environment"?: ContainerAutoParam | LocalEnvironmentParam | ContainerReferenceParam | null + readonly "allowed_callers"?: ReadonlyArray | null +} +export const FunctionShellToolParam = Schema.Struct({ + "type": Schema.Literal("shell").annotate({ "description": "The type of the shell tool. Always `shell`." }), + "environment": Schema.optionalKey( + Schema.Union([ + Schema.Union([ContainerAutoParam, LocalEnvironmentParam, ContainerReferenceParam], { mode: "oneOf" }), + Schema.Null + ]) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(CallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Shell tool", + "description": "A tool that allows the model to execute shell commands.", + "identifier": "FunctionShellToolParam" +}) +export type EvalItemContentArray = ReadonlyArray +export const EvalItemContentArray = Schema.Array(EvalItemContentItem).annotate({ + "title": "An array of Input text, Output text, Input image, and Input audio", + "description": + "A list of inputs, each of which may be either an input text, output text, input\nimage, or input audio object.\n", + "identifier": "EvalItemContentArray" +}) +export type Message = { + readonly "type": "message" + readonly "id": string + readonly "status": MessageStatus + readonly "role": MessageRole + readonly "content": ReadonlyArray< + | InputTextContent + | OutputTextContent + | TextContent + | SummaryTextContent + | ReasoningTextContent + | RefusalContent + | InputImageContent + | ComputerScreenshotContent + | InputFileContent + > + readonly "phase"?: MessagePhase_2 | null +} +export const Message = Schema.Struct({ + "type": Schema.Literal("message").annotate({ "description": "The type of the message. Always set to `message`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the message." }), + "status": Schema.suspend((): Schema.Codec => MessageStatus).annotate({ + "description": + "The status of item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API." + }), + "role": Schema.suspend((): Schema.Codec => MessageRole).annotate({ + "description": + "The role of the message. One of `unknown`, `user`, `assistant`, `system`, `critic`, `discriminator`, `developer`, or `tool`." + }), + "content": Schema.Array( + Schema.Union([ + InputTextContent, + OutputTextContent, + TextContent, + SummaryTextContent, + ReasoningTextContent, + RefusalContent, + InputImageContent, + ComputerScreenshotContent, + InputFileContent + ], { mode: "oneOf" }).annotate({ "description": "A content part that makes up an input or output item." }) + ).annotate({ "description": "The content of the message" }), + "phase": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => MessagePhase_2).annotate({ + "description": + "Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend phase on all assistant messages — dropping it can degrade performance. Not used for user messages." + }), + Schema.Null + ]) + ) +}).annotate({ "title": "Message", "description": "A message to or from the model.", "identifier": "Message" }) +export type OutputMessageContent = OutputTextContent | RefusalContent +export const OutputMessageContent = Schema.Union([OutputTextContent, RefusalContent], { mode: "oneOf" }).annotate({ + "identifier": "OutputMessageContent" +}) +export type OutputContent = OutputTextContent | RefusalContent | ReasoningTextContent +export const OutputContent = Schema.Union([OutputTextContent, RefusalContent, ReasoningTextContent], { mode: "oneOf" }) + .annotate({ "identifier": "OutputContent" }) +export type FunctionToolCallOutputResource = { + readonly "id": string + readonly "type": "function_call_output" + readonly "call_id"?: string + readonly "name"?: string + readonly "namespace"?: string + readonly "caller"?: ToolCallCallerParam | null + readonly "output": string | ReadonlyArray + readonly "status": "in_progress" | "completed" | "incomplete" + readonly "created_by"?: string +} +export const FunctionToolCallOutputResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the function call tool output.\n" }), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The type of the function tool call output. Always `function_call_output`.\n" + }), + "call_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the function tool call generated by the model.\n" }) + ), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the tool that produced the output.\n" }) + ), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the tool that produced the output.\n" }) + ), + "caller": Schema.optionalKey(Schema.Union([ToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the function call.\n" + }), + Schema.Array(FunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the function call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the function call generated by your code.\nCan be a string or an list of output content.\n" + }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "identifier": "FunctionToolCallOutputResource" }) +export type CustomToolCallOutput = { + readonly "type": "custom_tool_call_output" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "output": string | ReadonlyArray +} +export const CustomToolCallOutput = Schema.Struct({ + "type": Schema.Literal("custom_tool_call_output").annotate({ + "description": "The type of the custom tool call output. Always `custom_tool_call_output`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the custom tool call output in the OpenAI platform.\n" }) + ), + "call_id": Schema.String.annotate({ + "description": "The call ID, used to map this custom tool call output to a custom tool call.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the custom tool call.\n" + }), + Schema.Array(FunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the custom tool call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the custom tool call generated by your code.\nCan be a string or an list of output content.\n" + }) +}).annotate({ + "title": "Custom tool call output", + "description": "The output of a custom tool call from your code, being sent back to the model.\n", + "identifier": "CustomToolCallOutput" +}) +export type CustomToolCallOutputResource = { + readonly "type": "custom_tool_call_output" + readonly "id": string + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "output": string | ReadonlyArray + readonly "status": FunctionCallOutputStatusEnum + readonly "created_by"?: string +} +export const CustomToolCallOutputResource = Schema.Struct({ + "type": Schema.Literal("custom_tool_call_output").annotate({ + "description": "The type of the custom tool call output. Always `custom_tool_call_output`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the custom tool call output item.\n" }), + "call_id": Schema.String.annotate({ + "description": "The call ID, used to map this custom tool call output to a custom tool call.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the custom tool call.\n" + }), + Schema.Array(FunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the custom tool call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the custom tool call generated by your code.\nCan be a string or an list of output content.\n" + }), + "status": Schema.suspend((): Schema.Codec => FunctionCallOutputStatusEnum).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "title": "ResponseCustomToolCallOutputItem", "identifier": "CustomToolCallOutputResource" }) +export type FunctionToolCallOutput = { + readonly "id"?: string + readonly "type": "function_call_output" + readonly "call_id"?: string + readonly "name"?: string + readonly "namespace"?: string + readonly "caller"?: ToolCallCallerParam | null + readonly "output": string | ReadonlyArray + readonly "status"?: "in_progress" | "completed" | "incomplete" +} +export const FunctionToolCallOutput = Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The unique ID of the function tool call output. Populated when this item\nis returned via API.\n" + }) + ), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The type of the function tool call output. Always `function_call_output`.\n" + }), + "call_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the function tool call generated by the model.\n" }) + ), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the tool that produced the output.\n" }) + ), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the tool that produced the output.\n" }) + ), + "caller": Schema.optionalKey(Schema.Union([ToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the function call.\n" + }), + Schema.Array(FunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the function call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the function call generated by your code.\nCan be a string or an list of output content.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ) +}).annotate({ + "title": "Function tool call output", + "description": "The output of a function tool call.\n", + "identifier": "FunctionToolCallOutput" +}) +export type InputMessageContentList = ReadonlyArray +export const InputMessageContentList = Schema.Array(InputContent).annotate({ + "title": "Input item content list", + "description": "A list of one or many input items to the model, containing different content \ntypes.\n", + "identifier": "InputMessageContentList" +}) +export type Prompt = { + readonly "id": string + readonly "version"?: string | null + readonly "variables"?: ResponsePromptVariables +} | null +export const Prompt = Schema.Union([ + Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique identifier of the prompt template to use." }), + "version": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional version of the prompt template." }), Schema.Null]) + ), + "variables": Schema.optionalKey(ResponsePromptVariables) + }).annotate({ + "description": + "Reference to a prompt template and its variables.\n[Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).\n" + }), + Schema.Null +]).annotate({ "identifier": "Prompt" }) +export type ComputerActionList = ReadonlyArray +export const ComputerActionList = Schema.Array(ComputerAction).annotate({ + "title": "Computer Action List", + "description": + "Flattened batched actions for `computer_use`. Each action includes an\n`type` discriminator and action-specific fields.\n", + "identifier": "ComputerActionList" +}) +export type NamespaceToolParam = { + readonly "type": "namespace" + readonly "name": string + readonly "description": string + readonly "tools": ReadonlyArray +} +export const NamespaceToolParam = Schema.Struct({ + "type": Schema.Literal("namespace").annotate({ "description": "The type of the tool. Always `namespace`." }), + "name": Schema.String.annotate({ "description": "The namespace name used in tool calls (for example, `crm`)." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "description": Schema.String.annotate({ "description": "A description of the namespace shown to the model." }), + "tools": Schema.Array( + Schema.Union([FunctionToolParam, CustomToolParam], { mode: "oneOf" }).annotate({ + "description": "A function or custom tool that belongs to a namespace." + }) + ).annotate({ "description": "The function/custom tools available inside this namespace." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) +}).annotate({ + "title": "Namespace", + "description": "Groups function/custom tools under a shared namespace.", + "identifier": "NamespaceToolParam" +}) +export type ToolSearchOutputNamespaceToolParam = { + readonly "type": "namespace" + readonly "name": string + readonly "description": string + readonly "tools": ReadonlyArray +} +export const ToolSearchOutputNamespaceToolParam = Schema.Struct({ + "type": Schema.Literal("namespace").annotate({ "description": "The type of the tool. Always `namespace`." }), + "name": Schema.String.annotate({ "description": "The namespace name used in tool calls (for example, `crm`)." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "description": Schema.String.annotate({ "description": "A description of the namespace shown to the model." }), + "tools": Schema.Array( + Schema.Union([ToolSearchOutputFunctionToolParam, CustomToolParam], { mode: "oneOf" }).annotate({ + "description": "A function or custom tool loaded into a namespace by tool search." + }) + ).annotate({ "description": "The function/custom tools loaded inside this namespace." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) +}).annotate({ + "title": "Namespace", + "description": "Groups function/custom tools under a shared namespace.", + "identifier": "ToolSearchOutputNamespaceToolParam" +}) +export type LiveHostedShellToolInputParam = { + readonly "type": "shell" + readonly "environment"?: + | LiveHostedShellContainerAutoParam + | LiveContainerReferenceParam + | LiveLocalEnvironmentParam + | null +} +export const LiveHostedShellToolInputParam = Schema.Struct({ + "type": Schema.Literal("shell"), + "environment": Schema.optionalKey( + Schema.Union([ + Schema.Union([LiveHostedShellContainerAutoParam, LiveContainerReferenceParam, LiveLocalEnvironmentParam], { + mode: "oneOf" + }), + Schema.Null + ]) + ) +}).annotate({ + "description": + "A Responses shell tool. Use a hosted container or return local shell results with response.item.create. Domain secrets are not supported.", + "identifier": "LiveHostedShellToolInputParam" +}) +export type Input = ReadonlyArray +export const Input = Schema.Array(LiveInitialItem).annotate({ + "description": + "Ordered text-only history supplied before startup. Supports developer, user, and assistant messages with one text part each; at most 128 messages and 8,192 rendered tokens in total." +}).check( + Schema.isMaxLength(128).annotate({ "expected": "a value with a length of at most 128", "identifier": "input" }) +) +export type AuditLog = { + readonly "id": string + readonly "type": AuditLogEventType + readonly "effective_at": number + readonly "project"?: { readonly "id"?: string; readonly "name"?: string } + readonly "actor"?: AuditLogActor | null + readonly "api_key.created"?: { + readonly "id"?: string + readonly "data"?: { readonly "scopes"?: ReadonlyArray } + } + readonly "api_key.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { readonly "scopes"?: ReadonlyArray } + } + readonly "api_key.deleted"?: { readonly "id"?: string } + readonly "checkpoint.permission.created"?: { + readonly "id"?: string + readonly "data"?: { readonly "project_id"?: string; readonly "fine_tuned_model_checkpoint"?: string } + } + readonly "checkpoint.permission.deleted"?: { readonly "id"?: string } + readonly "external_key.registered"?: { + readonly "id"?: string + readonly "data"?: { readonly [x: string]: Schema.Json } + } + readonly "external_key.removed"?: { readonly "id"?: string } + readonly "external_storage.registered"?: { + readonly "id"?: string + readonly "data"?: { readonly "provider"?: Provider; readonly "geography"?: string } + } + readonly "external_storage.removed"?: { readonly "id"?: string } + readonly "group.created"?: { readonly "id"?: string; readonly "data"?: { readonly "group_name"?: string } } + readonly "group.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { readonly "group_name"?: string } + } + readonly "group.deleted"?: { readonly "id"?: string } + readonly "scim.enabled"?: { readonly "id"?: string } + readonly "scim.disabled"?: { readonly "id"?: string } + readonly "invite.sent"?: { + readonly "id"?: string + readonly "data"?: { readonly "email"?: string; readonly "role"?: string } + } + readonly "invite.accepted"?: { readonly "id"?: string } + readonly "invite.deleted"?: { readonly "id"?: string } + readonly "ip_allowlist.created"?: { + readonly "id"?: string + readonly "name"?: string + readonly "allowed_ips"?: ReadonlyArray + } + readonly "ip_allowlist.updated"?: { readonly "id"?: string; readonly "allowed_ips"?: ReadonlyArray } + readonly "ip_allowlist.deleted"?: { + readonly "id"?: string + readonly "name"?: string + readonly "allowed_ips"?: ReadonlyArray + } + readonly "ip_allowlist.config.activated"?: { + readonly "configs"?: ReadonlyArray<{ readonly "id"?: string; readonly "name"?: string }> + } + readonly "ip_allowlist.config.deactivated"?: { + readonly "configs"?: ReadonlyArray<{ readonly "id"?: string; readonly "name"?: string }> + } + readonly "login.succeeded"?: { readonly [x: string]: Schema.Json } + readonly "login.failed"?: { readonly "error_code"?: string; readonly "error_message"?: string } + readonly "logout.succeeded"?: { readonly [x: string]: Schema.Json } + readonly "logout.failed"?: { readonly "error_code"?: string; readonly "error_message"?: string } + readonly "organization.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { + readonly "title"?: string + readonly "description"?: string + readonly "name"?: string + readonly "threads_ui_visibility"?: string + readonly "usage_dashboard_visibility"?: string + readonly "api_call_logging"?: string + readonly "api_call_logging_project_ids"?: string + } + } + readonly "project.created"?: { + readonly "id"?: string + readonly "data"?: { readonly "name"?: string; readonly "title"?: string } + } + readonly "project.updated"?: { readonly "id"?: string; readonly "changes_requested"?: { readonly "title"?: string } } + readonly "project.archived"?: { readonly "id"?: string } + readonly "project.deleted"?: { readonly "id"?: string } + readonly "rate_limit.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { + readonly "max_requests_per_1_minute"?: number + readonly "max_tokens_per_1_minute"?: number + readonly "max_images_per_1_minute"?: number + readonly "max_audio_megabytes_per_1_minute"?: number + readonly "max_requests_per_1_day"?: number + readonly "batch_1_day_max_input_tokens"?: number + } + } + readonly "rate_limit.deleted"?: { readonly "id"?: string } + readonly "role.created"?: { + readonly "id"?: string + readonly "role_name"?: string + readonly "permissions"?: ReadonlyArray + readonly "resource_type"?: string + readonly "resource_id"?: string + } + readonly "role.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { + readonly "role_name"?: string + readonly "resource_id"?: string + readonly "resource_type"?: string + readonly "permissions_added"?: ReadonlyArray + readonly "permissions_removed"?: ReadonlyArray + readonly "description"?: string + readonly "metadata"?: { readonly [x: string]: Schema.Json } + } + } + readonly "role.deleted"?: { readonly "id"?: string } + readonly "role.assignment.created"?: { + readonly "id"?: string + readonly "principal_id"?: string + readonly "principal_type"?: string + readonly "resource_id"?: string + readonly "resource_type"?: string + } + readonly "role.assignment.deleted"?: { + readonly "id"?: string + readonly "principal_id"?: string + readonly "principal_type"?: string + readonly "resource_id"?: string + readonly "resource_type"?: string + } + readonly "role.bound_to_resource"?: { + readonly "id"?: string + readonly "role_id"?: string + readonly "resource_id"?: string + readonly "resource_type"?: string + readonly "permissions"?: ReadonlyArray + readonly "workspace_id"?: string + readonly "connector_id"?: string + readonly "connector_name"?: string + readonly "enabled"?: boolean + readonly "source"?: + | "role_toggle" + | "role_connector_update" + | "role_delete" + | "workspace_permissions" + | "connector_publish" + } + readonly "role.unbound_from_resource"?: { + readonly "id"?: string + readonly "role_id"?: string + readonly "resource_id"?: string + readonly "resource_type"?: string + readonly "permissions"?: ReadonlyArray + readonly "workspace_id"?: string + readonly "connector_id"?: string + readonly "connector_name"?: string + readonly "enabled"?: boolean + readonly "source"?: + | "role_toggle" + | "role_connector_update" + | "role_delete" + | "workspace_permissions" + | "connector_publish" + } + readonly "service_account.created"?: { readonly "id"?: string; readonly "data"?: { readonly "role"?: string } } + readonly "service_account.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { readonly "role"?: string } + } + readonly "service_account.deleted"?: { readonly "id"?: string } + readonly "workload_identity_provider.created"?: { + readonly "id"?: string + readonly "data"?: { readonly [x: string]: Schema.Json } + } + readonly "workload_identity_provider.updated"?: { + readonly "id"?: string + readonly "changes_requested"?: { readonly [x: string]: Schema.Json } + } + readonly "workload_identity_provider.deleted"?: { readonly "id"?: string; readonly "name"?: string } + readonly "workload_identity_provider_mapping.created"?: { + readonly "id"?: string + readonly "identity_provider_id"?: string + readonly "data"?: { readonly [x: string]: Schema.Json } + } + readonly "workload_identity_provider_mapping.updated"?: { + readonly "id"?: string + readonly "identity_provider_id"?: string + readonly "changes_requested"?: { readonly [x: string]: Schema.Json } + } + readonly "workload_identity_provider_mapping.deleted"?: { + readonly "id"?: string + readonly "identity_provider_id"?: string + readonly "project_id"?: string + readonly "service_account_id"?: string + } + readonly "user.added"?: { readonly "id"?: string; readonly "data"?: { readonly "role"?: string } } + readonly "user.updated"?: { readonly "id"?: string; readonly "changes_requested"?: { readonly "role"?: string } } + readonly "user.deleted"?: { readonly "id"?: string } + readonly "certificate.created"?: { readonly "id"?: string; readonly "name"?: string } + readonly "certificate.updated"?: { readonly "id"?: string; readonly "name"?: string } + readonly "certificate.deleted"?: { readonly "id"?: string; readonly "name"?: string; readonly "certificate"?: string } + readonly "certificates.activated"?: { + readonly "certificates"?: ReadonlyArray<{ readonly "id"?: string; readonly "name"?: string }> + } + readonly "certificates.deactivated"?: { + readonly "certificates"?: ReadonlyArray<{ readonly "id"?: string; readonly "name"?: string }> + } +} +export const AuditLog = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of this log." }), + "type": AuditLogEventType, + "effective_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of the event.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "project": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The project title." })) + }).annotate({ + "description": + "The project that the action was scoped to. Absent for actions not scoped to projects. Note that any admin actions taken via Admin API keys are associated with the default project." + }) + ), + "actor": Schema.optionalKey(Schema.Union([AuditLogActor, Schema.Null])), + "api_key.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The tracking ID of the API key." })), + "data": Schema.optionalKey( + Schema.Struct({ + "scopes": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "A list of scopes allowed for the API key, e.g. `[\"api.model.request\"]`" + }) + ) + }).annotate({ "description": "The payload used to create the API key." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "api_key.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The tracking ID of the API key." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "scopes": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "A list of scopes allowed for the API key, e.g. `[\"api.model.request\"]`" + }) + ) + }).annotate({ "description": "The payload used to update the API key." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "api_key.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The tracking ID of the API key." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "checkpoint.permission.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the checkpoint permission." })), + "data": Schema.optionalKey( + Schema.Struct({ + "project_id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The ID of the project that the checkpoint permission was created for." + }) + ), + "fine_tuned_model_checkpoint": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the fine-tuned model checkpoint." }) + ) + }).annotate({ "description": "The payload used to create the checkpoint permission." }) + ) + }).annotate({ + "description": "The project and fine-tuned model checkpoint that the checkpoint permission was created for." + }) + ), + "checkpoint.permission.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the checkpoint permission." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "external_key.registered": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the external key configuration." })), + "data": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The configuration for the external key." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "external_key.removed": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the external key configuration." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "external_storage.registered": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the external storage configuration." }) + ), + "data": Schema.optionalKey( + Schema.Struct({ + "provider": Schema.optionalKey( + Schema.suspend((): Schema.Codec => Provider).annotate({ + "description": "The external storage provider configuration." + }) + ), + "geography": Schema.optionalKey( + Schema.String.annotate({ "description": "The OpenAI geography derived from the storage region." }) + ) + }).annotate({ "description": "The configuration for the external storage." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "external_storage.removed": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the external storage configuration." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "group.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the group." })), + "data": Schema.optionalKey( + Schema.Struct({ + "group_name": Schema.optionalKey(Schema.String.annotate({ "description": "The group name." })) + }).annotate({ "description": "Information about the created group." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "group.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the group." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "group_name": Schema.optionalKey(Schema.String.annotate({ "description": "The updated group name." })) + }).annotate({ "description": "The payload used to update the group." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "group.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the group." })) }) + .annotate({ "description": "The details for events with this `type`." }) + ), + "scim.enabled": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the SCIM was enabled for." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "scim.disabled": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the SCIM was disabled for." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "invite.sent": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the invite." })), + "data": Schema.optionalKey( + Schema.Struct({ + "email": Schema.optionalKey( + Schema.String.annotate({ "description": "The email invited to the organization." }) + ), + "role": Schema.optionalKey( + Schema.String.annotate({ + "description": "The role the email was invited to be. Is either `owner` or `member`." + }) + ) + }).annotate({ "description": "The payload used to create the invite." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "invite.accepted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the invite." })) }) + .annotate({ "description": "The details for events with this `type`." }) + ), + "invite.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the invite." })) }) + .annotate({ "description": "The details for events with this `type`." }) + ), + "ip_allowlist.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the IP allowlist configuration." })), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the IP allowlist configuration." }) + ), + "allowed_ips": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "The IP addresses or CIDR ranges included in the configuration." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "ip_allowlist.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the IP allowlist configuration." })), + "allowed_ips": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "The updated set of IP addresses or CIDR ranges in the configuration." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "ip_allowlist.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The ID of the IP allowlist configuration." })), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the IP allowlist configuration." }) + ), + "allowed_ips": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "The IP addresses or CIDR ranges that were in the configuration." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "ip_allowlist.config.activated": Schema.optionalKey( + Schema.Struct({ + "configs": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the IP allowlist configuration." }) + ), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the IP allowlist configuration." }) + ) + }) + ).annotate({ "description": "The configurations that were activated." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "ip_allowlist.config.deactivated": Schema.optionalKey( + Schema.Struct({ + "configs": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the IP allowlist configuration." }) + ), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the IP allowlist configuration." }) + ) + }) + ).annotate({ "description": "The configurations that were deactivated." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "login.succeeded": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "This event has no additional fields beyond the standard audit log attributes." + }) + ), + "login.failed": Schema.optionalKey( + Schema.Struct({ + "error_code": Schema.optionalKey(Schema.String.annotate({ "description": "The error code of the failure." })), + "error_message": Schema.optionalKey( + Schema.String.annotate({ "description": "The error message of the failure." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "logout.succeeded": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "This event has no additional fields beyond the standard audit log attributes." + }) + ), + "logout.failed": Schema.optionalKey( + Schema.Struct({ + "error_code": Schema.optionalKey(Schema.String.annotate({ "description": "The error code of the failure." })), + "error_message": Schema.optionalKey( + Schema.String.annotate({ "description": "The error message of the failure." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "organization.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The organization ID." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "title": Schema.optionalKey(Schema.String.annotate({ "description": "The organization title." })), + "description": Schema.optionalKey(Schema.String.annotate({ "description": "The organization description." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The organization name." })), + "threads_ui_visibility": Schema.optionalKey( + Schema.String.annotate({ + "description": + "Visibility of the threads page which shows messages created with the Assistants API and Playground. One of `ANY_ROLE`, `OWNERS`, or `NONE`." + }) + ), + "usage_dashboard_visibility": Schema.optionalKey( + Schema.String.annotate({ + "description": + "Visibility of the usage dashboard which shows activity and costs for your organization. One of `ANY_ROLE` or `OWNERS`." + }) + ), + "api_call_logging": Schema.optionalKey( + Schema.String.annotate({ + "description": + "How your organization logs data from supported API calls. One of `disabled`, `enabled_per_call`, `enabled_for_all_projects`, or `enabled_for_selected_projects`" + }) + ), + "api_call_logging_project_ids": Schema.optionalKey( + Schema.String.annotate({ + "description": "The list of project ids if api_call_logging is set to `enabled_for_selected_projects`" + }) + ) + }).annotate({ "description": "The payload used to update the organization settings." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "project.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })), + "data": Schema.optionalKey( + Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The project name." })), + "title": Schema.optionalKey( + Schema.String.annotate({ "description": "The title of the project as seen on the dashboard." }) + ) + }).annotate({ "description": "The payload used to create the project." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "project.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "title": Schema.optionalKey( + Schema.String.annotate({ "description": "The title of the project as seen on the dashboard." }) + ) + }).annotate({ "description": "The payload used to update the project." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "project.archived": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })) }).annotate({ + "description": "The details for events with this `type`." + }) + ), + "project.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })) }).annotate({ + "description": "The details for events with this `type`." + }) + ), + "rate_limit.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The rate limit ID" })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "max_requests_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum requests per minute." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "max_tokens_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum tokens per minute." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "max_images_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum images per minute. Only relevant for certain models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "max_audio_megabytes_per_1_minute": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum audio megabytes per minute. Only relevant for certain models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "max_requests_per_1_day": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum requests per day. Only relevant for certain models." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "batch_1_day_max_input_tokens": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The maximum batch input tokens per day. Only relevant for certain models." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ "description": "The payload used to update the rate limits." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "rate_limit.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The rate limit ID" })) }) + .annotate({ "description": "The details for events with this `type`." }) + ), + "role.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The role ID." })), + "role_name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the role." })), + "permissions": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "description": "The permissions granted by the role." }) + ), + "resource_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of resource the role belongs to." }) + ), + "resource_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The resource the role is scoped to." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "role.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The role ID." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "role_name": Schema.optionalKey( + Schema.String.annotate({ "description": "The updated role name, when provided." }) + ), + "resource_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The resource the role is scoped to." }) + ), + "resource_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of resource the role belongs to." }) + ), + "permissions_added": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "description": "The permissions added to the role." }) + ), + "permissions_removed": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "description": "The permissions removed from the role." }) + ), + "description": Schema.optionalKey( + Schema.String.annotate({ "description": "The updated role description, when provided." }) + ), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Additional metadata stored on the role." + }) + ) + }).annotate({ "description": "The payload used to update the role." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "role.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The role ID." })) }).annotate({ + "description": "The details for events with this `type`." + }) + ), + "role.assignment.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The identifier of the role assignment." })), + "principal_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The principal (user or group) that received the role." }) + ), + "principal_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of principal (user or group) that received the role." }) + ), + "resource_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The resource the role assignment is scoped to." }) + ), + "resource_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of resource the role assignment is scoped to." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "role.assignment.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The identifier of the role assignment." })), + "principal_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The principal (user or group) that had the role removed." }) + ), + "principal_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of principal (user or group) that had the role removed." }) + ), + "resource_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The resource the role assignment was scoped to." }) + ), + "resource_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of resource the role assignment was scoped to." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "role.bound_to_resource": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The ID of the resource the role was bound to. ChatGPT workspace connector resources use `__`." + }) + ), + "role_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the role that was bound to the resource." }) + ), + "resource_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the resource the role was bound to." }) + ), + "resource_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of resource the role was bound to." }) + ), + "permissions": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ "description": "The permissions granted to the role for the resource." }) + ), + "workspace_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The workspace ID for a ChatGPT workspace connector resource." }) + ), + "connector_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The connector ID for a ChatGPT workspace connector resource." }) + ), + "connector_name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The connector display name for a ChatGPT workspace connector resource, or the connector ID when the display name could not be resolved." + }) + ), + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the connector is enabled for the role." }) + ), + "source": Schema.optionalKey( + Schema.Literals([ + "role_toggle", + "role_connector_update", + "role_delete", + "workspace_permissions", + "connector_publish" + ]).annotate({ "description": "The connector role mutation path that produced the event." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "role.unbound_from_resource": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The ID of the resource the role was unbound from. ChatGPT workspace connector resources use `__`." + }) + ), + "role_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the role that was unbound from the resource." }) + ), + "resource_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The ID of the resource the role was unbound from." }) + ), + "resource_type": Schema.optionalKey( + Schema.String.annotate({ "description": "The type of resource the role was unbound from." }) + ), + "permissions": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": "The permissions remaining for the role after the change." + }) + ), + "workspace_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The workspace ID for a ChatGPT workspace connector resource." }) + ), + "connector_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The connector ID for a ChatGPT workspace connector resource." }) + ), + "connector_name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The connector display name for a ChatGPT workspace connector resource, or the connector ID when the display name could not be resolved." + }) + ), + "enabled": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the connector is enabled for the role." }) + ), + "source": Schema.optionalKey( + Schema.Literals([ + "role_toggle", + "role_connector_update", + "role_delete", + "workspace_permissions", + "connector_publish" + ]).annotate({ "description": "The connector role mutation path that produced the event." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "service_account.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The service account ID." })), + "data": Schema.optionalKey( + Schema.Struct({ + "role": Schema.optionalKey( + Schema.String.annotate({ "description": "The role of the service account. Is either `owner` or `member`." }) + ) + }).annotate({ "description": "The payload used to create the service account." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "service_account.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The service account ID." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "role": Schema.optionalKey( + Schema.String.annotate({ "description": "The role of the service account. Is either `owner` or `member`." }) + ) + }).annotate({ "description": "The payload used to updated the service account." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "service_account.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The service account ID." })) }) + .annotate({ "description": "The details for events with this `type`." }) + ), + "workload_identity_provider.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider ID." })), + "data": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The payload used to create the workload identity provider." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "workload_identity_provider.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider ID." })), + "changes_requested": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The payload used to update the workload identity provider." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "workload_identity_provider.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider name." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "workload_identity_provider_mapping.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider mapping ID." })), + "identity_provider_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The workload identity provider ID." }) + ), + "data": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The payload used to create the workload identity provider mapping." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "workload_identity_provider_mapping.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider mapping ID." })), + "identity_provider_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The workload identity provider ID." }) + ), + "changes_requested": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "The payload used to update the workload identity provider mapping." + }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "workload_identity_provider_mapping.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The workload identity provider mapping ID." })), + "identity_provider_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The workload identity provider ID." }) + ), + "project_id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })), + "service_account_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The mapped service account ID." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "user.added": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The user ID." })), + "data": Schema.optionalKey( + Schema.Struct({ + "role": Schema.optionalKey( + Schema.String.annotate({ "description": "The role of the user. Is either `owner` or `member`." }) + ) + }).annotate({ "description": "The payload used to add the user to the project." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "user.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The project ID." })), + "changes_requested": Schema.optionalKey( + Schema.Struct({ + "role": Schema.optionalKey( + Schema.String.annotate({ "description": "The role of the user. Is either `owner` or `member`." }) + ) + }).annotate({ "description": "The payload used to update the user." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "user.deleted": Schema.optionalKey( + Schema.Struct({ "id": Schema.optionalKey(Schema.String.annotate({ "description": "The user ID." })) }).annotate({ + "description": "The details for events with this `type`." + }) + ), + "certificate.created": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The certificate ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the certificate." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "certificate.updated": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The certificate ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the certificate." })) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "certificate.deleted": Schema.optionalKey( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The certificate ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the certificate." })), + "certificate": Schema.optionalKey( + Schema.String.annotate({ "description": "The certificate content in PEM format." }) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "certificates.activated": Schema.optionalKey( + Schema.Struct({ + "certificates": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The certificate ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the certificate." })) + }) + ) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ), + "certificates.deactivated": Schema.optionalKey( + Schema.Struct({ + "certificates": Schema.optionalKey( + Schema.Array( + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The certificate ID." })), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the certificate." })) + }) + ) + ) + }).annotate({ "description": "The details for events with this `type`." }) + ) +}).annotate({ + "description": "A log of a user action or configuration change within this organization.", + "identifier": "AuditLog" +}) +export type CreateThreadAndRunRequest = { + readonly "assistant_id": string + readonly "thread"?: CreateThreadRequest + readonly "model"?: + | string + | "gpt-5" + | "gpt-5-mini" + | "gpt-5-nano" + | "gpt-5-2025-08-07" + | "gpt-5-mini-2025-08-07" + | "gpt-5-nano-2025-08-07" + | "gpt-4.1" + | "gpt-4.1-mini" + | "gpt-4.1-nano" + | "gpt-4.1-2025-04-14" + | "gpt-4.1-mini-2025-04-14" + | "gpt-4.1-nano-2025-04-14" + | "gpt-4o" + | "gpt-4o-2024-11-20" + | "gpt-4o-2024-08-06" + | "gpt-4o-2024-05-13" + | "gpt-4o-mini" + | "gpt-4o-mini-2024-07-18" + | "gpt-4.5-preview" + | "gpt-4.5-preview-2025-02-27" + | "gpt-4-turbo" + | "gpt-4-turbo-2024-04-09" + | "gpt-4-0125-preview" + | "gpt-4-turbo-preview" + | "gpt-4-1106-preview" + | "gpt-4-vision-preview" + | "gpt-4" + | "gpt-4-0314" + | "gpt-4-0613" + | "gpt-4-32k" + | "gpt-4-32k-0314" + | "gpt-4-32k-0613" + | "gpt-3.5-turbo" + | "gpt-3.5-turbo-16k" + | "gpt-3.5-turbo-0613" + | "gpt-3.5-turbo-1106" + | "gpt-3.5-turbo-0125" + | "gpt-3.5-turbo-16k-0613" + | null + readonly "instructions"?: string | null + readonly "tools"?: ReadonlyArray | null + readonly "tool_resources"?: + | { + readonly "code_interpreter"?: { readonly "file_ids"?: ReadonlyArray } + readonly "file_search"?: { readonly "vector_store_ids"?: ReadonlyArray } + } & { readonly [x: string]: Schema.Json } + | null + readonly "metadata"?: Metadata + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "stream"?: boolean | null + readonly "max_prompt_tokens"?: number | null + readonly "max_completion_tokens"?: number | null + readonly "truncation_strategy"?: TruncationObject | null + readonly "tool_choice"?: AssistantsApiToolChoiceOption | null + readonly "parallel_tool_calls"?: ParallelToolCalls + readonly "response_format"?: AssistantsApiResponseFormatOption | null +} +export const CreateThreadAndRunRequest = Schema.Struct({ + "assistant_id": Schema.String.annotate({ + "description": + "The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run." + }), + "thread": Schema.optionalKey(CreateThreadRequest), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4.5-preview", + "gpt-4.5-preview-2025-02-27", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613" + ]), + Schema.Null + ]).annotate({ + "description": + "The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used." + }) + ), + "instructions": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis." + }) + ), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.Union([AssistantToolsCode, AssistantToolsFileSearch, AssistantToolsFunction], { mode: "oneOf" }) + ).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })), + Schema.Null + ]).annotate({ + "description": + "Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis." + }) + ), + "tool_resources": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "code_interpreter": Schema.optionalKey(Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + ) + })), + "file_search": Schema.optionalKey(Schema.Struct({ + "vector_store_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + }).check(Schema.isMaxLength(1).annotate({ "expected": "a value with a length of at most 1" })) + ) + })) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": + "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + }) + ), + "metadata": Schema.optionalKey(Metadata), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }) + ), + "top_p": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ]).annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }) + ), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + }) + ), + "max_prompt_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(256).annotate({ "expected": "a value greater than or equal to 256" }) + ), + Schema.Null + ]).annotate({ + "description": + "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + }) + ), + "max_completion_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(256).annotate({ "expected": "a value greater than or equal to 256" }) + ), + Schema.Null + ]).annotate({ + "description": + "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + }) + ), + "truncation_strategy": Schema.optionalKey(Schema.Union([TruncationObject, Schema.Null])), + "tool_choice": Schema.optionalKey(Schema.Union([AssistantsApiToolChoiceOption, Schema.Null])), + "parallel_tool_calls": Schema.optionalKey(ParallelToolCalls), + "response_format": Schema.optionalKey(Schema.Union([AssistantsApiResponseFormatOption, Schema.Null])) +}).annotate({ "identifier": "CreateThreadAndRunRequest" }) +export type RunStepDeltaObject = { + readonly "id": string + readonly "object": "thread.run.step.delta" + readonly "delta": { + readonly "step_details"?: RunStepDeltaStepDetailsMessageCreationObject | RunStepDeltaStepDetailsToolCallsObject + } +} +export const RunStepDeltaObject = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The identifier of the run step, which can be referenced in API endpoints." + }), + "object": Schema.Literal("thread.run.step.delta").annotate({ + "description": "The object type, which is always `thread.run.step.delta`." + }), + "delta": Schema.Struct({ + "step_details": Schema.optionalKey( + Schema.Union([RunStepDeltaStepDetailsMessageCreationObject, RunStepDeltaStepDetailsToolCallsObject], { + mode: "oneOf" + }).annotate({ "description": "The details of the run step." }) + ) + }).annotate({ "description": "The delta containing the fields that have changed on the run step." }) +}).annotate({ + "title": "Run step delta object", + "description": "Represents a run step delta i.e. any changed fields on a run step during streaming.\n", + "identifier": "RunStepDeltaObject" +}) +export type ListMessagesResponse = + | null + | string + | number + | boolean + | { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean + } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const ListMessagesResponse = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(MessageObject), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]).annotate({ "identifier": "ListMessagesResponse" }) +export type MessageStreamEvent = + | { readonly "event": "thread.message.created"; readonly "data": MessageObject } + | { readonly "event": "thread.message.in_progress"; readonly "data": MessageObject } + | { readonly "event": "thread.message.delta"; readonly "data": MessageDeltaObject } + | { readonly "event": "thread.message.completed"; readonly "data": MessageObject } + | { readonly "event": "thread.message.incomplete"; readonly "data": MessageObject } +export const MessageStreamEvent = Schema.Union([ + Schema.Struct({ "event": Schema.Literal("thread.message.created"), "data": MessageObject }).annotate({ + "description": "Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is created." + }), + Schema.Struct({ "event": Schema.Literal("thread.message.in_progress"), "data": MessageObject }).annotate({ + "description": + "Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state." + }), + Schema.Struct({ "event": Schema.Literal("thread.message.delta"), "data": MessageDeltaObject }).annotate({ + "description": + "Occurs when parts of a [Message](https://developers.openai.com/api/docs/assistants/migration) are being streamed." + }), + Schema.Struct({ "event": Schema.Literal("thread.message.completed"), "data": MessageObject }).annotate({ + "description": "Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is completed." + }), + Schema.Struct({ "event": Schema.Literal("thread.message.incomplete"), "data": MessageObject }).annotate({ + "description": + "Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) ends before it is completed." + }) +], { mode: "oneOf" }).annotate({ "identifier": "MessageStreamEvent" }) +export type CreateVectorStoreFileRequest = { + readonly "file_id": string + readonly "chunking_strategy"?: ChunkingStrategyRequestParam + readonly "attributes"?: VectorStoreFileAttributes +} +export const CreateVectorStoreFileRequest = Schema.Struct({ + "file_id": Schema.String.annotate({ + "description": + "A [File](https://developers.openai.com/api/reference/resources/files) ID that the vector store should use. Useful for tools like `file_search` that can access files. For multi-file ingestion, we recommend [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) to minimize per-vector-store write requests." + }), + "chunking_strategy": Schema.optionalKey(ChunkingStrategyRequestParam), + "attributes": Schema.optionalKey(VectorStoreFileAttributes) +}).annotate({ "identifier": "CreateVectorStoreFileRequest" }) +export type ListVectorStoreFilesResponse = + | null + | string + | number + | boolean + | { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean + } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const ListVectorStoreFilesResponse = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(VectorStoreFileObject), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]).annotate({ "identifier": "ListVectorStoreFilesResponse" }) +export type PublicDecisionsUsageResponseResource = { + readonly "object": "page" + readonly "has_more": boolean + readonly "next_page": string | null + readonly "data": ReadonlyArray +} +export const PublicDecisionsUsageResponseResource = Schema.Struct({ + "object": Schema.Literal("page"), + "has_more": Schema.Boolean, + "next_page": Schema.Union([Schema.String, Schema.Null]), + "data": Schema.Array(DecisionsUsageBucket) +}).annotate({ "identifier": "PublicDecisionsUsageResponseResource" }) +export type AssistantMessageItem = { + readonly "id": string + readonly "object": "chatkit.thread_item" + readonly "created_at": number + readonly "thread_id": string + readonly "type": "chatkit.assistant_message" + readonly "content": ReadonlyArray +} +export const AssistantMessageItem = Schema.Struct({ + "id": Schema.String.annotate({ "description": "Identifier of the thread item." }), + "object": Schema.Literal("chatkit.thread_item").annotate({ + "description": "Type discriminator that is always `chatkit.thread_item`." + }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) for when the item was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "thread_id": Schema.String.annotate({ "description": "Identifier of the parent thread." }), + "type": Schema.Literal("chatkit.assistant_message").annotate({ + "description": "Type discriminator that is always `chatkit.assistant_message`." + }), + "content": Schema.Array(ResponseOutputText).annotate({ "description": "Ordered assistant response segments." }) +}).annotate({ + "title": "Assistant message", + "description": "Assistant-authored message within a thread.", + "identifier": "AssistantMessageItem" +}) +export type DecisionInputContent = string | DecisionInputContentParts +export const DecisionInputContent = Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.suspend((): Schema.Codec => DecisionInputContentParts).annotate({ + "title": "Parts" + }) +], { mode: "oneOf" }).annotate({ + "description": "Text evidence or an ordered list of text and image parts.", + "identifier": "DecisionInputContent" +}) +export type QuestionParam = QuestionParamPredicate | QuestionParamChoice | QuestionParamScore +export const QuestionParam = Schema.Union([QuestionParamPredicate, QuestionParamChoice, QuestionParamScore], { + mode: "oneOf" +}).annotate({ + "description": "A question about the request's input, with an optional correlation name.", + "identifier": "QuestionParam" +}) +export type AnswerResource = + | AnswerResourcePredicate + | AnswerResourceChoice + | AnswerResourceScore + | AnswerResourceRefusal +export const AnswerResource = Schema.Union([ + AnswerResourcePredicate, + AnswerResourceChoice, + AnswerResourceScore, + AnswerResourceRefusal +], { mode: "oneOf" }).annotate({ + "description": "A completed question always includes its name, including null when unnamed.", + "identifier": "AnswerResource" +}) +export type AgentEnvironmentListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const AgentEnvironmentListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(PublicEnvironmentResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "AgentEnvironmentListResource" +}) +export type HostedSkillParam = HostedSkillParamSkillReference | HostedSkillParamInline +export const HostedSkillParam = Schema.Union([HostedSkillParamSkillReference, HostedSkillParamInline], { + mode: "oneOf" +}).annotate({ "description": "A skill installed in an OpenAI-hosted environment.", "identifier": "HostedSkillParam" }) +export type HostedPluginParam = HostedPluginParamInline +export const HostedPluginParam = Schema.Union([HostedPluginParamInline], { mode: "oneOf" }).annotate({ + "description": "A plugin installed in an OpenAI-hosted environment.", + "identifier": "HostedPluginParam" +}) +export type SessionEventAgentSessionSubagentCreated = { + readonly "type": "agent.session.subagent.created" + readonly "event_id": string + readonly "subagent": SubagentResource +} +export const SessionEventAgentSessionSubagentCreated = Schema.Struct({ + "type": Schema.Literal("agent.session.subagent.created").annotate({ + "description": "The type of the object. Always `agent.session.subagent.created`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "subagent": Schema.suspend((): Schema.Codec => SubagentResource).annotate({ + "description": "The subagent that was created." + }) +}).annotate({ + "description": "Emitted when a subagent is created.", + "identifier": "SessionEventAgentSessionSubagentCreated" +}) +export type SessionEventAgentSessionSubagentActive = { + readonly "type": "agent.session.subagent.active" + readonly "event_id": string + readonly "subagent": SubagentResource +} +export const SessionEventAgentSessionSubagentActive = Schema.Struct({ + "type": Schema.Literal("agent.session.subagent.active").annotate({ + "description": "The type of the object. Always `agent.session.subagent.active`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "subagent": Schema.suspend((): Schema.Codec => SubagentResource).annotate({ + "description": "The subagent that resumed." + }) +}).annotate({ + "description": "Emitted when a closed subagent successfully resumes.", + "identifier": "SessionEventAgentSessionSubagentActive" +}) +export type SessionEventAgentSessionSubagentClosed = { + readonly "type": "agent.session.subagent.closed" + readonly "event_id": string + readonly "subagent": SubagentResource +} +export const SessionEventAgentSessionSubagentClosed = Schema.Struct({ + "type": Schema.Literal("agent.session.subagent.closed").annotate({ + "description": "The type of the object. Always `agent.session.subagent.closed`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "subagent": Schema.suspend((): Schema.Codec => SubagentResource).annotate({ + "description": "The subagent that was closed." + }) +}).annotate({ + "description": "Emitted when a subagent is closed.", + "identifier": "SessionEventAgentSessionSubagentClosed" +}) +export type FunctionCallOutputItemResource = { + readonly "id": string + readonly "turn_id": string + readonly "type": "function_call_output" + readonly "call_id": string + readonly "status": FunctionCallStatusResource + readonly "output": FunctionCallOutputResource | null + readonly "error": string | null +} +export const FunctionCallOutputItemResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the function call output item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that contains this item." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The item type. Always `function_call_output`." + }), + "call_id": Schema.String.annotate({ "description": "The ID of the function call that produced this output." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "status": Schema.suspend((): Schema.Codec => FunctionCallStatusResource).annotate({ + "description": "The status of the function call." + }), + "output": Schema.Union([FunctionCallOutputResource, Schema.Null]).annotate({ + "description": "The function result, if the call succeeded." + }), + "error": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The error message, if the call failed." }) +}).annotate({ + "description": "The result supplied for a function call.", + "identifier": "FunctionCallOutputItemResource" +}) +export type BrowserAuthenticationRequestItemResource = { + readonly "turn_id": string + readonly "request_id": string + readonly "request": BrowserAuthenticationHistoryRequestKindResource + readonly "id": string + readonly "type": "computer_use_approval_request" +} +export const BrowserAuthenticationRequestItemResource = Schema.Struct({ + "turn_id": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "request_id": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "request": BrowserAuthenticationHistoryRequestKindResource, + "id": Schema.String.annotate({ "description": "The stable history item ID." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "type": Schema.Literal("computer_use_approval_request").annotate({ + "description": "The item type. Always computer_use_approval_request." + }) +}).annotate({ + "description": "A credential-free history record of the emitted login request.", + "identifier": "BrowserAuthenticationRequestItemResource" +}) +export type SessionRequiredActionResourceComputerUseApprovalRequest = { + readonly "type": "computer_use_approval_request" + readonly "turn_id": string + readonly "request_id": string + readonly "request": ComputerUseApprovalRequestKindResource +} +export const SessionRequiredActionResourceComputerUseApprovalRequest = Schema.Struct({ + "type": Schema.Literal("computer_use_approval_request").annotate({ + "description": "The type of the object. Always `computer_use_approval_request`." + }), + "turn_id": Schema.String.annotate({ "description": "The turn that requested approval." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "request_id": Schema.String.annotate({ "description": "The registered request ID to echo when responding." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "request": Schema.suspend((): Schema.Codec => + ComputerUseApprovalRequestKindResource + ).annotate({ "description": "The information needed to render the request." }) +}).annotate({ + "description": "Respond to a computer-use request.", + "identifier": "SessionRequiredActionResourceComputerUseApprovalRequest" +}) +export type SessionTurnListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SessionTurnListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(TurnResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "SessionTurnListResource" +}) +export type SessionEventAgentSessionTurnCreated = { + readonly "type": "agent.session.turn.created" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string + readonly "turn": TurnResource +} +export const SessionEventAgentSessionTurnCreated = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.created").annotate({ + "description": "The type of the object. Always `agent.session.turn.created`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn": Schema.suspend((): Schema.Codec => TurnResource).annotate({ + "description": "The turn at the time it was created." + }) +}).annotate({ "description": "Emitted when a turn is created.", "identifier": "SessionEventAgentSessionTurnCreated" }) +export type SessionEventAgentSessionTurnInProgress = { + readonly "type": "agent.session.turn.in_progress" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string + readonly "turn": TurnResource +} +export const SessionEventAgentSessionTurnInProgress = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.in_progress").annotate({ + "description": "The type of the object. Always `agent.session.turn.in_progress`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn": Schema.suspend((): Schema.Codec => TurnResource).annotate({ + "description": "The turn at the time it started running." + }) +}).annotate({ + "description": "Emitted when a turn starts running.", + "identifier": "SessionEventAgentSessionTurnInProgress" +}) +export type SessionEventAgentSessionTurnCompleted = { + readonly "type": "agent.session.turn.completed" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string + readonly "turn": TurnResource + readonly "usage": TokenUsageResource | null +} +export const SessionEventAgentSessionTurnCompleted = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.completed").annotate({ + "description": "The type of the object. Always `agent.session.turn.completed`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn": Schema.suspend((): Schema.Codec => TurnResource).annotate({ + "description": "The completed turn." + }), + "usage": Schema.Union([TokenUsageResource, Schema.Null]).annotate({ + "description": "Token usage by the root agent during the turn, when available." + }) +}).annotate({ "description": "Emitted when a turn completes.", "identifier": "SessionEventAgentSessionTurnCompleted" }) +export type SessionEventAgentSessionTurnFailed = { + readonly "type": "agent.session.turn.failed" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string + readonly "turn": TurnResource + readonly "usage": TokenUsageResource | null +} +export const SessionEventAgentSessionTurnFailed = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.failed").annotate({ + "description": "The type of the object. Always `agent.session.turn.failed`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn": Schema.suspend((): Schema.Codec => TurnResource).annotate({ + "description": "The failed turn." + }), + "usage": Schema.Union([TokenUsageResource, Schema.Null]).annotate({ + "description": "Token usage by the root agent during the turn, when available." + }) +}).annotate({ "description": "Emitted when a turn fails.", "identifier": "SessionEventAgentSessionTurnFailed" }) +export type SessionEventAgentSessionTurnCancelled = { + readonly "type": "agent.session.turn.cancelled" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string + readonly "turn": TurnResource + readonly "usage": TokenUsageResource | null +} +export const SessionEventAgentSessionTurnCancelled = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.cancelled").annotate({ + "description": "The type of the object. Always `agent.session.turn.cancelled`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn": Schema.suspend((): Schema.Codec => TurnResource).annotate({ + "description": "The cancelled turn." + }), + "usage": Schema.Union([TokenUsageResource, Schema.Null]).annotate({ + "description": "Token usage by the root agent during the turn, when available." + }) +}).annotate({ + "description": "Emitted when a turn is cancelled.", + "identifier": "SessionEventAgentSessionTurnCancelled" +}) +export type PersistedAgentToolResource = + | PersistedAgentToolResourceFunction + | PersistedAgentToolResourceToolSearch + | PersistedAgentToolResourceProgrammaticToolCalling + | PersistedAgentToolResourceMcp + | PersistedAgentToolResourceWebSearch + | PersistedAgentToolResourceComputerUse +export const PersistedAgentToolResource = Schema.Union([ + PersistedAgentToolResourceFunction, + PersistedAgentToolResourceToolSearch, + PersistedAgentToolResourceProgrammaticToolCalling, + PersistedAgentToolResourceMcp, + PersistedAgentToolResourceWebSearch, + PersistedAgentToolResourceComputerUse +], { mode: "oneOf" }).annotate({ + "description": "A credential-free tool available to a reusable agent.", + "identifier": "PersistedAgentToolResource" +}) +export type UpdateAgentSessionParams = { + readonly "spend_control"?: SessionSpendControlParam | null + readonly "agent"?: UpdateSessionAgentParam + readonly "metadata"?: { readonly [x: string]: string } | null +} +export const UpdateAgentSessionParams = Schema.Struct({ + "spend_control": Schema.optionalKey( + Schema.Union([SessionSpendControlParam, Schema.Null]).annotate({ + "description": "Omit to retain the limit; null or a null limit removes it without resetting spend." + }) + ), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => UpdateSessionAgentParam).annotate({ + "description": "Model settings for subsequent turns. Omitted fields stay unchanged." + }) + ), + "metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ + "description": + "Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters." + }) + ) +}).annotate({ "description": "Fields to update on an existing session.", "identifier": "UpdateAgentSessionParams" }) +export type PersistedAgentToolConfigParam = + | PersistedAgentToolConfigParamFunction + | PersistedAgentToolConfigParamToolSearch + | PersistedAgentToolConfigParamProgrammaticToolCalling + | PersistedAgentToolConfigParamMcp + | PersistedAgentToolConfigParamWebSearch + | PersistedAgentToolConfigParamComputerUse +export const PersistedAgentToolConfigParam = Schema.Union([ + PersistedAgentToolConfigParamFunction, + PersistedAgentToolConfigParamToolSearch, + PersistedAgentToolConfigParamProgrammaticToolCalling, + PersistedAgentToolConfigParamMcp, + PersistedAgentToolConfigParamWebSearch, + PersistedAgentToolConfigParamComputerUse +], { mode: "oneOf" }).annotate({ + "description": "A tool that can be stored on a reusable agent without session credentials.", + "identifier": "PersistedAgentToolConfigParam" +}) +export type EnvironmentResource = + | EnvironmentResourceNone + | EnvironmentResourceOpenaiHosted + | EnvironmentResourceSelfHosted +export const EnvironmentResource = Schema.Union([ + EnvironmentResourceNone, + EnvironmentResourceOpenaiHosted, + EnvironmentResourceSelfHosted +], { mode: "oneOf" }).annotate({ + "description": "The execution environment for a session.", + "identifier": "EnvironmentResource" +}) +export type EnvironmentTemplateListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const EnvironmentTemplateListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(EnvironmentTemplateResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "EnvironmentTemplateListResource" +}) +export type AgentToolResource = + | AgentToolResourceFunction + | AgentToolResourceProgrammaticToolCalling + | AgentToolResourceMcp + | AgentToolResourceWebSearch + | AgentToolResourceComputerUse +export const AgentToolResource = Schema.Union([ + AgentToolResourceFunction, + AgentToolResourceProgrammaticToolCalling, + AgentToolResourceMcp, + AgentToolResourceWebSearch, + AgentToolResourceComputerUse +], { mode: "oneOf" }).annotate({ "description": "A tool available to the agent.", "identifier": "AgentToolResource" }) +export type AgentToolConfigParam = + | AgentToolConfigParamFunction + | AgentToolConfigParamToolSearch + | AgentToolConfigParamProgrammaticToolCalling + | AgentToolConfigParamMcp + | AgentToolConfigParamWebSearch + | AgentToolConfigParamComputerUse +export const AgentToolConfigParam = Schema.Union([ + AgentToolConfigParamFunction, + AgentToolConfigParamToolSearch, + AgentToolConfigParamProgrammaticToolCalling, + AgentToolConfigParamMcp, + AgentToolConfigParamWebSearch, + AgentToolConfigParamComputerUse +], { mode: "oneOf" }).annotate({ + "description": "A tool available to the agent.", + "identifier": "AgentToolConfigParam" +}) +export type CreateSessionInputParam = string | ReadonlyArray +export const CreateSessionInputParam = Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + Schema.Array(InputMessageParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })) +], { mode: "oneOf" }).annotate({ + "description": "Initial input submitted when creating a session.", + "identifier": "CreateSessionInputParam" +}) +export type SessionInputParamAgentSessionInputMessage = { + readonly "type": "agent.session.input.message" + readonly "input": ReadonlyArray +} +export const SessionInputParamAgentSessionInputMessage = Schema.Struct({ + "type": Schema.Literal("agent.session.input.message").annotate({ + "description": "The type of the object. Always `agent.session.input.message`." + }), + "input": Schema.Array(InputMessageParam).annotate({ "description": "The user messages to add to the session." }) + .check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ) +}).annotate({ + "description": "Adds one or more user messages and starts a turn.", + "identifier": "SessionInputParamAgentSessionInputMessage" +}) +export type SessionInputParamAgentSessionInputToolResult = { + readonly "type": "agent.session.input.tool_result" + readonly "turn_id": string + readonly "call_id": string + readonly "success": boolean + readonly "output"?: FunctionCallOutputParam | null + readonly "error"?: string | null +} +export const SessionInputParamAgentSessionInputToolResult = Schema.Struct({ + "type": Schema.Literal("agent.session.input.tool_result").annotate({ + "description": "The type of the object. Always `agent.session.input.tool_result`." + }), + "turn_id": Schema.String.annotate({ "description": "The ID of the turn that requested the function call." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "call_id": Schema.String.annotate({ "description": "The ID of the function call." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "success": Schema.Boolean.annotate({ "description": "Whether the function call succeeded." }), + "output": Schema.optionalKey( + Schema.Union([FunctionCallOutputParam, Schema.Null]).annotate({ + "description": "The function result when the call succeeded." + }) + ), + "error": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "The error message when the call failed." }) + ) +}).annotate({ + "description": "Submits the result of a function call.", + "identifier": "SessionInputParamAgentSessionInputToolResult" +}) +export type SessionInputParamAgentSessionInputComputerUseApprovalRequestResult = { + readonly "type": "agent.session.input.computer_use_approval_request_result" + readonly "request_id": string + readonly "response": ComputerUseApprovalResponseParam +} +export const SessionInputParamAgentSessionInputComputerUseApprovalRequestResult = Schema.Struct({ + "type": Schema.Literal("agent.session.input.computer_use_approval_request_result").annotate({ + "description": "The type of the object. Always `agent.session.input.computer_use_approval_request_result`." + }), + "request_id": Schema.String.annotate({ "description": "The registered request ID from the required action." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "response": Schema.suspend((): Schema.Codec => ComputerUseApprovalResponseParam) + .annotate({ "description": "The response for this request type." }) +}).annotate({ + "description": "Responds to a pending Computer Use approval request.", + "identifier": "SessionInputParamAgentSessionInputComputerUseApprovalRequestResult" +}) +export type VaultCredentialAuthResourceMcpOauth = { + readonly "type": "mcp_oauth" + readonly "mcp_server_url": string + readonly "expires_at": string | null + readonly "refresh": McpOauthRefreshResource | null +} +export const VaultCredentialAuthResourceMcpOauth = Schema.Struct({ + "type": Schema.Literal("mcp_oauth").annotate({ "description": "The type of the object. Always `mcp_oauth`." }), + "mcp_server_url": Schema.String.annotate({ "description": "The HTTPS MCP server URL authorized by this credential." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + "expires_at": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "When the OAuth access token expires, as an RFC 3339 timestamp, if known." }), + "refresh": Schema.Union([McpOauthRefreshResource, Schema.Null]).annotate({ + "description": "Public refresh metadata without refresh tokens or OAuth client secrets." + }) +}).annotate({ + "description": "Public metadata for an OAuth credential; tokens and client secrets are never returned.", + "identifier": "VaultCredentialAuthResourceMcpOauth" +}) +export type CreateVaultCredentialAuthParamMcpOauth = { + readonly "type": "mcp_oauth" + readonly "mcp_server_url": string + readonly "access_token": string + readonly "expires_at"?: string | null + readonly "refresh"?: CreateMcpOauthRefreshParam | null +} +export const CreateVaultCredentialAuthParamMcpOauth = Schema.Struct({ + "type": Schema.Literal("mcp_oauth").annotate({ "description": "The type of the object. Always `mcp_oauth`." }), + "mcp_server_url": Schema.String.annotate({ "description": "The HTTPS MCP server URL authorized by this credential." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "access_token": Schema.String.annotate({ + "description": "A write-only OAuth access token; never returned by credential resources." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "When the OAuth access token expires, as an RFC 3339 timestamp, if known." }) + ), + "refresh": Schema.optionalKey( + Schema.Union([CreateMcpOauthRefreshParam, Schema.Null]).annotate({ + "description": "Optional refresh configuration for an HTTPS OAuth token endpoint." + }) + ) +}).annotate({ + "description": "An OAuth credential for an HTTPS MCP destination.", + "identifier": "CreateVaultCredentialAuthParamMcpOauth" +}) +export type RotateVaultCredentialAuthParamMcpOauth = { + readonly "type": "mcp_oauth" + readonly "access_token"?: string | null + readonly "expires_at"?: string | null + readonly "refresh"?: RotateMcpOauthRefreshParam | null +} +export const RotateVaultCredentialAuthParamMcpOauth = Schema.Struct({ + "type": Schema.Literal("mcp_oauth").annotate({ "description": "The type of the object. Always `mcp_oauth`." }), + "access_token": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ "description": "A write-only replacement OAuth access token." }) + ), + "expires_at": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "The replacement expiry as an RFC 3339 timestamp, or `null` to clear it. Omitting this field preserves the expiry unless a new access token is supplied, in which case the expiry is cleared." + }) + ), + "refresh": Schema.optionalKey( + Schema.Union([RotateMcpOauthRefreshParam, Schema.Null]).annotate({ + "description": "Optional write-only refresh-token and client-secret updates." + }) + ) +}).annotate({ + "description": "Rotate an OAuth credential for an HTTPS MCP destination.", + "identifier": "RotateVaultCredentialAuthParamMcpOauth" +}) +export type BetaResponseTextParam = { + readonly "format"?: BetaTextResponseFormatConfiguration + readonly "verbosity"?: BetaVerbosity +} +export const BetaResponseTextParam = Schema.Struct({ + "format": Schema.optionalKey(BetaTextResponseFormatConfiguration), + "verbosity": Schema.optionalKey(BetaVerbosity) +}).annotate({ + "description": + "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)\n- [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)\n", + "identifier": "BetaResponseTextParam" +}) +export type BetaCodeInterpreterTool = { + readonly "type": "code_interpreter" + readonly "container": string | BetaAutoCodeInterpreterToolParam + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaCodeInterpreterTool = Schema.Struct({ + "type": Schema.Literal("code_interpreter").annotate({ + "description": "The type of the code interpreter tool. Always `code_interpreter`.\n" + }), + "container": Schema.Union([ + Schema.String.annotate({ "description": "The container ID." }), + BetaAutoCodeInterpreterToolParam + ], { mode: "oneOf" }).annotate({ + "description": + "The code interpreter container. Can be a container ID or an object that\nspecifies uploaded file IDs to make available to your code, along with an\noptional `memory_limit` setting.\n" + }), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Code interpreter", + "description": "A tool that runs Python code to help generate a response to a prompt.\n", + "identifier": "BetaCodeInterpreterTool" +}) +export type BetaFunctionShellToolParam = { + readonly "type": "shell" + readonly "environment"?: BetaContainerAutoParam | BetaLocalEnvironmentParam | BetaContainerReferenceParam | null + readonly "allowed_callers"?: ReadonlyArray | null +} +export const BetaFunctionShellToolParam = Schema.Struct({ + "type": Schema.Literal("shell").annotate({ "description": "The type of the shell tool. Always `shell`." }), + "environment": Schema.optionalKey( + Schema.Union([ + Schema.Union([BetaContainerAutoParam, BetaLocalEnvironmentParam, BetaContainerReferenceParam], { mode: "oneOf" }), + Schema.Null + ]) + ), + "allowed_callers": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaCallableToolAllowedCaller).annotate({ "description": "The tool invocation context(s)." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Null + ]) + ) +}).annotate({ + "title": "Shell tool", + "description": "A tool that allows the model to execute shell commands.", + "identifier": "BetaFunctionShellToolParam" +}) +export type BetaNamespaceToolParam = { + readonly "type": "namespace" + readonly "name": string + readonly "description": string + readonly "tools": ReadonlyArray +} +export const BetaNamespaceToolParam = Schema.Struct({ + "type": Schema.Literal("namespace").annotate({ "description": "The type of the tool. Always `namespace`." }), + "name": Schema.String.annotate({ "description": "The namespace name used in tool calls (for example, `crm`)." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "description": Schema.String.annotate({ "description": "A description of the namespace shown to the model." }), + "tools": Schema.Array( + Schema.Union([BetaFunctionToolParam, BetaCustomToolParam], { mode: "oneOf" }).annotate({ + "description": "A function or custom tool that belongs to a namespace." + }) + ).annotate({ "description": "The function/custom tools available inside this namespace." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) +}).annotate({ + "title": "Namespace", + "description": "Groups function/custom tools under a shared namespace.", + "identifier": "BetaNamespaceToolParam" +}) +export type BetaToolSearchOutputNamespaceToolParam = { + readonly "type": "namespace" + readonly "name": string + readonly "description": string + readonly "tools": ReadonlyArray +} +export const BetaToolSearchOutputNamespaceToolParam = Schema.Struct({ + "type": Schema.Literal("namespace").annotate({ "description": "The type of the tool. Always `namespace`." }), + "name": Schema.String.annotate({ "description": "The namespace name used in tool calls (for example, `crm`)." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "description": Schema.String.annotate({ "description": "A description of the namespace shown to the model." }), + "tools": Schema.Array( + Schema.Union([BetaToolSearchOutputFunctionToolParam, BetaCustomToolParam], { mode: "oneOf" }).annotate({ + "description": "A function or custom tool loaded into a namespace by tool search." + }) + ).annotate({ "description": "The function/custom tools loaded inside this namespace." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ) +}).annotate({ + "title": "Namespace", + "description": "Groups function/custom tools under a shared namespace.", + "identifier": "BetaToolSearchOutputNamespaceToolParam" +}) +export type BetaPrompt = { + readonly "id": string + readonly "version"?: string | null + readonly "variables"?: BetaResponsePromptVariables +} | null +export const BetaPrompt = Schema.Union([ + Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique identifier of the prompt template to use." }), + "version": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional version of the prompt template." }), Schema.Null]) + ), + "variables": Schema.optionalKey(BetaResponsePromptVariables) + }).annotate({ + "description": + "Reference to a prompt template and its variables.\n[Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).\n" + }), + Schema.Null +]).annotate({ "identifier": "BetaPrompt" }) +export type BetaInputMessageContentList = ReadonlyArray +export const BetaInputMessageContentList = Schema.Array(BetaInputContent).annotate({ + "title": "Input item content list", + "description": "A list of one or many input items to the model, containing different content \ntypes.\n", + "identifier": "BetaInputMessageContentList" +}) +export type BetaFunctionToolCallOutputResource = { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "function_call_output" + readonly "call_id"?: string + readonly "name"?: string + readonly "namespace"?: string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "output": string | ReadonlyArray + readonly "status": "in_progress" | "completed" | "incomplete" + readonly "created_by"?: string +} +export const BetaFunctionToolCallOutputResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the function call tool output.\n" }), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The type of the function tool call output. Always `function_call_output`.\n" + }), + "call_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the function tool call generated by the model.\n" }) + ), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the tool that produced the output.\n" }) + ), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the tool that produced the output.\n" }) + ), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the function call.\n" + }), + Schema.Array(BetaFunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the function call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the function call generated by your code.\nCan be a string or an list of output content.\n" + }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "identifier": "BetaFunctionToolCallOutputResource" }) +export type BetaCustomToolCallOutputResource = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "custom_tool_call_output" + readonly "id": string + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "output": string | ReadonlyArray + readonly "status": BetaFunctionCallOutputStatusEnum + readonly "created_by"?: string +} +export const BetaCustomToolCallOutputResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("custom_tool_call_output").annotate({ + "description": "The type of the custom tool call output. Always `custom_tool_call_output`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the custom tool call output item.\n" }), + "call_id": Schema.String.annotate({ + "description": "The call ID, used to map this custom tool call output to a custom tool call.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the custom tool call.\n" + }), + Schema.Array(BetaFunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the custom tool call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the custom tool call generated by your code.\nCan be a string or an list of output content.\n" + }), + "status": Schema.suspend((): Schema.Codec => BetaFunctionCallOutputStatusEnum) + .annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item.\n" }) + ) +}).annotate({ "title": "ResponseCustomToolCallOutputItem", "identifier": "BetaCustomToolCallOutputResource" }) +export type BetaFunctionToolCallOutput = { + readonly "agent"?: BetaAgentTag | null + readonly "id"?: string + readonly "type": "function_call_output" + readonly "call_id"?: string + readonly "name"?: string + readonly "namespace"?: string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "output": string | ReadonlyArray + readonly "status"?: "in_progress" | "completed" | "incomplete" +} +export const BetaFunctionToolCallOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The unique ID of the function tool call output. Populated when this item\nis returned via API.\n" + }) + ), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The type of the function tool call output. Always `function_call_output`.\n" + }), + "call_id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the function tool call generated by the model.\n" }) + ), + "name": Schema.optionalKey( + Schema.String.annotate({ "description": "The name of the tool that produced the output.\n" }) + ), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the tool that produced the output.\n" }) + ), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the function call.\n" + }), + Schema.Array(BetaFunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the function call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the function call generated by your code.\nCan be a string or an list of output content.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ) +}).annotate({ + "title": "Function tool call output", + "description": "The output of a function tool call.\n", + "identifier": "BetaFunctionToolCallOutput" +}) +export type BetaCustomToolCallOutput = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "custom_tool_call_output" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "output": string | ReadonlyArray +} +export const BetaCustomToolCallOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("custom_tool_call_output").annotate({ + "description": "The type of the custom tool call output. Always `custom_tool_call_output`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the custom tool call output in the OpenAI platform.\n" }) + ), + "call_id": Schema.String.annotate({ + "description": "The call ID, used to map this custom tool call output to a custom tool call.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the custom tool call.\n" + }), + Schema.Array(BetaFunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the custom tool call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the custom tool call generated by your code.\nCan be a string or an list of output content.\n" + }) +}).annotate({ + "title": "Custom tool call output", + "description": "The output of a custom tool call from your code, being sent back to the model.\n", + "identifier": "BetaCustomToolCallOutput" +}) +export type BetaOutputMessageContent = BetaOutputTextContent | BetaRefusalContent +export const BetaOutputMessageContent = Schema.Union([BetaOutputTextContent, BetaRefusalContent], { mode: "oneOf" }) + .annotate({ "identifier": "BetaOutputMessageContent" }) +export type BetaAgentMessage = { + readonly "agent"?: BetaAgentTag + readonly "type": "agent_message" + readonly "id": string + readonly "author": string + readonly "recipient": string + readonly "content": ReadonlyArray< + | BetaInputTextContent + | BetaOutputTextContent + | BetaTextContent + | BetaSummaryTextContent + | BetaReasoningTextContent + | BetaRefusalContent + | BetaInputImageContent + | BetaComputerScreenshotContent + | BetaInputFileContent + | BetaEncryptedContent + > +} +export const BetaAgentMessage = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("agent_message").annotate({ "description": "The type of the item. Always `agent_message`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the agent message." }), + "author": Schema.String.annotate({ "description": "The sending agent identity." }), + "recipient": Schema.String.annotate({ "description": "The destination agent identity." }), + "content": Schema.Array( + Schema.Union([ + BetaInputTextContent, + BetaOutputTextContent, + BetaTextContent, + BetaSummaryTextContent, + BetaReasoningTextContent, + BetaRefusalContent, + BetaInputImageContent, + BetaComputerScreenshotContent, + BetaInputFileContent, + BetaEncryptedContent + ], { mode: "oneOf" }).annotate({ "description": "A content part that makes up an input or output item." }) + ).annotate({ "description": "Encrypted content sent between agents." }) +}).annotate({ "identifier": "BetaAgentMessage" }) +export type BetaMultiAgentCallOutput = { + readonly "agent"?: BetaAgentTag + readonly "type": "multi_agent_call_output" + readonly "id": string + readonly "call_id": string + readonly "action": BetaMultiAgentAction + readonly "output": ReadonlyArray +} +export const BetaMultiAgentCallOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("multi_agent_call_output").annotate({ + "description": "The type of the multi-agent result. Always `multi_agent_call_output`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the multi-agent call output item." }), + "call_id": Schema.String.annotate({ "description": "The unique ID of the multi-agent call." }), + "action": Schema.suspend((): Schema.Codec => BetaMultiAgentAction).annotate({ + "description": "The multi-agent action that produced this result." + }), + "output": Schema.Array(BetaOutputTextContent).annotate({ + "description": "Text output returned by the multi-agent action." + }) +}).annotate({ "identifier": "BetaMultiAgentCallOutput" }) +export type BetaOutputContent = BetaOutputTextContent | BetaRefusalContent | BetaReasoningTextContent +export const BetaOutputContent = Schema.Union([BetaOutputTextContent, BetaRefusalContent, BetaReasoningTextContent], { + mode: "oneOf" +}).annotate({ "identifier": "BetaOutputContent" }) +export type BetaMessage = { + readonly "agent"?: BetaAgentTag + readonly "type": "message" + readonly "id": string + readonly "status": BetaMessageStatus + readonly "role": BetaMessageRole + readonly "content": ReadonlyArray< + | BetaInputTextContent + | BetaOutputTextContent + | BetaTextContent + | BetaSummaryTextContent + | BetaReasoningTextContent + | BetaRefusalContent + | BetaInputImageContent + | BetaComputerScreenshotContent + | BetaInputFileContent + | BetaEncryptedContent + > + readonly "phase"?: BetaMessagePhase_2 | null +} +export const BetaMessage = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("message").annotate({ "description": "The type of the message. Always set to `message`." }), + "id": Schema.String.annotate({ "description": "The unique ID of the message." }), + "status": Schema.suspend((): Schema.Codec => BetaMessageStatus).annotate({ + "description": + "The status of item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API." + }), + "role": Schema.suspend((): Schema.Codec => BetaMessageRole).annotate({ + "description": + "The role of the message. One of `unknown`, `user`, `assistant`, `system`, `critic`, `discriminator`, `developer`, or `tool`." + }), + "content": Schema.Array( + Schema.Union([ + BetaInputTextContent, + BetaOutputTextContent, + BetaTextContent, + BetaSummaryTextContent, + BetaReasoningTextContent, + BetaRefusalContent, + BetaInputImageContent, + BetaComputerScreenshotContent, + BetaInputFileContent, + BetaEncryptedContent + ], { mode: "oneOf" }).annotate({ "description": "A content part that makes up an input or output item." }) + ).annotate({ "description": "The content of the message" }), + "phase": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaMessagePhase_2).annotate({ + "description": + "Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend phase on all assistant messages — dropping it can degrade performance. Not used for user messages." + }), + Schema.Null + ]) + ) +}).annotate({ "title": "Message", "description": "A message to or from the model.", "identifier": "BetaMessage" }) +export type BetaComputerActionList = ReadonlyArray +export const BetaComputerActionList = Schema.Array(BetaComputerAction).annotate({ + "title": "Computer Action List", + "description": + "Flattened batched actions for `computer_use`. Each action includes an\n`type` discriminator and action-specific fields.\n", + "identifier": "BetaComputerActionList" +}) +export type BetaModerationParam = { readonly "model": string; readonly "policy"?: BetaModerationPolicyParam | null } +export const BetaModerationParam = Schema.Struct({ + "model": Schema.String.annotate({ + "description": "The moderation model to use for moderated completions, e.g. 'omni-moderation-latest'." + }), + "policy": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaModerationPolicyParam).annotate({ + "description": "The policy to apply to moderated response input and output." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "Configuration for running moderation on the input and output of this response.", + "identifier": "BetaModerationParam" +}) +export type BetaErrorResponse = { readonly "error": BetaError } +export const BetaErrorResponse = Schema.Struct({ "error": BetaError }).annotate({ "identifier": "BetaErrorResponse" }) +export type RunStepObject = { + readonly "id": string + readonly "object": "thread.run.step" + readonly "created_at": number + readonly "assistant_id": string + readonly "thread_id": string + readonly "run_id": string + readonly "type": "message_creation" | "tool_calls" + readonly "status": "in_progress" | "cancelled" | "failed" | "completed" | "expired" + readonly "step_details": RunStepDetailsMessageCreationObject | RunStepDetailsToolCallsObject + readonly "last_error": { readonly "code": "server_error" | "rate_limit_exceeded"; readonly "message": string } | null + readonly "expired_at": number | null + readonly "cancelled_at": number | null + readonly "failed_at": number | null + readonly "completed_at": number | null + readonly "metadata": Metadata + readonly "usage": RunStepCompletionUsage +} +export const RunStepObject = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The identifier of the run step, which can be referenced in API endpoints." + }), + "object": Schema.Literal("thread.run.step").annotate({ + "description": "The object type, which is always `thread.run.step`." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the run step was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "assistant_id": Schema.String.annotate({ + "description": + "The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) associated with the run step." + }), + "thread_id": Schema.String.annotate({ + "description": "The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was run." + }), + "run_id": Schema.String.annotate({ + "description": + "The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) that this run step is a part of." + }), + "type": Schema.Literals(["message_creation", "tool_calls"]).annotate({ + "description": "The type of run step, which can be either `message_creation` or `tool_calls`." + }), + "status": Schema.Literals(["in_progress", "cancelled", "failed", "completed", "expired"]).annotate({ + "description": + "The status of the run step, which can be either `in_progress`, `cancelled`, `failed`, `completed`, or `expired`." + }), + "step_details": Schema.Union([RunStepDetailsMessageCreationObject, RunStepDetailsToolCallsObject], { mode: "oneOf" }) + .annotate({ "description": "The details of the run step." }), + "last_error": Schema.Union([ + Schema.Struct({ + "code": Schema.Literals(["server_error", "rate_limit_exceeded"]).annotate({ + "description": "One of `server_error` or `rate_limit_exceeded`." + }), + "message": Schema.String.annotate({ "description": "A human-readable description of the error." }) + }).annotate({ + "description": "The last error associated with this run step. Will be `null` if there are no errors." + }), + Schema.Null + ]), + "expired_at": Schema.Union([ + Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "cancelled_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the run step was cancelled.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "failed_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the run step failed.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "completed_at": Schema.Union([ + Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the run step completed.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "metadata": Metadata, + "usage": RunStepCompletionUsage +}).annotate({ + "title": "Run steps", + "description": "Represents a step in execution of a run.\n", + "identifier": "RunStepObject" +}) +export type ListAssistantsResponse = { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const ListAssistantsResponse = Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(AssistantObject), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListAssistantsResponse" }) +export type RunStreamEvent = + | { readonly "event": "thread.run.created"; readonly "data": RunObject } + | { readonly "event": "thread.run.queued"; readonly "data": RunObject } + | { readonly "event": "thread.run.in_progress"; readonly "data": RunObject } + | { readonly "event": "thread.run.requires_action"; readonly "data": RunObject } + | { readonly "event": "thread.run.completed"; readonly "data": RunObject } + | { readonly "event": "thread.run.incomplete"; readonly "data": RunObject } + | { readonly "event": "thread.run.failed"; readonly "data": RunObject } + | { readonly "event": "thread.run.cancelling"; readonly "data": RunObject } + | { readonly "event": "thread.run.cancelled"; readonly "data": RunObject } + | { readonly "event": "thread.run.expired"; readonly "data": RunObject } +export const RunStreamEvent = Schema.Union([ + Schema.Struct({ "event": Schema.Literal("thread.run.created"), "data": RunObject }).annotate({ + "description": "Occurs when a new [run](https://developers.openai.com/api/docs/assistants/migration) is created." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.queued"), "data": RunObject }).annotate({ + "description": + "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `queued` status." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.in_progress"), "data": RunObject }).annotate({ + "description": + "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` status." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.requires_action"), "data": RunObject }).annotate({ + "description": + "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `requires_action` status." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.completed"), "data": RunObject }).annotate({ + "description": "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is completed." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.incomplete"), "data": RunObject }).annotate({ + "description": + "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) ends with status `incomplete`." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.failed"), "data": RunObject }).annotate({ + "description": "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) fails." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.cancelling"), "data": RunObject }).annotate({ + "description": + "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `cancelling` status." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.cancelled"), "data": RunObject }).annotate({ + "description": "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is cancelled." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.expired"), "data": RunObject }).annotate({ + "description": "Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) expires." + }) +], { mode: "oneOf" }).annotate({ "identifier": "RunStreamEvent" }) +export type ListRunsResponse = { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const ListRunsResponse = Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(RunObject), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListRunsResponse" }) +export type VideoListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const VideoListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(VideoResource).annotate({ "description": "A list of items" }), + "first_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the first item in the list." }), + Schema.Null + ]), + "last_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the last item in the list." }), + Schema.Null + ]), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }) +}).annotate({ "identifier": "VideoListResource" }) +export type CreateChatCompletionResponse = { + readonly "id": string + readonly "choices": ReadonlyArray< + { + readonly "finish_reason": "stop" | "length" | "tool_calls" | "content_filter" | "function_call" + readonly "index": number + readonly "message": ChatCompletionResponseMessage + readonly "logprobs": { + readonly "content": ReadonlyArray | null + readonly "refusal": ReadonlyArray | null + } | null + } + > + readonly "created": number + readonly "model": string + readonly "metadata"?: Metadata + readonly "service_tier"?: ServiceTier + readonly "system_fingerprint"?: string + readonly "object": "chat.completion" + readonly "usage"?: CompletionUsage + readonly "moderation"?: ChatCompletionModeration | null +} +export const CreateChatCompletionResponse = Schema.Struct({ + "id": Schema.String.annotate({ "description": "A unique identifier for the chat completion." }), + "choices": Schema.Array(Schema.Struct({ + "finish_reason": Schema.Literals(["stop", "length", "tool_calls", "content_filter", "function_call"]).annotate({ + "description": + "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\nRead the [Model Spec](https://model-spec.openai.com/2025-12-18.html) for more.\n" + }), + "index": Schema.Number.annotate({ "description": "The index of the choice in the list of choices." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "message": ChatCompletionResponseMessage, + "logprobs": Schema.Union([ + Schema.Struct({ + "content": Schema.Union([ + Schema.Array(ChatCompletionTokenLogprob).annotate({ + "description": "A list of message content tokens with log probability information." + }), + Schema.Null + ]), + "refusal": Schema.Union([ + Schema.Array(ChatCompletionTokenLogprob).annotate({ + "description": "A list of message refusal tokens with log probability information." + }), + Schema.Null + ]) + }).annotate({ "description": "Log probability information for the choice." }), + Schema.Null + ]) + })).annotate({ "description": "A list of chat completion choices. Can be more than one if `n` is greater than 1." }), + "created": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) of when the chat completion was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "model": Schema.String.annotate({ "description": "The model used for the chat completion." }), + "metadata": Schema.optionalKey(Metadata), + "service_tier": Schema.optionalKey(ServiceTier), + "system_fingerprint": Schema.optionalKey( + Schema.String.annotate({ + "description": + "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + }) + ), + "object": Schema.Literal("chat.completion").annotate({ + "description": "The object type, which is always `chat.completion`." + }), + "usage": Schema.optionalKey(CompletionUsage), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ChatCompletionModeration).annotate({ + "description": + "Moderation results for the request input and generated output, if moderated\ncompletions were requested.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "Represents a chat completion response returned by model, based on the provided input.", + "identifier": "CreateChatCompletionResponse" +}) +export type CreateChatCompletionStreamResponse = { + readonly "id": string + readonly "choices": ReadonlyArray< + { + readonly "delta": ChatCompletionStreamResponseDelta + readonly "logprobs"?: + | { + readonly "content": ReadonlyArray | null + readonly "refusal": ReadonlyArray | null + } & { readonly [x: string]: Schema.Json } + | null + readonly "finish_reason"?: "stop" | "length" | "tool_calls" | "content_filter" | "function_call" | null + readonly "index": number + } + > + readonly "created": number + readonly "model": string + readonly "obfuscation"?: string + readonly "service_tier"?: ServiceTier + readonly "system_fingerprint"?: string + readonly "object": "chat.completion.chunk" + readonly "usage"?: CompletionUsage | null + readonly "moderation"?: ChatCompletionModeration | null +} +export const CreateChatCompletionStreamResponse = Schema.Struct({ + "id": Schema.String.annotate({ + "description": "A unique identifier for the chat completion. Each chunk has the same ID." + }), + "choices": Schema.Array(Schema.Struct({ + "delta": ChatCompletionStreamResponseDelta, + "logprobs": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "content": Schema.Union([Schema.Array(ChatCompletionTokenLogprob), Schema.Null]).annotate({ + "description": "A list of message content tokens with log probability information." + }), + "refusal": Schema.Union([Schema.Array(ChatCompletionTokenLogprob), Schema.Null]).annotate({ + "description": "A list of message refusal tokens with log probability information." + }) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ "description": "Log probability information for the choice." }) + ), + "finish_reason": Schema.optionalKey( + Schema.Union([Schema.Literals(["stop", "length", "tool_calls", "content_filter", "function_call"]), Schema.Null]) + .annotate({ + "description": + "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\nOmitted from the final audio update containing only `delta.audio.expires_at`.\n" + }) + ), + "index": Schema.Number.annotate({ "description": "The index of the choice in the list of choices." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + })).annotate({ + "description": + "A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. Can also be empty for the\nlast chunk if you set `stream_options: {\"include_usage\": true}`.\n" + }), + "created": Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "model": Schema.String.annotate({ "description": "The model to generate the completion." }), + "obfuscation": Schema.optionalKey( + Schema.String.annotate({ + "description": + "An obfuscation string added to normalize the size of streamed chunks as a\nmitigation to certain side-channel attacks. The field is included by\ndefault and omitted when `stream_options.include_obfuscation` is `false`.\n" + }) + ), + "service_tier": Schema.optionalKey(ServiceTier), + "system_fingerprint": Schema.optionalKey( + Schema.String.annotate({ + "description": + "This fingerprint represents the backend configuration that the model runs with.\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + }) + ), + "object": Schema.Literal("chat.completion.chunk").annotate({ + "description": "The object type, which is always `chat.completion.chunk`." + }), + "usage": Schema.optionalKey( + Schema.Union([CompletionUsage, Schema.Null]).annotate({ + "description": + "An optional field that will only be present when you set\n`stream_options: {\"include_usage\": true}` in your request. When present, it\ncontains a null value **except for the last chunk** which contains the\ntoken usage statistics for the entire request.\n\n**NOTE:** If the stream is interrupted or cancelled, you may not\nreceive the final usage chunk which contains the total token usage for\nthe request.\n" + }) + ), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ChatCompletionModeration).annotate({ + "description": + "Moderation results for the request input and generated output. Present\non the moderation chunk when moderated completions are requested.\n" + }), + Schema.Null + ]) + ) +}).annotate({ + "description": + "Represents a streamed chunk of a chat completion response returned\nby the model, based on the provided input.\n[Learn more](https://developers.openai.com/api/docs/guides/streaming-responses).\n", + "identifier": "CreateChatCompletionStreamResponse" +}) +export type ChatCompletionRequestMessage = + | ChatCompletionRequestDeveloperMessage + | ChatCompletionRequestSystemMessage + | ChatCompletionRequestUserMessage + | ChatCompletionRequestAssistantMessage + | ChatCompletionRequestToolMessage + | ChatCompletionRequestFunctionMessage +export const ChatCompletionRequestMessage = Schema.Union([ + ChatCompletionRequestDeveloperMessage, + ChatCompletionRequestSystemMessage, + ChatCompletionRequestUserMessage, + ChatCompletionRequestAssistantMessage, + ChatCompletionRequestToolMessage, + ChatCompletionRequestFunctionMessage +], { mode: "oneOf" }).annotate({ "identifier": "ChatCompletionRequestMessage" }) +export type EvalItemContent = EvalItemContentItem | EvalItemContentArray +export const EvalItemContent = Schema.Union([EvalItemContentItem, EvalItemContentArray], { mode: "oneOf" }).annotate({ + "title": "Eval content", + "description": + "Inputs to the model - can contain template strings. Supports text, output text, input images, and input audio, either as a single item or an array of items.\n", + "identifier": "EvalItemContent" +}) +export type OutputMessage = { + readonly "id": string + readonly "type": "message" + readonly "role": "assistant" + readonly "content": ReadonlyArray + readonly "phase"?: MessagePhase | null + readonly "status": "in_progress" | "completed" | "incomplete" +} +export const OutputMessage = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the output message.\n" }), + "type": Schema.Literal("message").annotate({ "description": "The type of the output message. Always `message`.\n" }), + "role": Schema.Literal("assistant").annotate({ + "description": "The role of the output message. Always `assistant`.\n" + }), + "content": Schema.Array(OutputMessageContent).annotate({ "description": "The content of the output message.\n" }), + "phase": Schema.optionalKey(Schema.Union([MessagePhase, Schema.Null])), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + }) +}).annotate({ + "title": "Output message", + "description": "An output message from the model.\n", + "identifier": "OutputMessage" +}) +export type ResponseContentPartAddedEvent = { + readonly "type": "response.content_part.added" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "part": OutputContent + readonly "sequence_number": number +} +export const ResponseContentPartAddedEvent = Schema.Struct({ + "type": Schema.Literal("response.content_part.added").annotate({ + "description": "The type of the event. Always `response.content_part.added`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the content part was added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the content part was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the content part that was added.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.suspend((): Schema.Codec => OutputContent).annotate({ + "description": "The content part that was added.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a new content part is added.", + "identifier": "ResponseContentPartAddedEvent" +}) +export type ResponseContentPartDoneEvent = { + readonly "type": "response.content_part.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "sequence_number": number + readonly "part": OutputContent +} +export const ResponseContentPartDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.content_part.done").annotate({ + "description": "The type of the event. Always `response.content_part.done`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the content part was added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the content part was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the content part that is done.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.suspend((): Schema.Codec => OutputContent).annotate({ + "description": "The content part that is done.\n" + }) +}).annotate({ "description": "Emitted when a content part is done.", "identifier": "ResponseContentPartDoneEvent" }) +export type EasyInputMessage = { + readonly "role": "user" | "assistant" | "system" | "developer" + readonly "content": string | InputMessageContentList + readonly "phase"?: MessagePhase | null + readonly "type"?: "message" +} +export const EasyInputMessage = Schema.Struct({ + "role": Schema.Literals(["user", "assistant", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n" + }), + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text input", "description": "A text input to the model.\n" }), + InputMessageContentList + ], { mode: "oneOf" }).annotate({ + "description": + "Text, image, or audio input to the model, used to generate a response.\nCan also contain previous assistant responses.\n" + }), + "phase": Schema.optionalKey(Schema.Union([MessagePhase, Schema.Null])), + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The type of the message input. Always `message`.\n" }) + ) +}).annotate({ + "title": "Input message", + "description": + "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n", + "identifier": "EasyInputMessage" +}) +export type InputMessageResource = { + readonly "type": "message" + readonly "role": "user" | "system" | "developer" + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "content": InputMessageContentList + readonly "id": string +} +export const InputMessageResource = Schema.Struct({ + "type": Schema.Literal("message").annotate({ + "description": "The type of the message input. Always set to `message`.\n" + }), + "role": Schema.Literals(["user", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `system`, or `developer`.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "content": InputMessageContentList, + "id": Schema.String.annotate({ "description": "The unique ID of the message input.\n" }) +}).annotate({ "identifier": "InputMessageResource" }) +export type RealtimeSessionCreateRequestGA = { + readonly "type": "realtime" + readonly "output_modalities"?: ReadonlyArray<"text" | "audio"> + readonly "model"?: + | string + | "gpt-realtime" + | "gpt-realtime-1.5" + | "gpt-realtime-2" + | "gpt-realtime-2.1" + | "gpt-realtime-2.1-mini" + | "gpt-realtime-2025-08-28" + | "gpt-4o-realtime-preview" + | "gpt-4o-realtime-preview-2024-10-01" + | "gpt-4o-realtime-preview-2024-12-17" + | "gpt-4o-realtime-preview-2025-06-03" + | "gpt-4o-mini-realtime-preview" + | "gpt-4o-mini-realtime-preview-2024-12-17" + | "gpt-realtime-mini" + | "gpt-realtime-mini-2025-10-06" + | "gpt-realtime-mini-2025-12-15" + | "gpt-audio-1.5" + | "gpt-audio-mini" + | "gpt-audio-mini-2025-10-06" + | "gpt-audio-mini-2025-12-15" + readonly "instructions"?: string + readonly "audio"?: { + readonly "input"?: { + readonly "format"?: RealtimeAudioFormats + readonly "transcription"?: AudioTranscription + readonly "noise_reduction"?: + | null + | { readonly "type"?: NoiseReductionType } & { readonly [x: string]: Schema.Json } + readonly "turn_detection"?: RealtimeTurnDetection + } + readonly "output"?: { + readonly "format"?: RealtimeAudioFormats + readonly "voice"?: VoiceIdsOrCustomVoice + readonly "speed"?: number + } + } + readonly "include"?: ReadonlyArray<"item.input_audio_transcription.logprobs"> + readonly "tracing"?: "auto" | { + readonly "workflow_name"?: string + readonly "group_id"?: string + readonly "metadata"?: { readonly [x: string]: Schema.Json } + } | null + readonly "tools"?: ReadonlyArray + readonly "tool_choice"?: ToolChoiceOptions | ToolChoiceFunction | ToolChoiceMCP + readonly "parallel_tool_calls"?: boolean + readonly "reasoning"?: RealtimeReasoning + readonly "max_output_tokens"?: number | "inf" + readonly "truncation"?: RealtimeTruncation + readonly "prompt"?: Prompt +} +export const RealtimeSessionCreateRequestGA = Schema.Struct({ + "type": Schema.Literal("realtime").annotate({ + "description": "The type of session to create. Always `realtime` for the Realtime API.\n" + }), + "output_modalities": Schema.optionalKey( + Schema.Array(Schema.Literals(["text", "audio"])).annotate({ + "description": + "The set of modalities the model can respond with. It defaults to `[\"audio\"]`, indicating\nthat the model will respond with audio plus a transcript. `[\"text\"]` can be used to make\nthe model respond with text only. It is not possible to request both `text` and `audio` at the same time.\n" + }) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15" + ]) + ]).annotate({ "description": "The Realtime model used for this session.\n" }) + ), + "instructions": Schema.optionalKey(Schema.String.annotate({ + "description": + "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\n\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + })), + "audio": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RealtimeAudioFormats).annotate({ + "description": "The format of the input audio." + }) + ), + "transcription": Schema.optionalKey( + Schema.suspend((): Schema.Codec => AudioTranscription).annotate({ + "description": + "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + }) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.Null, + Schema.StructWithRest(Schema.Struct({ "type": Schema.optionalKey(NoiseReductionType) }), [ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + ]) + ]).annotate({ + "description": + "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + }) + ), + "turn_detection": Schema.optionalKey(RealtimeTurnDetection) + })), + "output": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RealtimeAudioFormats).annotate({ + "description": "The format of the output audio." + }) + ), + "voice": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VoiceIdsOrCustomVoice).annotate({ + "description": + "The voice the model uses to respond. Supported built-in voices are\n`alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,\n`marin`, and `cedar`. You may also provide a custom voice object with\nan `id`, for example `{ \"id\": \"voice_1234\" }`. Voice cannot be changed\nduring the session once the model has responded with audio at least once.\nCustom voices must be created from audio samples.\nWe recommend `marin` and `cedar` for best quality.\n" + }) + ), + "speed": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The speed of the model's spoken response as a multiple of the original speed.\n1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. This value can only be changed in between model turns, not while a response is in progress.\n\nThis parameter is a post-processing adjustment to the audio after it is generated, it's\nalso possible to prompt the model to speak faster or slower.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0.25).annotate({ "expected": "a value greater than or equal to 0.25" }) + ).check(Schema.isLessThanOrEqualTo(1.5).annotate({ "expected": "a value less than or equal to 1.5" })) + ) + })) + }).annotate({ "description": "Configuration for input and output audio.\n" }) + ), + "include": Schema.optionalKey( + Schema.Array(Schema.Literal("item.input_audio_transcription.logprobs")).annotate({ + "description": + "Additional fields to include in server outputs.\n\n`item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.\n" + }) + ), + "tracing": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto").annotate({ + "title": "auto", + "description": "Enables tracing and sets default values for tracing configuration options. Always `auto`.\n" + }), + Schema.Struct({ + "workflow_name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The name of the workflow to attach to this trace. This is used to\nname the trace in the Traces Dashboard.\n" + }) + ), + "group_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The group id to attach to this trace to enable filtering and\ngrouping in the Traces Dashboard.\n" + }) + ), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The arbitrary metadata to attach to this trace to enable\nfiltering in the Traces Dashboard.\n" + }) + ) + }).annotate({ "title": "Tracing Configuration", "description": "Granular configuration for tracing.\n" }), + Schema.Null + ], { mode: "oneOf" }).annotate({ + "title": "Tracing Configuration", + "description": + "Realtime API can write session traces to the [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to disable tracing. Once\ntracing is enabled for a session, the configuration cannot be modified.\n\n`auto` will create a trace for the session with default values for the\nworkflow name, group id, and metadata.\n" + }) + ), + "tools": Schema.optionalKey( + Schema.Array(Schema.Union([RealtimeFunctionTool, MCPTool], { mode: "oneOf" })).annotate({ + "description": "Tools available to the model." + }) + ), + "tool_choice": Schema.optionalKey( + Schema.Union([ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMCP], { mode: "oneOf" }).annotate({ + "description": + "How the model chooses tools. Provide one of the string modes or force a specific\nfunction/MCP tool.\n" + }) + ), + "parallel_tool_calls": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Whether the model may call multiple tools in parallel. Only supported by\nreasoning Realtime models such as `gpt-realtime-2`.\n" + }) + ), + "reasoning": Schema.optionalKey(RealtimeReasoning), + "max_output_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Literal("inf")], { + mode: "oneOf" + }).annotate({ + "description": + "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + }) + ), + "truncation": Schema.optionalKey(RealtimeTruncation), + "prompt": Schema.optionalKey(Prompt) +}).annotate({ + "title": "Realtime session configuration", + "description": "Realtime session object configuration.", + "identifier": "RealtimeSessionCreateRequestGA" +}) +export type RealtimeSessionCreateResponseGA = { + readonly "type": "realtime" + readonly "id": string + readonly "object": "realtime.session" + readonly "expires_at"?: number + readonly "output_modalities"?: ReadonlyArray<"text" | "audio"> + readonly "model"?: + | string + | "gpt-realtime" + | "gpt-realtime-1.5" + | "gpt-realtime-2" + | "gpt-realtime-2.1" + | "gpt-realtime-2.1-mini" + | "gpt-realtime-2025-08-28" + | "gpt-4o-realtime-preview" + | "gpt-4o-realtime-preview-2024-10-01" + | "gpt-4o-realtime-preview-2024-12-17" + | "gpt-4o-realtime-preview-2025-06-03" + | "gpt-4o-mini-realtime-preview" + | "gpt-4o-mini-realtime-preview-2024-12-17" + | "gpt-realtime-mini" + | "gpt-realtime-mini-2025-10-06" + | "gpt-realtime-mini-2025-12-15" + | "gpt-audio-1.5" + | "gpt-audio-mini" + | "gpt-audio-mini-2025-10-06" + | "gpt-audio-mini-2025-12-15" + readonly "instructions"?: string + readonly "audio"?: { + readonly "input"?: { + readonly "format"?: RealtimeAudioFormats + readonly "transcription"?: AudioTranscriptionResponse | null + readonly "noise_reduction"?: + | { readonly "type"?: NoiseReductionType } & { readonly [x: string]: Schema.Json } + | null + readonly "turn_detection"?: RealtimeTurnDetection + } + readonly "output"?: { + readonly "format"?: RealtimeAudioFormats + readonly "voice"?: VoiceIdsShared + readonly "speed"?: number + } + } + readonly "include"?: ReadonlyArray<"item.input_audio_transcription.logprobs"> | null + readonly "tracing"?: "auto" | { + readonly "workflow_name"?: string + readonly "group_id"?: string + readonly "metadata"?: { readonly [x: string]: Schema.Json } + } | null + readonly "tools"?: ReadonlyArray + readonly "tool_choice"?: ToolChoiceOptions | ToolChoiceFunction | ToolChoiceMCP + readonly "reasoning"?: RealtimeReasoning + readonly "max_output_tokens"?: number | "inf" + readonly "truncation"?: RealtimeTruncation + readonly "prompt"?: Prompt +} +export const RealtimeSessionCreateResponseGA = Schema.Struct({ + "type": Schema.Literal("realtime").annotate({ + "description": "The type of session to create. Always `realtime` for the Realtime API.\n" + }), + "id": Schema.String.annotate({ + "description": "Unique identifier for the session that looks like `sess_1234567890abcdef`.\n" + }), + "object": Schema.Literal("realtime.session").annotate({ + "description": "The object type. Always `realtime.session`." + }), + "expires_at": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Expiration timestamp for the session, in seconds since epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "output_modalities": Schema.optionalKey( + Schema.Array(Schema.Literals(["text", "audio"])).annotate({ + "description": + "The set of modalities the model can respond with. It defaults to `[\"audio\"]`, indicating\nthat the model will respond with audio plus a transcript. `[\"text\"]` can be used to make\nthe model respond with text only. It is not possible to request both `text` and `audio` at the same time.\n" + }) + ), + "model": Schema.optionalKey( + Schema.Union([ + Schema.String, + Schema.Literals([ + "gpt-realtime", + "gpt-realtime-1.5", + "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15" + ]) + ]).annotate({ "description": "The Realtime model used for this session.\n" }) + ), + "instructions": Schema.optionalKey(Schema.String.annotate({ + "description": + "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\n\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + })), + "audio": Schema.optionalKey( + Schema.Struct({ + "input": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RealtimeAudioFormats).annotate({ + "description": "The format of the input audio." + }) + ), + "transcription": Schema.optionalKey( + Schema.Union([AudioTranscriptionResponse, Schema.Null]).annotate({ + "description": + "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + }) + ), + "noise_reduction": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest(Schema.Struct({ "type": Schema.optionalKey(NoiseReductionType) }), [ + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })) + ]), + Schema.Null + ]).annotate({ + "description": + "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + }) + ), + "turn_detection": Schema.optionalKey(RealtimeTurnDetection) + })), + "output": Schema.optionalKey(Schema.Struct({ + "format": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RealtimeAudioFormats).annotate({ + "description": "The format of the output audio." + }) + ), + "voice": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VoiceIdsShared).annotate({ + "description": + "The voice the model uses to respond. Voice cannot be changed during the\nsession once the model has responded with audio at least once. Current\nvoice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,\n`shimmer`, `verse`, `marin`, and `cedar`. We recommend `marin` and `cedar` for\nbest quality.\n" + }) + ), + "speed": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The speed of the model's spoken response as a multiple of the original speed.\n1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. This value can only be changed in between model turns, not while a response is in progress.\n\nThis parameter is a post-processing adjustment to the audio after it is generated, it's\nalso possible to prompt the model to speak faster or slower.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0.25).annotate({ "expected": "a value greater than or equal to 0.25" }) + ).check(Schema.isLessThanOrEqualTo(1.5).annotate({ "expected": "a value less than or equal to 1.5" })) + ) + })) + }).annotate({ "description": "Configuration for input and output audio.\n" }) + ), + "include": Schema.optionalKey( + Schema.Union([Schema.Array(Schema.Literal("item.input_audio_transcription.logprobs")), Schema.Null]).annotate({ + "description": + "Additional fields to include in server outputs.\n\n`item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.\n" + }) + ), + "tracing": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Literal("auto").annotate({ + "title": "auto", + "description": "Enables tracing and sets default values for tracing configuration options. Always `auto`.\n" + }), + Schema.Struct({ + "workflow_name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The name of the workflow to attach to this trace. This is used to\nname the trace in the Traces Dashboard.\n" + }) + ), + "group_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The group id to attach to this trace to enable filtering and\ngrouping in the Traces Dashboard.\n" + }) + ), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The arbitrary metadata to attach to this trace to enable\nfiltering in the Traces Dashboard.\n" + }) + ) + }).annotate({ "title": "Tracing Configuration", "description": "Granular configuration for tracing.\n" }) + ], { mode: "oneOf" }).annotate({ + "title": "Tracing Configuration", + "description": + "Realtime API can write session traces to the [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to disable tracing. Once\ntracing is enabled for a session, the configuration cannot be modified.\n\n`auto` will create a trace for the session with default values for the\nworkflow name, group id, and metadata.\n" + }), + Schema.Null + ]) + ), + "tools": Schema.optionalKey( + Schema.Array(Schema.Union([RealtimeFunctionTool, MCPTool], { mode: "oneOf" })).annotate({ + "description": "Tools available to the model." + }) + ), + "tool_choice": Schema.optionalKey( + Schema.Union([ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMCP], { mode: "oneOf" }).annotate({ + "description": + "How the model chooses tools. Provide one of the string modes or force a specific\nfunction/MCP tool.\n" + }) + ), + "reasoning": Schema.optionalKey(RealtimeReasoning), + "max_output_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Literal("inf")], { + mode: "oneOf" + }).annotate({ + "description": + "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + }) + ), + "truncation": Schema.optionalKey(RealtimeTruncation), + "prompt": Schema.optionalKey(Prompt) +}).annotate({ + "title": "Realtime session configuration object", + "description": "A Realtime session configuration object.\n", + "identifier": "RealtimeSessionCreateResponseGA" +}) +export type RealtimeSessionCreateRequest = { + readonly "client_secret": { readonly "value": string; readonly "expires_at": number } + readonly "modalities"?: ReadonlyArray<"text" | "audio"> + readonly "instructions"?: string + readonly "voice"?: VoiceIdsOrCustomVoice + readonly "input_audio_format"?: string + readonly "output_audio_format"?: string + readonly "input_audio_transcription"?: { readonly "model"?: string } + readonly "speed"?: number + readonly "tracing"?: "auto" | { + readonly "workflow_name"?: string + readonly "group_id"?: string + readonly "metadata"?: { readonly [x: string]: Schema.Json } + } + readonly "turn_detection"?: { + readonly "type"?: string + readonly "threshold"?: number + readonly "prefix_padding_ms"?: number + readonly "silence_duration_ms"?: number + } + readonly "tools"?: ReadonlyArray< + { + readonly "type"?: "function" + readonly "name"?: string + readonly "description"?: string + readonly "parameters"?: { readonly [x: string]: Schema.Json } + } + > + readonly "tool_choice"?: string + readonly "temperature"?: number + readonly "max_response_output_tokens"?: number | "inf" + readonly "truncation"?: RealtimeTruncation + readonly "prompt"?: Prompt +} +export const RealtimeSessionCreateRequest = Schema.Struct({ + "client_secret": Schema.Struct({ + "value": Schema.String.annotate({ + "description": + "Ephemeral key usable in client environments to authenticate connections\nto the Realtime API. Use this in client-side environments rather than\na standard API token, which should only be used server-side.\n" + }), + "expires_at": Schema.Number.annotate({ + "description": "Timestamp for when the token expires. Currently, all tokens expire\nafter one minute.\n", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + }).annotate({ "description": "Ephemeral key returned by the API." }), + "modalities": Schema.optionalKey( + Schema.Array(Schema.Literals(["text", "audio"])).annotate({ + "description": "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + }) + ), + "instructions": Schema.optionalKey(Schema.String.annotate({ + "description": + "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + })), + "voice": Schema.optionalKey( + Schema.suspend((): Schema.Codec => VoiceIdsOrCustomVoice).annotate({ + "description": + "The voice the model uses to respond. Supported built-in voices are\n`alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,\n`marin`, and `cedar`. You may also provide a custom voice object with an\n`id`, for example `{ \"id\": \"voice_1234\" }`. Voice cannot be changed during\nthe session once the model has responded with audio at least once.\nCustom voices must be created from audio samples.\n" + }) + ), + "input_audio_format": Schema.optionalKey( + Schema.String.annotate({ + "description": "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + }) + ), + "output_audio_format": Schema.optionalKey( + Schema.String.annotate({ + "description": "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + }) + ), + "input_audio_transcription": Schema.optionalKey( + Schema.Struct({ + "model": Schema.optionalKey(Schema.String.annotate({ "description": "The model to use for transcription.\n" })) + }).annotate({ + "description": + "Configuration for input audio transcription, defaults to off and can be\nset to `null` to turn off once on. Input audio transcription is not native\nto the model, since the model consumes audio directly. Transcription runs\nasynchronously and should be treated as rough guidance\nrather than the representation understood by the model.\n" + }) + ), + "speed": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The speed of the model's spoken response. 1.0 is the default speed. 0.25 is\nthe minimum speed. 1.5 is the maximum speed. This value can only be changed\nin between model turns, not while a response is in progress.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0.25).annotate({ "expected": "a value greater than or equal to 0.25" }) + ).check(Schema.isLessThanOrEqualTo(1.5).annotate({ "expected": "a value less than or equal to 1.5" })) + ), + "tracing": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto").annotate({ "description": "Default tracing mode for the session.\n" }), + Schema.Struct({ + "workflow_name": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The name of the workflow to attach to this trace. This is used to\nname the trace in the traces dashboard.\n" + }) + ), + "group_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The group id to attach to this trace to enable filtering and\ngrouping in the traces dashboard.\n" + }) + ), + "metadata": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The arbitrary metadata to attach to this trace to enable\nfiltering in the traces dashboard.\n" + }) + ) + }).annotate({ "title": "Tracing Configuration", "description": "Granular configuration for tracing.\n" }) + ], { mode: "oneOf" }).annotate({ + "title": "Tracing Configuration", + "description": + "Configuration options for tracing. Set to null to disable tracing. Once\ntracing is enabled for a session, the configuration cannot be modified.\n\n`auto` will create a trace for the session with default values for the\nworkflow name, group id, and metadata.\n" + }) + ), + "turn_detection": Schema.optionalKey( + Schema.Struct({ + "type": Schema.optionalKey( + Schema.String.annotate({ "description": "Type of turn detection, only `server_vad` is currently supported.\n" }) + ), + "threshold": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "prefix_padding_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "silence_duration_ms": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ) + }).annotate({ + "description": + "Configuration for turn detection. Can be set to `null` to turn off. Server\nVAD means that the model will detect the start and end of speech based on\naudio volume and respond at the end of user speech.\n" + }) + ), + "tools": Schema.optionalKey( + Schema.Array(Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("function").annotate({ "description": "The type of the tool, i.e. `function`." }) + ), + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the function." })), + "description": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The description of the function, including guidance on when and how\nto call it, and guidance about what to tell the user when calling\n(if anything).\n" + }) + ), + "parameters": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": "Parameters of the function in JSON Schema." + }) + ) + })).annotate({ "description": "Tools (functions) available to the model." }) + ), + "tool_choice": Schema.optionalKey( + Schema.String.annotate({ + "description": "How the model chooses tools. Options are `auto`, `none`, `required`, or\nspecify a function.\n" + }) + ), + "temperature": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "max_response_output_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Literal("inf")], { + mode: "oneOf" + }).annotate({ + "description": + "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + }) + ), + "truncation": Schema.optionalKey(RealtimeTruncation), + "prompt": Schema.optionalKey(Prompt) +}).annotate({ + "description": "A new Realtime session configuration, with an ephemeral key. Default TTL\nfor keys is one minute.\n", + "identifier": "RealtimeSessionCreateRequest" +}) +export type ComputerToolCall = { + readonly "type": "computer_call" + readonly "id": string + readonly "call_id": string + readonly "action"?: ComputerAction + readonly "actions"?: ComputerActionList + readonly "pending_safety_checks": ReadonlyArray + readonly "status": "in_progress" | "completed" | "incomplete" +} +export const ComputerToolCall = Schema.Struct({ + "type": Schema.Literal("computer_call").annotate({ + "description": "The type of the computer call. Always `computer_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the computer call." }), + "call_id": Schema.String.annotate({ + "description": "An identifier used when responding to the tool call with output.\n" + }), + "action": Schema.optionalKey(ComputerAction), + "actions": Schema.optionalKey(ComputerActionList), + "pending_safety_checks": Schema.Array(ComputerCallSafetyCheckParam).annotate({ + "description": "The pending safety checks for the computer call.\n" + }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) +}).annotate({ + "title": "Computer tool call", + "description": + "A tool call to a computer use tool. See the\n[computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.\n", + "identifier": "ComputerToolCall" +}) +export type Tool = + | FunctionTool + | FileSearchTool + | ComputerTool + | ComputerUsePreviewTool + | WebSearchTool + | MCPTool + | CodeInterpreterTool + | ProgrammaticToolCallingParam + | ImageGenTool + | LocalShellToolParam + | FunctionShellToolParam + | CustomToolParam + | NamespaceToolParam + | ToolSearchToolParam + | WebSearchPreviewTool + | ApplyPatchToolParam +export const Tool = Schema.Union([ + FunctionTool, + FileSearchTool, + ComputerTool, + ComputerUsePreviewTool, + WebSearchTool, + MCPTool, + CodeInterpreterTool, + ProgrammaticToolCallingParam, + ImageGenTool, + LocalShellToolParam, + FunctionShellToolParam, + CustomToolParam, + NamespaceToolParam, + ToolSearchToolParam, + WebSearchPreviewTool, + ApplyPatchToolParam +], { mode: "oneOf" }).annotate({ + "description": "A tool that can be used to generate a response.\n", + "identifier": "Tool" +}) +export type ToolSearchOutputTool = + | FunctionTool + | FileSearchTool + | ComputerTool + | ComputerUsePreviewTool + | WebSearchTool + | MCPTool + | CodeInterpreterTool + | ProgrammaticToolCallingParam + | ImageGenTool + | LocalShellToolParam + | FunctionShellToolParam + | CustomToolParam + | ToolSearchOutputNamespaceToolParam + | ToolSearchToolParam + | WebSearchPreviewTool + | ApplyPatchToolParam +export const ToolSearchOutputTool = Schema.Union([ + FunctionTool, + FileSearchTool, + ComputerTool, + ComputerUsePreviewTool, + WebSearchTool, + MCPTool, + CodeInterpreterTool, + ProgrammaticToolCallingParam, + ImageGenTool, + LocalShellToolParam, + FunctionShellToolParam, + CustomToolParam, + ToolSearchOutputNamespaceToolParam, + ToolSearchToolParam, + WebSearchPreviewTool, + ApplyPatchToolParam +], { mode: "oneOf" }).annotate({ + "description": "A tool definition returned by a tool search output.\n", + "identifier": "ToolSearchOutputTool" +}) +export type LiveResponsesDelegationSettingsInputParam = { + readonly "model": string + readonly "instructions"?: string | null + readonly "max_output_tokens"?: number | null + readonly "service_tier"?: LiveResponsesServiceTier | null + readonly "reasoning"?: LiveDelegationReasoningInputParam | null + readonly "text"?: LiveDelegationTextInputParam | null + readonly "tools"?: ReadonlyArray< + | LiveFunctionToolInputParam + | LiveWebSearchToolInputParam + | LiveFileSearchToolInputParam + | LiveCodeInterpreterToolInputParam + | LiveHostedShellToolInputParam + | LiveImageGenerationToolInputParam + | LiveMCPToolInputParam + | LiveCustomToolInputParam + | LiveNamespaceToolInputParam + | LiveToolSearchToolInputParam + | LiveProgrammaticToolInputParam + | LiveComputerToolInputParam + | LiveApplyPatchToolInputParam + > + readonly "tool_choice"?: + | LiveToolChoiceEnum + | LiveFunctionToolChoiceParam + | LiveMCPToolChoiceParam + | LiveSpecificFileSearchParam + | LiveSpecificWebSearchParam + | LiveSpecificWebSearchPreviewParam + | LiveSpecificImageGenParam + | LiveSpecificComputerParam + | LiveSpecificCodeInterpreterParam + | LiveSpecificProgrammaticToolCallingParam + | LiveSpecificFunctionShellParam + | LiveSpecificCustomToolParam + | LiveSpecificApplyPatchParam + | LiveAllowedToolsChoiceParam + readonly "parallel_tool_calls"?: boolean | null +} +export const LiveResponsesDelegationSettingsInputParam = Schema.Struct({ + "model": Schema.String.annotate({ "description": "The model used for server-owned Responses delegations." }), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt)." + }), + Schema.Null + ]) + ), + "max_output_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "Maximum number of output tokens for each delegated response." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(16).annotate({ "expected": "a value greater than or equal to 16" })), + Schema.Null + ]) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveResponsesServiceTier).annotate({ + "description": "Service tier for delegated Responses requests." + }), + Schema.Null + ]) + ), + "reasoning": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveDelegationReasoningInputParam).annotate( + { "description": "Reasoning settings passed to each delegated Responses request." } + ), + Schema.Null + ]) + ), + "text": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveDelegationTextInputParam).annotate({ + "description": "Text generation settings passed to each delegated Responses request." + }), + Schema.Null + ]) + ), + "tools": Schema.optionalKey( + Schema.Array( + Schema.Union([ + LiveFunctionToolInputParam, + LiveWebSearchToolInputParam, + LiveFileSearchToolInputParam, + LiveCodeInterpreterToolInputParam, + LiveHostedShellToolInputParam, + LiveImageGenerationToolInputParam, + LiveMCPToolInputParam, + LiveCustomToolInputParam, + LiveNamespaceToolInputParam, + LiveToolSearchToolInputParam, + LiveProgrammaticToolInputParam, + LiveComputerToolInputParam, + LiveApplyPatchToolInputParam + ], { mode: "oneOf" }) + ).annotate({ + "description": "Tools available to the Responses backend while it handles tasks delegated by the Live model." + }) + ), + "tool_choice": Schema.optionalKey( + Schema.Union([ + LiveToolChoiceEnum, + Schema.Union([ + LiveFunctionToolChoiceParam, + LiveMCPToolChoiceParam, + LiveSpecificFileSearchParam, + LiveSpecificWebSearchParam, + LiveSpecificWebSearchPreviewParam, + LiveSpecificImageGenParam, + LiveSpecificComputerParam, + LiveSpecificCodeInterpreterParam, + LiveSpecificProgrammaticToolCallingParam, + LiveSpecificFunctionShellParam, + LiveSpecificCustomToolParam, + LiveSpecificApplyPatchParam + ], { mode: "oneOf" }), + LiveAllowedToolsChoiceParam + ], { mode: "oneOf" }).annotate({ + "description": "Controls which tool the Responses backend uses when handling a task delegated by the Live model." + }) + ), + "parallel_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": "Whether the delegated Responses model may request multiple tool calls in a single response." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": "Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend.", + "identifier": "LiveResponsesDelegationSettingsInputParam" +}) +export type LiveResponsesDelegationSettingsUpdateInputParam = { + readonly "model"?: string + readonly "instructions"?: string | null + readonly "max_output_tokens"?: number | null + readonly "service_tier"?: LiveResponsesServiceTier | null + readonly "reasoning"?: LiveDelegationReasoningInputParam | null + readonly "text"?: LiveDelegationTextInputParam | null + readonly "tools"?: ReadonlyArray< + | LiveFunctionToolInputParam + | LiveWebSearchToolInputParam + | LiveFileSearchToolInputParam + | LiveCodeInterpreterToolInputParam + | LiveHostedShellToolInputParam + | LiveImageGenerationToolInputParam + | LiveMCPToolInputParam + | LiveCustomToolInputParam + | LiveNamespaceToolInputParam + | LiveToolSearchToolInputParam + | LiveProgrammaticToolInputParam + | LiveComputerToolInputParam + | LiveApplyPatchToolInputParam + > + readonly "tool_choice"?: + | LiveToolChoiceEnum + | LiveFunctionToolChoiceParam + | LiveMCPToolChoiceParam + | LiveSpecificFileSearchParam + | LiveSpecificWebSearchParam + | LiveSpecificWebSearchPreviewParam + | LiveSpecificImageGenParam + | LiveSpecificComputerParam + | LiveSpecificCodeInterpreterParam + | LiveSpecificProgrammaticToolCallingParam + | LiveSpecificFunctionShellParam + | LiveSpecificCustomToolParam + | LiveSpecificApplyPatchParam + | LiveAllowedToolsChoiceParam + readonly "parallel_tool_calls"?: boolean | null +} +export const LiveResponsesDelegationSettingsUpdateInputParam = Schema.Struct({ + "model": Schema.optionalKey( + Schema.String.annotate({ + "description": + "The Responses backend model to use for subsequent delegated requests. Omit to keep the current backend model." + }) + ), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt)." + }), + Schema.Null + ]) + ), + "max_output_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "Maximum number of output tokens for each delegated response." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ).check(Schema.isGreaterThanOrEqualTo(16).annotate({ "expected": "a value greater than or equal to 16" })), + Schema.Null + ]) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveResponsesServiceTier).annotate({ + "description": "Service tier for delegated Responses requests." + }), + Schema.Null + ]) + ), + "reasoning": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveDelegationReasoningInputParam).annotate( + { "description": "Reasoning settings passed to each delegated Responses request." } + ), + Schema.Null + ]) + ), + "text": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => LiveDelegationTextInputParam).annotate({ + "description": "Text generation settings passed to each delegated Responses request." + }), + Schema.Null + ]) + ), + "tools": Schema.optionalKey( + Schema.Array( + Schema.Union([ + LiveFunctionToolInputParam, + LiveWebSearchToolInputParam, + LiveFileSearchToolInputParam, + LiveCodeInterpreterToolInputParam, + LiveHostedShellToolInputParam, + LiveImageGenerationToolInputParam, + LiveMCPToolInputParam, + LiveCustomToolInputParam, + LiveNamespaceToolInputParam, + LiveToolSearchToolInputParam, + LiveProgrammaticToolInputParam, + LiveComputerToolInputParam, + LiveApplyPatchToolInputParam + ], { mode: "oneOf" }) + ).annotate({ + "description": "Tools available to the Responses backend while it handles tasks delegated by the Live model." + }) + ), + "tool_choice": Schema.optionalKey( + Schema.Union([ + LiveToolChoiceEnum, + Schema.Union([ + LiveFunctionToolChoiceParam, + LiveMCPToolChoiceParam, + LiveSpecificFileSearchParam, + LiveSpecificWebSearchParam, + LiveSpecificWebSearchPreviewParam, + LiveSpecificImageGenParam, + LiveSpecificComputerParam, + LiveSpecificCodeInterpreterParam, + LiveSpecificProgrammaticToolCallingParam, + LiveSpecificFunctionShellParam, + LiveSpecificCustomToolParam, + LiveSpecificApplyPatchParam + ], { mode: "oneOf" }), + LiveAllowedToolsChoiceParam + ], { mode: "oneOf" }).annotate({ + "description": "Controls which tool the Responses backend uses when handling a task delegated by the Live model." + }) + ), + "parallel_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": "Whether the delegated Responses model may request multiple tool calls in a single response." + }), + Schema.Null + ]) + ) +}).annotate({ + "description": + "Updates to the Responses backend of an existing Live session. Omitted settings retain their current values.", + "identifier": "LiveResponsesDelegationSettingsUpdateInputParam" +}) +export type ListAuditLogsResponse = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id"?: string | null + readonly "last_id"?: string | null + readonly "has_more": boolean +} +export const ListAuditLogsResponse = Schema.Struct({ + "object": Schema.Literal("list"), + "data": Schema.Array(AuditLog), + "first_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "last_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "has_more": Schema.Boolean +}).annotate({ "identifier": "ListAuditLogsResponse" }) +export type CreateVectorStoreFileBatchRequest = { + readonly "file_ids": ReadonlyArray + readonly "files"?: ReadonlyArray + readonly "chunking_strategy"?: ChunkingStrategyRequestParam + readonly "attributes"?: VectorStoreFileAttributes +} | { + readonly "file_ids"?: ReadonlyArray + readonly "files": ReadonlyArray + readonly "chunking_strategy"?: ChunkingStrategyRequestParam + readonly "attributes"?: VectorStoreFileAttributes +} +export const CreateVectorStoreFileBatchRequest = Schema.Union([ + Schema.Struct({ + "file_ids": Schema.Array(Schema.String).annotate({ + "description": + "A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied to all files in the batch. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `files`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "files": Schema.optionalKey( + Schema.Array(CreateVectorStoreFileRequest).annotate({ + "description": + "A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must be specified for each file. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `file_ids`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) + ), + "chunking_strategy": Schema.optionalKey(ChunkingStrategyRequestParam), + "attributes": Schema.optionalKey(VectorStoreFileAttributes) + }), + Schema.Struct({ + "file_ids": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied to all files in the batch. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `files`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ) + ), + "files": Schema.Array(CreateVectorStoreFileRequest).annotate({ + "description": + "A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must be specified for each file. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `file_ids`." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "chunking_strategy": Schema.optionalKey(ChunkingStrategyRequestParam), + "attributes": Schema.optionalKey(VectorStoreFileAttributes) + }) +]).annotate({ "identifier": "CreateVectorStoreFileBatchRequest" }) +export type ThreadItem = + | UserMessageItem + | AssistantMessageItem + | WidgetMessageItem + | ClientToolCallItem + | TaskItem + | TaskGroupItem +export const ThreadItem = Schema.Union([ + UserMessageItem, + AssistantMessageItem, + WidgetMessageItem, + ClientToolCallItem, + TaskItem, + TaskGroupItem +], { mode: "oneOf" }).annotate({ "title": "The thread item", "identifier": "ThreadItem" }) +export type DecisionInputMessage = { + readonly "role": "user" + readonly "content": DecisionInputContent + readonly "type"?: "message" +} +export const DecisionInputMessage = Schema.Struct({ + "role": Schema.Literal("user"), + "content": DecisionInputContent, + "type": Schema.optionalKey(Schema.Literal("message")) +}).annotate({ "description": "A user message containing text or images.", "identifier": "DecisionInputMessage" }) +export type DecisionResponse = { + readonly "model": string + readonly "answers": ReadonlyArray + readonly "usage": ResponseUsageResource +} +export const DecisionResponse = Schema.Struct({ + "model": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "answers": Schema.Array(AnswerResource).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "usage": ResponseUsageResource +}).annotate({ "identifier": "DecisionResponse" }) +export type CreateEnvironmentParamOpenaiHosted = { + readonly "type": "openai_hosted" + readonly "packages"?: EnvironmentPackagesParam | null + readonly "setup_commands"?: ReadonlyArray | null + readonly "network"?: NetworkPolicyParam | null + readonly "desktop"?: DesktopParam | null + readonly "env"?: { readonly [x: string]: string } | null + readonly "capability_directories"?: ReadonlyArray | null + readonly "skills"?: ReadonlyArray | null + readonly "plugins"?: ReadonlyArray | null + readonly "files"?: ReadonlyArray | null + readonly "environment_template_id"?: string +} +export const CreateEnvironmentParamOpenaiHosted = Schema.Struct({ + "type": Schema.Literal("openai_hosted").annotate({ + "description": "The type of the object. Always `openai_hosted`." + }), + "packages": Schema.optionalKey( + Schema.Union([EnvironmentPackagesParam, Schema.Null]).annotate({ + "description": "Packages to install in the environment. Defaults to empty package lists." + }) + ), + "setup_commands": Schema.optionalKey( + Schema.Union([ + Schema.Array(SetupCommandParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" })), + Schema.Null + ]).annotate({ "description": "Ordered, confidential setup commands. Command bodies are never returned." }) + ), + "network": Schema.optionalKey( + Schema.Union([NetworkPolicyParam, Schema.Null]).annotate({ + "description": + "Network access policy for the environment. If omitted, the API version determines whether network access is enabled or disabled." + }) + ), + "desktop": Schema.optionalKey( + Schema.Union([DesktopParam, Schema.Null]).annotate({ + "description": "Desktop provisioning. Omission or null inherits the template setting, or defaults to disabled." + }) + ), + "env": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Environment variables made available to the agent." }) + ), + "capability_directories": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ + "description": "Directories that contain capabilities exposed to the agent. Defaults to an empty list." + }) + ), + "skills": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedSkillParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })), + Schema.Null + ]).annotate({ + "description": "Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty list." + }) + ), + "plugins": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedPluginParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(32).annotate({ "expected": "a value with a length of at most 32" })), + Schema.Null + ]).annotate({ "description": "Plugins provided as inline ZIP archives. Defaults to an empty list." }) + ), + "files": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedEnvironmentFileParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })), + Schema.Null + ]).annotate({ "description": "Files available before the agent starts. Defaults to an empty list." }) + ), + "environment_template_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A reusable hosted template applied before inline configuration. Omitted fields inherit the template; network overrides cannot broaden its policy." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ) + ) +}).annotate({ + "description": "Provision an OpenAI-hosted environment from inline configuration or a template.", + "identifier": "CreateEnvironmentParamOpenaiHosted" +}) +export type CreateEnvironmentTemplateParams = { + readonly "packages"?: EnvironmentPackagesParam | null + readonly "setup_commands"?: ReadonlyArray | null + readonly "network"?: NetworkPolicyParam | null + readonly "desktop"?: DesktopParam | null + readonly "env"?: { readonly [x: string]: string } | null + readonly "capability_directories"?: ReadonlyArray | null + readonly "skills"?: ReadonlyArray | null + readonly "plugins"?: ReadonlyArray | null + readonly "files"?: ReadonlyArray | null + readonly "name"?: string | null +} +export const CreateEnvironmentTemplateParams = Schema.Struct({ + "packages": Schema.optionalKey( + Schema.Union([EnvironmentPackagesParam, Schema.Null]).annotate({ + "description": "Packages to install in the environment. Defaults to empty package lists." + }) + ), + "setup_commands": Schema.optionalKey( + Schema.Union([ + Schema.Array(SetupCommandParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" })), + Schema.Null + ]).annotate({ "description": "Ordered, confidential setup commands. Command bodies are never returned." }) + ), + "network": Schema.optionalKey( + Schema.Union([NetworkPolicyParam, Schema.Null]).annotate({ + "description": + "Network access policy for the environment. If omitted, the API version determines whether network access is enabled or disabled." + }) + ), + "desktop": Schema.optionalKey( + Schema.Union([DesktopParam, Schema.Null]).annotate({ + "description": "Desktop provisioning. Omission or null inherits the template setting, or defaults to disabled." + }) + ), + "env": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Environment variables made available to the agent." }) + ), + "capability_directories": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ + "description": "Directories that contain capabilities exposed to the agent. Defaults to an empty list." + }) + ), + "skills": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedSkillParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })), + Schema.Null + ]).annotate({ + "description": "Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty list." + }) + ), + "plugins": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedPluginParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(32).annotate({ "expected": "a value with a length of at most 32" })), + Schema.Null + ]).annotate({ "description": "Plugins provided as inline ZIP archives. Defaults to an empty list." }) + ), + "files": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedEnvironmentFileParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })), + Schema.Null + ]).annotate({ "description": "Files available before the agent starts. Defaults to an empty list." }) + ), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ), + Schema.Null + ]).annotate({ "description": "An optional human-readable display name for the template." }) + ) +}).annotate({ + "description": "Parameters for creating a reusable, project-scoped OpenAI-hosted environment template.", + "identifier": "CreateEnvironmentTemplateParams" +}) +export type UpdateEnvironmentTemplateParams = { + readonly "name"?: string | null + readonly "packages"?: EnvironmentPackagesParam | null + readonly "setup_commands"?: ReadonlyArray | null + readonly "network"?: NetworkPolicyParam | null + readonly "desktop"?: DesktopParam | null + readonly "env"?: { readonly [x: string]: string } | null + readonly "capability_directories"?: ReadonlyArray | null + readonly "skills"?: ReadonlyArray | null + readonly "plugins"?: ReadonlyArray | null + readonly "files"?: ReadonlyArray | null +} +export const UpdateEnvironmentTemplateParams = Schema.Struct({ + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ), + Schema.Null + ]).annotate({ "description": "A replacement human-readable display name, or `null` to clear the name." }) + ), + "packages": Schema.optionalKey( + Schema.Union([EnvironmentPackagesParam, Schema.Null]).annotate({ + "description": "Packages installed before the runtime network policy applies." + }) + ), + "setup_commands": Schema.optionalKey( + Schema.Union([ + Schema.Array(SetupCommandParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" })), + Schema.Null + ]).annotate({ "description": "Replacement confidential setup commands, never included in returned resources." }) + ), + "network": Schema.optionalKey( + Schema.Union([NetworkPolicyParam, Schema.Null]).annotate({ + "description": + "Network access available after setup completes. Omit to preserve the current policy, or pass `null` to reset to the default policy." + }) + ), + "desktop": Schema.optionalKey( + Schema.Union([DesktopParam, Schema.Null]).annotate({ + "description": "Replacement desktop configuration, or null to disable the desktop." + }) + ), + "env": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Replacement confidential environment values." }) + ), + "capability_directories": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "Directories that expose capabilities to the agent." }) + ), + "skills": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedSkillParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })), + Schema.Null + ]).annotate({ "description": "Replacement skill configuration installed for each new session." }) + ), + "plugins": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedPluginParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(32).annotate({ "expected": "a value with a length of at most 32" })), + Schema.Null + ]).annotate({ "description": "Replacement plugin configuration installed for each new session." }) + ), + "files": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedEnvironmentFileParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })), + Schema.Null + ]).annotate({ "description": "Replacement file configuration materialized for each new session." }) + ) +}).annotate({ + "description": "Fields to replace on an existing reusable OpenAI-hosted environment template.", + "identifier": "UpdateEnvironmentTemplateParams" +}) +export type EnvironmentParamOpenaiHosted = { + readonly "type": "openai_hosted" + readonly "packages"?: EnvironmentPackagesParam | null + readonly "setup_commands"?: ReadonlyArray | null + readonly "network"?: NetworkPolicyParam | null + readonly "desktop"?: DesktopParam | null + readonly "env"?: { readonly [x: string]: string } | null + readonly "capability_directories"?: ReadonlyArray | null + readonly "skills"?: ReadonlyArray | null + readonly "plugins"?: ReadonlyArray | null + readonly "files"?: ReadonlyArray | null + readonly "environment_id"?: never + readonly "environment_template_id"?: string + readonly "container_size"?: ContainerSizeParam +} | { + readonly "type": "openai_hosted" + readonly "packages"?: never + readonly "setup_commands"?: never + readonly "network"?: never + readonly "desktop"?: never + readonly "env"?: never + readonly "capability_directories"?: never + readonly "skills"?: never + readonly "plugins"?: never + readonly "files"?: never + readonly "environment_id"?: string + readonly "environment_template_id"?: never + readonly "container_size"?: never +} +export const EnvironmentParamOpenaiHosted = Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("openai_hosted").annotate({ + "description": "The type of the object. Always `openai_hosted`." + }), + "packages": Schema.optionalKey( + Schema.Union([EnvironmentPackagesParam, Schema.Null]).annotate({ + "description": "Packages to install in the environment. Defaults to empty package lists." + }) + ), + "setup_commands": Schema.optionalKey( + Schema.Union([ + Schema.Array(SetupCommandParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16).annotate({ "expected": "a value with a length of at most 16" })), + Schema.Null + ]).annotate({ "description": "Ordered, confidential setup commands. Command bodies are never returned." }) + ), + "network": Schema.optionalKey( + Schema.Union([NetworkPolicyParam, Schema.Null]).annotate({ + "description": + "Network access policy for the environment. If omitted, the API version determines whether network access is enabled or disabled." + }) + ), + "desktop": Schema.optionalKey( + Schema.Union([DesktopParam, Schema.Null]).annotate({ + "description": "Desktop provisioning. Omission or null inherits the template setting, or defaults to disabled." + }) + ), + "env": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(1024).annotate({ "expected": "a value with at most 1024 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ "description": "Environment variables made available to the agent." }) + ), + "capability_directories": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ + "description": "Directories that contain capabilities exposed to the agent. Defaults to an empty list." + }) + ), + "skills": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedSkillParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })), + Schema.Null + ]).annotate({ + "description": "Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty list." + }) + ), + "plugins": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedPluginParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(32).annotate({ "expected": "a value with a length of at most 32" })), + Schema.Null + ]).annotate({ "description": "Plugins provided as inline ZIP archives. Defaults to an empty list." }) + ), + "files": Schema.optionalKey( + Schema.Union([ + Schema.Array(HostedEnvironmentFileParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(50).annotate({ "expected": "a value with a length of at most 50" })), + Schema.Null + ]).annotate({ "description": "Files available before the agent starts. Defaults to an empty list." }) + ), + "environment_id": Schema.optionalKey(Schema.Never), + "environment_template_id": Schema.optionalKey( + Schema.String.annotate({ + "description": + "A reusable hosted template applied before inline session configuration. Omitted fields inherit the template; network overrides cannot broaden its policy." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ) + ), + "container_size": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ContainerSizeParam).annotate({ + "description": "The hosted container size. Omission selects the medium tier." + }) + ) + }), + Schema.Struct({ + "type": Schema.Literal("openai_hosted").annotate({ + "description": "The type of the object. Always `openai_hosted`." + }), + "packages": Schema.optionalKey(Schema.Never), + "setup_commands": Schema.optionalKey(Schema.Never), + "network": Schema.optionalKey(Schema.Never), + "desktop": Schema.optionalKey(Schema.Never), + "env": Schema.optionalKey(Schema.Never), + "capability_directories": Schema.optionalKey(Schema.Never), + "skills": Schema.optionalKey(Schema.Never), + "plugins": Schema.optionalKey(Schema.Never), + "files": Schema.optionalKey(Schema.Never), + "environment_id": Schema.optionalKey( + Schema.String.annotate({ + "description": "An existing prewarmed environment. Cannot be combined with a template or inline configuration." + }).check(Schema.isMinCodePoints(9).annotate({ "expected": "a string with at least 9 code points" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ), + "environment_template_id": Schema.optionalKey(Schema.Never), + "container_size": Schema.optionalKey(Schema.Never) + }) +]).annotate({ + "description": "An existing OpenAI-hosted environment or new inline/template-based hosted configuration.", + "identifier": "EnvironmentParamOpenaiHosted" +}) +export type SessionTurnItemResource = + | MessageItemResource + | ReasoningItemResource + | FunctionCallItemResource + | FunctionCallOutputItemResource + | AgentMessageItemResource + | McpCallItemResource + | ComputerUseCallItemResource + | BrowserAuthenticationRequestItemResource + | ComputerUseApprovalRequestResultItemResource + | WebSearchCallItemResource + | CommandExecutionItemResource + | CreateSubagentCallItemResource + | SendSubagentInputCallItemResource + | ResumeSubagentCallItemResource + | WaitForSubagentsCallItemResource + | InterruptSubagentCallItemResource + | CloseSubagentCallItemResource +export const SessionTurnItemResource = Schema.Union([ + MessageItemResource, + ReasoningItemResource, + FunctionCallItemResource, + FunctionCallOutputItemResource, + AgentMessageItemResource, + McpCallItemResource, + ComputerUseCallItemResource, + BrowserAuthenticationRequestItemResource, + ComputerUseApprovalRequestResultItemResource, + WebSearchCallItemResource, + CommandExecutionItemResource, + CreateSubagentCallItemResource, + SendSubagentInputCallItemResource, + ResumeSubagentCallItemResource, + WaitForSubagentsCallItemResource, + InterruptSubagentCallItemResource, + CloseSubagentCallItemResource +], { mode: "oneOf" }).annotate({ + "description": "An item associated with a session turn.", + "identifier": "SessionTurnItemResource" +}) +export type AgentOutputItemResource = + | AssistantMessageItemResource + | ReasoningItemResource + | FunctionCallItemResource + | McpCallItemResource + | ComputerUseCallItemResource + | BrowserAuthenticationRequestItemResource + | WebSearchCallItemResource + | CommandExecutionItemResource + | CreateSubagentCallItemResource + | SendSubagentInputCallItemResource + | ResumeSubagentCallItemResource + | WaitForSubagentsCallItemResource + | InterruptSubagentCallItemResource + | CloseSubagentCallItemResource +export const AgentOutputItemResource = Schema.Union([ + AssistantMessageItemResource, + ReasoningItemResource, + FunctionCallItemResource, + McpCallItemResource, + ComputerUseCallItemResource, + BrowserAuthenticationRequestItemResource, + WebSearchCallItemResource, + CommandExecutionItemResource, + CreateSubagentCallItemResource, + SendSubagentInputCallItemResource, + ResumeSubagentCallItemResource, + WaitForSubagentsCallItemResource, + InterruptSubagentCallItemResource, + CloseSubagentCallItemResource +], { mode: "oneOf" }).annotate({ + "description": "An output item produced by an agent.", + "identifier": "AgentOutputItemResource" +}) +export type SessionRequiredActionResource = + | SessionRequiredActionResourceComputerUseApprovalRequest + | SessionRequiredActionResourceFunctionCall + | SessionRequiredActionResourceEnvironmentConnection +export const SessionRequiredActionResource = Schema.Union([ + SessionRequiredActionResourceComputerUseApprovalRequest, + SessionRequiredActionResourceFunctionCall, + SessionRequiredActionResourceEnvironmentConnection +], { mode: "oneOf" }).annotate({ + "description": "An action that must be completed before a session can continue.", + "identifier": "SessionRequiredActionResource" +}) +export type AgentResource = { + readonly "id": string + readonly "object": "agent" + readonly "created_at": number + readonly "updated_at": number + readonly "name": string | null + readonly "metadata": { readonly [x: string]: string } + readonly "model": string + readonly "reasoning": ReasoningResource + readonly "text": TextResource + readonly "service_tier": ServiceTierResource + readonly "instructions": string | null + readonly "tools": ReadonlyArray + readonly "multi_agent": MultiAgentConfigResource +} +export const AgentResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the reusable agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent").annotate({ "description": "The object type. Always `agent`." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the agent was created.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "updated_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the agent was last updated.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "A human-readable name for the agent, or null if it is unnamed." }), + "metadata": Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Custom string key-value pairs attached to the agent." }).check( + Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "model": Schema.String.annotate({ "description": "The requested model name used for inference." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "reasoning": Schema.suspend((): Schema.Codec => ReasoningResource).annotate({ + "description": "The resolved reasoning configuration, including the model default for an omitted effort." + }), + "text": Schema.suspend((): Schema.Codec => TextResource).annotate({ + "description": "The resolved configuration for text generated by the agent." + }), + "service_tier": Schema.suspend((): Schema.Codec => ServiceTierResource).annotate({ + "description": "The resolved service-tier policy used for model requests." + }), + "instructions": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "Custom instructions appended to the agent's default base instructions." }), + "tools": Schema.Array(PersistedAgentToolResource).annotate({ "description": "Tools available to the agent." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "multi_agent": Schema.suspend((): Schema.Codec => MultiAgentConfigResource).annotate({ + "description": "The resolved configuration for creating and coordinating subagents." + }) +}).annotate({ "description": "A reusable agent scoped to the caller's project.", "identifier": "AgentResource" }) +export type CreateAgentParams = { + readonly "metadata"?: { readonly [x: string]: string } | null + readonly "name"?: string | null + readonly "model": string + readonly "reasoning"?: ReasoningParam | null + readonly "text"?: TextParam | null + readonly "service_tier"?: ServiceTierParam | null + readonly "instructions"?: string | null + readonly "tools"?: ReadonlyArray | null + readonly "multi_agent"?: MultiAgentConfigCurrentParam | null +} +export const CreateAgentParams = Schema.Struct({ + "metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ + "description": + "Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters. Omission or null defaults to an empty map." + }) + ), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ]).annotate({ "description": "A human-readable name for the agent. Omission or null leaves the agent unnamed." }) + ), + "model": Schema.String.annotate({ + "description": "The model to use for the agent. The requested model name is preserved." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "reasoning": Schema.optionalKey( + Schema.Union([ReasoningParam, Schema.Null]).annotate({ + "description": "Configuration for model reasoning. Omission uses the model's default effort." + }) + ), + "text": Schema.optionalKey( + Schema.Union([TextParam, Schema.Null]).annotate({ + "description": "Configuration for generated text. Defaults to the `text` format and medium verbosity." + }) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ServiceTierParam, Schema.Null]).annotate({ + "description": "The service tier used for model requests. Defaults to `auto`." + }) + ), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "Additional instructions appended to the agent's default base instructions. Omit or set to null to add no custom instructions." + }) + ), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array(PersistedAgentToolConfigParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + Schema.Null + ]).annotate({ + "description": + "Tools available to the agent. Defaults to an empty list. The tool list must fit within 3 MiB (3,145,728 bytes) of compact UTF-8 JSON." + }) + ), + "multi_agent": Schema.optionalKey( + Schema.Union([MultiAgentConfigCurrentParam, Schema.Null]).annotate({ + "description": "Configuration for creating and coordinating subagents. Subagent tools are disabled by default." + }) + ) +}).annotate({ "description": "Parameters for creating a reusable agent.", "identifier": "CreateAgentParams" }) +export type UpdateAgentParams = { + readonly "model"?: string + readonly "reasoning"?: ReasoningParam | null + readonly "text"?: TextParam | null + readonly "service_tier"?: ServiceTierParam | null + readonly "instructions"?: string | null + readonly "multi_agent"?: MultiAgentConfigCurrentParam | null + readonly "metadata"?: { readonly [x: string]: string } | null + readonly "name"?: string | null + readonly "tools"?: ReadonlyArray | null +} +export const UpdateAgentParams = Schema.Struct({ + "model": Schema.optionalKey( + Schema.String.annotate({ "description": "The model to use for the agent. The requested model name is preserved." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) + ), + "reasoning": Schema.optionalKey( + Schema.Union([ReasoningParam, Schema.Null]).annotate({ + "description": + "Configuration for model reasoning. Omit to keep the current settings; pass `null` to reset to the model's default effort." + }) + ), + "text": Schema.optionalKey( + Schema.Union([TextParam, Schema.Null]).annotate({ "description": "Configuration for text generated by the agent." }) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ServiceTierParam, Schema.Null]).annotate({ + "description": "The service tier used for model requests." + }) + ), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "Additional instructions appended to the agent's default base instructions. Omit to leave unchanged." + }) + ), + "multi_agent": Schema.optionalKey( + Schema.Union([MultiAgentConfigCurrentParam, Schema.Null]).annotate({ + "description": "Configuration for creating and coordinating subagents." + }) + ), + "metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ + "description": + "Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters." + }) + ), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ]).annotate({ "description": "A replacement name. Omit to leave unchanged, or pass null to clear it." }) + ), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array(PersistedAgentToolConfigParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + Schema.Null + ]).annotate({ + "description": + "Replaces the tool list. Omit to leave it unchanged, or pass null to clear it. The replacement must fit within 3 MiB (3,145,728 bytes) of compact UTF-8 JSON." + }) + ) +}).annotate({ "description": "Fields to replace on an existing reusable agent.", "identifier": "UpdateAgentParams" }) +export type SessionAgentResource = { + readonly "id": string + readonly "name": string | null + readonly "model": string + readonly "reasoning": ReasoningResource + readonly "text": TextResource + readonly "service_tier": ServiceTierResource + readonly "instructions": string | null + readonly "tools": ReadonlyArray + readonly "multi_agent": MultiAgentConfigResource +} +export const SessionAgentResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The reusable agent's name when the session was created, or null if no name was saved. Later changes to the agent's name do not affect this value." + }), + "model": Schema.String.annotate({ "description": "The model used by the agent." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "reasoning": Schema.suspend((): Schema.Codec => ReasoningResource).annotate({ + "description": "The agent's reasoning configuration." + }), + "text": Schema.suspend((): Schema.Codec => TextResource).annotate({ + "description": "Configuration for text generated by the agent." + }), + "service_tier": Schema.suspend((): Schema.Codec => ServiceTierResource).annotate({ + "description": "The effective service-tier policy for model requests. Defaults to `auto`." + }), + "instructions": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "Custom instructions appended to the agent's default base instructions." }), + "tools": Schema.Array(AgentToolResource).annotate({ "description": "Tools available to the agent." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "multi_agent": Schema.suspend((): Schema.Codec => MultiAgentConfigResource).annotate({ + "description": "Configuration for creating and coordinating subagents." + }) +}).annotate({ "description": "The effective agent configuration for a session.", "identifier": "SessionAgentResource" }) +export type SessionAgentConfigParam = { + readonly "model"?: string + readonly "reasoning"?: ReasoningParam | null + readonly "text"?: TextParam | null + readonly "service_tier"?: ServiceTierParam | null + readonly "instructions"?: string | null + readonly "multi_agent"?: MultiAgentConfigCurrentParam | null + readonly "tools"?: ReadonlyArray | null +} +export const SessionAgentConfigParam = Schema.Struct({ + "model": Schema.optionalKey( + Schema.String.annotate({ "description": "The model to use for the agent. The requested model name is preserved." }) + .check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ) + ), + "reasoning": Schema.optionalKey( + Schema.Union([ReasoningParam, Schema.Null]).annotate({ + "description": + "Configuration for model reasoning. Omit to keep the current settings; pass `null` to reset to the model's default effort." + }) + ), + "text": Schema.optionalKey( + Schema.Union([TextParam, Schema.Null]).annotate({ "description": "Configuration for text generated by the agent." }) + ), + "service_tier": Schema.optionalKey( + Schema.Union([ServiceTierParam, Schema.Null]).annotate({ + "description": "The service tier used for model requests." + }) + ), + "instructions": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]).annotate({ + "description": + "Additional instructions appended to the agent's default base instructions. Omit to leave unchanged." + }) + ), + "multi_agent": Schema.optionalKey( + Schema.Union([MultiAgentConfigCurrentParam, Schema.Null]).annotate({ + "description": "Configuration for creating and coordinating subagents." + }) + ), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array(AgentToolConfigParam).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" })), + Schema.Null + ]).annotate({ + "description": + "Tools available to the agent. Omit to inherit, or pass null to clear them. The resolved tool list must fit within 3 MiB (3,145,728 bytes) of compact UTF-8 JSON." + }) + ) +}).annotate({ + "description": + "Agent configuration for a session. Omitted fields inherit from `agent_id` when supplied. Supplied objects and arrays replace the whole field; null resets nullable fields.", + "identifier": "SessionAgentConfigParam" +}) +export type SessionInputParam = + | SessionInputParamAgentSessionInputComputerUseApprovalRequestResult + | SessionInputParamAgentSessionInputMessage + | SessionInputParamAgentSessionInputCancel + | SessionInputParamAgentSessionInputToolResult +export const SessionInputParam = Schema.Union([ + SessionInputParamAgentSessionInputComputerUseApprovalRequestResult, + SessionInputParamAgentSessionInputMessage, + SessionInputParamAgentSessionInputCancel, + SessionInputParamAgentSessionInputToolResult +], { mode: "oneOf" }).annotate({ + "description": "Input submitted to an existing session.", + "identifier": "SessionInputParam" +}) +export type VaultCredentialAuthResource = + | VaultCredentialAuthResourceMcpOauth + | VaultCredentialAuthResourceStaticBearer + | VaultCredentialAuthResourceEnvironmentVariable +export const VaultCredentialAuthResource = Schema.Union([ + VaultCredentialAuthResourceMcpOauth, + VaultCredentialAuthResourceStaticBearer, + VaultCredentialAuthResourceEnvironmentVariable +], { mode: "oneOf" }).annotate({ + "description": "The authentication configuration of a vault credential, excluding secrets.", + "identifier": "VaultCredentialAuthResource" +}) +export type CreateVaultCredentialAuthParam = + | CreateVaultCredentialAuthParamMcpOauth + | CreateVaultCredentialAuthParamStaticBearer + | CreateVaultCredentialAuthParamEnvironmentVariable +export const CreateVaultCredentialAuthParam = Schema.Union([ + CreateVaultCredentialAuthParamMcpOauth, + CreateVaultCredentialAuthParamStaticBearer, + CreateVaultCredentialAuthParamEnvironmentVariable +], { mode: "oneOf" }).annotate({ + "description": "Authentication credentials for an MCP server or an OpenAI-hosted environment.", + "identifier": "CreateVaultCredentialAuthParam" +}) +export type RotateVaultCredentialAuthParam = + | RotateVaultCredentialAuthParamMcpOauth + | RotateVaultCredentialAuthParamStaticBearer + | RotateVaultCredentialAuthParamEnvironmentVariable +export const RotateVaultCredentialAuthParam = Schema.Union([ + RotateVaultCredentialAuthParamMcpOauth, + RotateVaultCredentialAuthParamStaticBearer, + RotateVaultCredentialAuthParamEnvironmentVariable +], { mode: "oneOf" }).annotate({ + "description": + "Updates to a vault credential without changing its authentication method or destination configuration.", + "identifier": "RotateVaultCredentialAuthParam" +}) +export type BetaTool = + | BetaFunctionTool + | BetaFileSearchTool + | BetaComputerTool + | BetaComputerUsePreviewTool + | BetaWebSearchTool + | BetaMCPTool + | BetaCodeInterpreterTool + | BetaProgrammaticToolCallingParam + | BetaImageGenTool + | BetaLocalShellToolParam + | BetaFunctionShellToolParam + | BetaCustomToolParam + | BetaNamespaceToolParam + | BetaToolSearchToolParam + | BetaWebSearchPreviewTool + | BetaApplyPatchToolParam +export const BetaTool = Schema.Union([ + BetaFunctionTool, + BetaFileSearchTool, + BetaComputerTool, + BetaComputerUsePreviewTool, + BetaWebSearchTool, + BetaMCPTool, + BetaCodeInterpreterTool, + BetaProgrammaticToolCallingParam, + BetaImageGenTool, + BetaLocalShellToolParam, + BetaFunctionShellToolParam, + BetaCustomToolParam, + BetaNamespaceToolParam, + BetaToolSearchToolParam, + BetaWebSearchPreviewTool, + BetaApplyPatchToolParam +], { mode: "oneOf" }).annotate({ + "description": "A tool that can be used to generate a response.\n", + "identifier": "BetaTool" +}) +export type BetaToolSearchOutputTool = + | BetaFunctionTool + | BetaFileSearchTool + | BetaComputerTool + | BetaComputerUsePreviewTool + | BetaWebSearchTool + | BetaMCPTool + | BetaCodeInterpreterTool + | BetaProgrammaticToolCallingParam + | BetaImageGenTool + | BetaLocalShellToolParam + | BetaFunctionShellToolParam + | BetaCustomToolParam + | BetaToolSearchOutputNamespaceToolParam + | BetaToolSearchToolParam + | BetaWebSearchPreviewTool + | BetaApplyPatchToolParam +export const BetaToolSearchOutputTool = Schema.Union([ + BetaFunctionTool, + BetaFileSearchTool, + BetaComputerTool, + BetaComputerUsePreviewTool, + BetaWebSearchTool, + BetaMCPTool, + BetaCodeInterpreterTool, + BetaProgrammaticToolCallingParam, + BetaImageGenTool, + BetaLocalShellToolParam, + BetaFunctionShellToolParam, + BetaCustomToolParam, + BetaToolSearchOutputNamespaceToolParam, + BetaToolSearchToolParam, + BetaWebSearchPreviewTool, + BetaApplyPatchToolParam +], { mode: "oneOf" }).annotate({ + "description": "A tool definition returned by a tool search output.\n", + "identifier": "BetaToolSearchOutputTool" +}) +export type BetaEasyInputMessage = { + readonly "role": "user" | "assistant" | "system" | "developer" + readonly "content": string | BetaInputMessageContentList + readonly "phase"?: BetaMessagePhase | null + readonly "type"?: "message" +} +export const BetaEasyInputMessage = Schema.Struct({ + "role": Schema.Literals(["user", "assistant", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n" + }), + "content": Schema.Union([ + Schema.String.annotate({ "title": "Text input", "description": "A text input to the model.\n" }), + BetaInputMessageContentList + ], { mode: "oneOf" }).annotate({ + "description": + "Text, image, or audio input to the model, used to generate a response.\nCan also contain previous assistant responses.\n" + }), + "phase": Schema.optionalKey(Schema.Union([BetaMessagePhase, Schema.Null])), + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The type of the message input. Always `message`.\n" }) + ) +}).annotate({ + "title": "Input message", + "description": + "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n", + "identifier": "BetaEasyInputMessage" +}) +export type BetaInputMessageResource = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "message" + readonly "role": "user" | "system" | "developer" + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "content": BetaInputMessageContentList + readonly "id": string +} +export const BetaInputMessageResource = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("message").annotate({ + "description": "The type of the message input. Always set to `message`.\n" + }), + "role": Schema.Literals(["user", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `system`, or `developer`.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "content": BetaInputMessageContentList, + "id": Schema.String.annotate({ "description": "The unique ID of the message input.\n" }) +}).annotate({ "identifier": "BetaInputMessageResource" }) +export type BetaOutputMessage = { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "message" + readonly "role": "assistant" + readonly "content": ReadonlyArray + readonly "phase"?: BetaMessagePhase | null + readonly "status": "in_progress" | "completed" | "incomplete" +} +export const BetaOutputMessage = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the output message.\n" }), + "type": Schema.Literal("message").annotate({ "description": "The type of the output message. Always `message`.\n" }), + "role": Schema.Literal("assistant").annotate({ + "description": "The role of the output message. Always `assistant`.\n" + }), + "content": Schema.Array(BetaOutputMessageContent).annotate({ "description": "The content of the output message.\n" }), + "phase": Schema.optionalKey(Schema.Union([BetaMessagePhase, Schema.Null])), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + }) +}).annotate({ + "title": "Output message", + "description": "An output message from the model.\n", + "identifier": "BetaOutputMessage" +}) +export type BetaResponseContentPartAddedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.content_part.added" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "part": BetaOutputContent + readonly "sequence_number": number +} +export const BetaResponseContentPartAddedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.content_part.added").annotate({ + "description": "The type of the event. Always `response.content_part.added`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the content part was added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the content part was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the content part that was added.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.suspend((): Schema.Codec => BetaOutputContent).annotate({ + "description": "The content part that was added.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when a new content part is added.", + "identifier": "BetaResponseContentPartAddedEvent" +}) +export type BetaResponseContentPartDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.content_part.done" + readonly "item_id": string + readonly "output_index": number + readonly "content_index": number + readonly "sequence_number": number + readonly "part": BetaOutputContent +} +export const BetaResponseContentPartDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.content_part.done").annotate({ + "description": "The type of the event. Always `response.content_part.done`.\n" + }), + "item_id": Schema.String.annotate({ + "description": "The ID of the output item that the content part was added to.\n" + }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item that the content part was added to.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "content_index": Schema.Number.annotate({ "description": "The index of the content part that is done.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "part": Schema.suspend((): Schema.Codec => BetaOutputContent).annotate({ + "description": "The content part that is done.\n" + }) +}).annotate({ "description": "Emitted when a content part is done.", "identifier": "BetaResponseContentPartDoneEvent" }) +export type BetaComputerToolCall = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "computer_call" + readonly "id": string + readonly "call_id": string + readonly "action"?: BetaComputerAction + readonly "actions"?: BetaComputerActionList + readonly "pending_safety_checks": ReadonlyArray + readonly "status": "in_progress" | "completed" | "incomplete" +} +export const BetaComputerToolCall = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("computer_call").annotate({ + "description": "The type of the computer call. Always `computer_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the computer call." }), + "call_id": Schema.String.annotate({ + "description": "An identifier used when responding to the tool call with output.\n" + }), + "action": Schema.optionalKey(BetaComputerAction), + "actions": Schema.optionalKey(BetaComputerActionList), + "pending_safety_checks": Schema.Array(BetaComputerCallSafetyCheckParam).annotate({ + "description": "The pending safety checks for the computer call.\n" + }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) +}).annotate({ + "title": "Computer tool call", + "description": + "A tool call to a computer use tool. See the\n[computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.\n", + "identifier": "BetaComputerToolCall" +}) +export type RunStepStreamEvent = + | { readonly "event": "thread.run.step.created"; readonly "data": RunStepObject } + | { readonly "event": "thread.run.step.in_progress"; readonly "data": RunStepObject } + | { readonly "event": "thread.run.step.delta"; readonly "data": RunStepDeltaObject } + | { readonly "event": "thread.run.step.completed"; readonly "data": RunStepObject } + | { readonly "event": "thread.run.step.failed"; readonly "data": RunStepObject } + | { readonly "event": "thread.run.step.cancelled"; readonly "data": RunStepObject } + | { readonly "event": "thread.run.step.expired"; readonly "data": RunStepObject } +export const RunStepStreamEvent = Schema.Union([ + Schema.Struct({ "event": Schema.Literal("thread.run.step.created"), "data": RunStepObject }).annotate({ + "description": "Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is created." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.step.in_progress"), "data": RunStepObject }).annotate({ + "description": + "Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.step.delta"), "data": RunStepDeltaObject }).annotate({ + "description": + "Occurs when parts of a [run step](https://developers.openai.com/api/docs/assistants/migration) are being streamed." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.step.completed"), "data": RunStepObject }).annotate({ + "description": "Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is completed." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.step.failed"), "data": RunStepObject }).annotate({ + "description": "Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) fails." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.step.cancelled"), "data": RunStepObject }).annotate({ + "description": "Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is cancelled." + }), + Schema.Struct({ "event": Schema.Literal("thread.run.step.expired"), "data": RunStepObject }).annotate({ + "description": "Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) expires." + }) +], { mode: "oneOf" }).annotate({ "identifier": "RunStepStreamEvent" }) +export type ListRunStepsResponse = + | null + | string + | number + | boolean + | { + readonly "object": string + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean + } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const ListRunStepsResponse = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "object": Schema.String, + "data": Schema.Array(RunStepObject), + "first_id": Schema.String, + "last_id": Schema.String, + "has_more": Schema.Boolean + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]).annotate({ "identifier": "ListRunStepsResponse" }) +export type ChatCompletionList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ChatCompletionList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ + "description": "The type of this object. It is always set to \"list\".\n" + }), + "data": Schema.Array(CreateChatCompletionResponse).annotate({ + "description": "An array of chat completion objects.\n" + }), + "first_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The identifier of the first chat completion in the data array." + }), + "last_id": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The identifier of the last chat completion in the data array." + }), + "has_more": Schema.Boolean.annotate({ "description": "Indicates whether there are more Chat Completions available." }) +}).annotate({ + "title": "ChatCompletionList", + "description": "An object representing a list of Chat Completions.\n", + "identifier": "ChatCompletionList" +}) +export type CreateChatCompletionRequest = { + readonly "metadata"?: Metadata + readonly "top_logprobs"?: number + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "user"?: string | null + readonly "safety_identifier"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: "in_memory" | "24h" | null + readonly "prompt_cache_options"?: PromptCacheOptionsParam + readonly "messages": ReadonlyArray + readonly "model": ModelIdsShared + readonly "service_tier"?: ServiceTier + readonly "modalities"?: ResponseModalities + readonly "verbosity"?: Verbosity + readonly "reasoning_effort"?: ReasoningEffort + readonly "max_completion_tokens"?: number | null + readonly "frequency_penalty"?: number | null + readonly "presence_penalty"?: number | null + readonly "web_search_options"?: { + readonly "user_location"?: + | { readonly "type": "approximate"; readonly "approximate": WebSearchLocation } & { + readonly [x: string]: Schema.Json + } + | null + readonly "search_context_size"?: WebSearchContextSize + } + readonly "response_format"?: ResponseFormatText | ResponseFormatJsonSchema | ResponseFormatJsonObject + readonly "audio"?: + | { readonly "voice": VoiceIdsOrCustomVoice; readonly "format": "wav" | "aac" | "mp3" | "flac" | "opus" | "pcm16" } + & { readonly [x: string]: Schema.Json } + | null + readonly "store"?: boolean | null + readonly "moderation"?: ModerationParam | null + readonly "stream"?: boolean | null + readonly "stop"?: StopConfiguration + readonly "logit_bias"?: { readonly [x: string]: number } | null + readonly "logprobs"?: boolean | null + readonly "max_tokens"?: number | null + readonly "n"?: number | null + readonly "prediction"?: PredictionContent | null + readonly "seed"?: number | null + readonly "stream_options"?: ChatCompletionStreamOptions + readonly "tools"?: ReadonlyArray + readonly "tool_choice"?: ChatCompletionToolChoiceOption + readonly "parallel_tool_calls"?: ParallelToolCalls + readonly "function_call"?: "none" | "auto" | ChatCompletionFunctionCallOption + readonly "functions"?: ReadonlyArray +} +export const CreateChatCompletionRequest = Schema.Struct({ + "metadata": Schema.optionalKey(Metadata), + "top_logprobs": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" })).check( + Schema.makeFilterGroup([ + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }), + Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" }) + ]).annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }) + ).check( + Schema.makeFilterGroup([ + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }), + Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" }) + ]).annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n`logprobs` must be set to `true` if this parameter is used.\n" + }) + ) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ])), + "user": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.\nA stable identifier for your end-users.\nUsed to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ), + "safety_identifier": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.\nThe IDs should be a string that uniquely identifies each user, with a maximum length of 128 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey(Schema.Union([ + Schema.Literals(["in_memory", "24h"]).annotate({ + "description": + "Deprecated. Use `prompt_cache_options.ttl` instead.\n\nThe retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).\nThis field expresses a maximum retention policy, while\n`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two\nfields are independent and do not interact.\nFor `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.\n\nFor older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:\n - Organizations without ZDR enabled default to `24h`.\n - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.\n" + }), + Schema.Null + ])), + "prompt_cache_options": Schema.optionalKey(PromptCacheOptionsParam), + "messages": Schema.Array(ChatCompletionRequestMessage).annotate({ + "description": + "A list of messages comprising the conversation so far. Depending on the\n[model](https://developers.openai.com/api/docs/models) you use, different message types (modalities) are\nsupported, like [text](https://developers.openai.com/api/docs/guides/text),\n[images](https://developers.openai.com/api/docs/guides/images-vision), and [audio](https://developers.openai.com/api/docs/guides/audio).\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + "model": Schema.suspend((): Schema.Codec => ModelIdsShared).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI\noffers a wide range of models with different capabilities, performance\ncharacteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)\nto browse and compare available models.\n" + }), + "service_tier": Schema.optionalKey(ServiceTier), + "modalities": Schema.optionalKey(ResponseModalities), + "verbosity": Schema.optionalKey(Verbosity), + "reasoning_effort": Schema.optionalKey(ReasoningEffort), + "max_completion_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": + "An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).\n" + }) + ), + "frequency_penalty": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(-2).annotate({ "expected": "a value greater than or equal to -2" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "Number between -2.0 and 2.0. Positive values penalize new tokens based on\ntheir existing frequency in the text so far, decreasing the model's\nlikelihood to repeat the same line verbatim.\n" + }) + ), + "presence_penalty": Schema.optionalKey( + Schema.Union([ + Schema.Finite.check( + Schema.isGreaterThanOrEqualTo(-2).annotate({ "expected": "a value greater than or equal to -2" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]).annotate({ + "description": + "Number between -2.0 and 2.0. Positive values penalize new tokens based on\nwhether they appear in the text so far, increasing the model's likelihood\nto talk about new topics.\n" + }) + ), + "web_search_options": Schema.optionalKey( + Schema.Struct({ + "user_location": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "type": Schema.Literal("approximate").annotate({ + "description": "The type of location approximation. Always `approximate`.\n" + }), + "approximate": WebSearchLocation + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ "description": "Approximate location parameters for the search.\n" }) + ), + "search_context_size": Schema.optionalKey(WebSearchContextSize) + }).annotate({ + "title": "Web search", + "description": + "This tool searches the web for relevant results to use in a response.\nLearn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).\n" + }) + ), + "response_format": Schema.optionalKey( + Schema.Union([ResponseFormatText, ResponseFormatJsonSchema, ResponseFormatJsonObject], { mode: "oneOf" }).annotate({ + "description": + "An object specifying the format that the model must output.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables\nStructured Outputs which ensures the model will match your supplied JSON\nschema. Learn more in the [Structured Outputs\nguide](https://developers.openai.com/api/docs/guides/structured-outputs).\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n" + }) + ), + "audio": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "voice": Schema.suspend((): Schema.Codec => VoiceIdsOrCustomVoice).annotate({ + "description": + "The voice the model uses to respond. Supported built-in voices are\n`alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`,\n`sage`, `shimmer`, `marin`, and `cedar`. You may also provide a\ncustom voice object with an `id`, for example `{ \"id\": \"voice_1234\" }`.\nCustom voices must be created from audio samples.\n" + }), + "format": Schema.Literals(["wav", "aac", "mp3", "flac", "opus", "pcm16"]).annotate({ + "description": + "Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,\n`opus`, or `pcm16`.\n" + }) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": + "Parameters for audio output. Required when audio output is requested with\n`modalities: [\"audio\"]`. [Learn more](https://developers.openai.com/api/docs/guides/audio).\n" + }) + ), + "store": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "Whether or not to store the output of this chat completion request for\nuse in our [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) or\n[evals](https://developers.openai.com/api/docs/guides/evals) products.\n\nSupports text and image inputs. Note: image inputs over 8MB will be dropped.\n" + }) + ), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ModerationParam).annotate({ + "description": "Configuration for running moderation on the request input and generated output.\n" + }), + Schema.Null + ]) + ), + "stream": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)\nfor more information, along with the [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)\nguide for more information on how to handle the streaming events.\n" + }) + ), + "stop": Schema.optionalKey(StopConfiguration), + "logit_bias": Schema.optionalKey( + Schema.Union([ + Schema.Record(Schema.String, Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + Schema.Null + ]).annotate({ + "description": + "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the\ntokenizer) to an associated bias value from -100 to 100. Mathematically,\nthe bias is added to the logits generated by the model prior to sampling.\nThe exact effect will vary per model, but values between -1 and 1 should\ndecrease or increase likelihood of selection; values like -100 or 100\nshould result in a ban or exclusive selection of the relevant token.\n" + }) + ), + "logprobs": Schema.optionalKey( + Schema.Union([Schema.Boolean, Schema.Null]).annotate({ + "description": + "Whether to return log probabilities of the output tokens or not. If true,\nreturns the log probabilities of each output token returned in the\n`content` of `message`.\n" + }) + ), + "max_tokens": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]).annotate({ + "description": + "The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the\nchat completion. This value can be used to control\n[costs](https://openai.com/api/pricing/) for text generated via API.\n\nThis value is now deprecated in favor of `max_completion_tokens`, and is\nnot compatible with [o-series models](https://developers.openai.com/api/docs/guides/reasoning).\n" + }) + ), + "n": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(128).annotate({ "expected": "a value less than or equal to 128" })), + Schema.Null + ]).annotate({ + "description": + "How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs." + }) + ), + "prediction": Schema.optionalKey( + Schema.Union([PredictionContent, Schema.Null]).annotate({ + "description": + "Configuration for a [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),\nwhich can greatly improve response times when large parts of the model\nresponse are known ahead of time. This is most common when you are\nregenerating a file with only minor changes to most of the content.\n" + }) + ), + "seed": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(-9223372036854776000).annotate({ + "expected": "a value greater than or equal to -9223372036854776000" + }) + ).check( + Schema.isLessThanOrEqualTo(9223372036854776000).annotate({ + "expected": "a value less than or equal to 9223372036854776000" + }) + ), + Schema.Null + ]).annotate({ + "description": + "This feature is in Beta.\nIf specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n", + "format": "int64" + }) + ), + "stream_options": Schema.optionalKey(ChatCompletionStreamOptions), + "tools": Schema.optionalKey( + Schema.Array(Schema.Union([ChatCompletionTool, CustomToolChatCompletions], { mode: "oneOf" })).annotate({ + "description": + "A list of tools the model may call. You can provide either\n[custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) or\n[function tools](https://developers.openai.com/api/docs/guides/function-calling).\n" + }) + ), + "tool_choice": Schema.optionalKey(ChatCompletionToolChoiceOption), + "parallel_tool_calls": Schema.optionalKey(ParallelToolCalls), + "function_call": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["none", "auto"]).annotate({ + "description": + "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n" + }), + ChatCompletionFunctionCallOption + ], { mode: "oneOf" }).annotate({ + "description": + "Deprecated in favor of `tool_choice`.\n\nControls which (if any) function is called by the model.\n\n`none` means the model will not call a function and instead generates a\nmessage.\n\n`auto` means the model can pick between generating a message or calling a\nfunction.\n\nSpecifying a particular function via `{\"name\": \"my_function\"}` forces the\nmodel to call that function.\n\n`none` is the default when no functions are present. `auto` is the default\nif functions are present.\n" + }) + ), + "functions": Schema.optionalKey( + Schema.Array(ChatCompletionFunctions).annotate({ + "description": "Deprecated in favor of `tools`.\n\nA list of functions the model may generate JSON inputs for.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxLength(128).annotate({ "expected": "a value with a length of at most 128" }) + ) + ) +}).annotate({ "identifier": "CreateChatCompletionRequest" }) +export type EvalItem = { + readonly "role": "user" | "assistant" | "system" | "developer" + readonly "content": EvalItemContent + readonly "type"?: "message" +} +export const EvalItem = Schema.Struct({ + "role": Schema.Literals(["user", "assistant", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n" + }), + "content": EvalItemContent, + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ "description": "The type of the message input. Always `message`.\n" }) + ) +}).annotate({ + "title": "Eval message object", + "description": + "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n", + "identifier": "EvalItem" +}) +export type RealtimeCallCreateRequest = { readonly "sdp": string; readonly "session"?: RealtimeSessionCreateRequestGA } +export const RealtimeCallCreateRequest = Schema.Struct({ + "sdp": Schema.String.annotate({ + "description": "WebRTC Session Description Protocol (SDP) offer generated by the caller." + }), + "session": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RealtimeSessionCreateRequestGA).annotate({ + "title": "Session configuration", + "description": + "Optional session configuration to apply before the realtime session is\ncreated. Use the same parameters you would send in a [`create client secret`](https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create)\nrequest." + }) + ) +}).annotate({ + "title": "Realtime call creation request", + "description": + "Parameters required to initiate a realtime call and receive the SDP answer\nneeded to complete a WebRTC peer connection. Provide an SDP offer generated\nby your client and optionally configure the session that will answer the call.", + "identifier": "RealtimeCallCreateRequest" +}) +export type RealtimeCreateClientSecretRequest = { + readonly "expires_after"?: { readonly "anchor"?: "created_at"; readonly "seconds"?: number } + readonly "session"?: RealtimeSessionCreateRequestGA | RealtimeTranscriptionSessionCreateRequestGA +} +export const RealtimeCreateClientSecretRequest = Schema.Struct({ + "expires_after": Schema.optionalKey( + Schema.Struct({ + "anchor": Schema.optionalKey( + Schema.Literal("created_at").annotate({ + "description": + "The anchor point for the client secret expiration, meaning that `seconds` will be added to the `created_at` time of the client secret to produce an expiration timestamp. Only `created_at` is currently supported.\n" + }) + ), + "seconds": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "The number of seconds from the anchor point to the expiration. Select a value between `10` and `7200` (2 hours). This default to 600 seconds (10 minutes) if not specified.\n", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(10).annotate({ "expected": "a value greater than or equal to 10" }) + ).check(Schema.isLessThanOrEqualTo(7200).annotate({ "expected": "a value less than or equal to 7200" })) + ) + }).annotate({ + "title": "Client secret expiration", + "description": + "Configuration for the client secret expiration. Expiration refers to the time after which\na client secret will no longer be valid for creating sessions. The session itself may\ncontinue after that time once started. A secret can be used to create multiple sessions\nuntil it expires.\n" + }) + ), + "session": Schema.optionalKey( + Schema.Union([RealtimeSessionCreateRequestGA, RealtimeTranscriptionSessionCreateRequestGA], { mode: "oneOf" }) + .annotate({ + "title": "Session configuration", + "description": + "Session configuration to use for the client secret. Choose either a realtime\nsession or a transcription session.\n" + }) + ) +}).annotate({ + "title": "Realtime client secret creation request", + "description": + "Create a session and client secret for the Realtime API. The request can specify\neither a realtime or a transcription session configuration.\n[Learn more about the Realtime API](https://developers.openai.com/api/docs/guides/realtime).\n", + "identifier": "RealtimeCreateClientSecretRequest" +}) +export type RealtimeCreateClientSecretResponse = { + readonly "value": string + readonly "expires_at": number + readonly "session": RealtimeSessionCreateResponseGA | RealtimeTranscriptionSessionCreateResponseGA +} +export const RealtimeCreateClientSecretResponse = Schema.Struct({ + "value": Schema.String.annotate({ "description": "The generated client secret value." }), + "expires_at": Schema.Number.annotate({ + "description": "Expiration timestamp for the client secret, in seconds since epoch.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "session": Schema.Union([RealtimeSessionCreateResponseGA, RealtimeTranscriptionSessionCreateResponseGA], { + mode: "oneOf" + }).annotate({ + "title": "Session configuration", + "description": "The session configuration for either a realtime or transcription session.\n" + }) +}).annotate({ + "title": "Realtime session and client secret", + "description": "Response from creating a session and client secret for the Realtime API.\n", + "identifier": "RealtimeCreateClientSecretResponse" +}) +export type ToolSearchOutput = { + readonly "type": "tool_search_output" + readonly "id": string + readonly "call_id": string | null + readonly "execution": ToolSearchExecutionType + readonly "tools": ReadonlyArray + readonly "status": FunctionCallOutputStatusEnum + readonly "created_by"?: string +} +export const ToolSearchOutput = Schema.Struct({ + "type": Schema.Literal("tool_search_output").annotate({ + "description": "The type of the item. Always `tool_search_output`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool search output item." }), + "call_id": Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }), + Schema.Null + ]), + "execution": Schema.suspend((): Schema.Codec => ToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }), + "tools": Schema.Array(Tool).annotate({ "description": "The loaded tool definitions returned by tool search." }), + "status": Schema.suspend((): Schema.Codec => FunctionCallOutputStatusEnum).annotate({ + "description": "The status of the tool search output item that was recorded." + }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ "identifier": "ToolSearchOutput" }) +export type AdditionalTools = { + readonly "type": "additional_tools" + readonly "id": string + readonly "role": MessageRole + readonly "tools": ReadonlyArray +} +export const AdditionalTools = Schema.Struct({ + "type": Schema.Literal("additional_tools").annotate({ + "description": "The type of the item. Always `additional_tools`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the additional tools item." }), + "role": Schema.suspend((): Schema.Codec => MessageRole).annotate({ + "description": "The role that provided the additional tools." + }), + "tools": Schema.Array(Tool).annotate({ + "description": "The additional tool definitions made available at this item." + }) +}).annotate({ "identifier": "AdditionalTools" }) +export type ToolsArray = ReadonlyArray +export const ToolsArray = Schema.Array(Tool).annotate({ + "description": + "An array of tools the model may call while generating a response. You\ncan specify which tool to use by setting the `tool_choice` parameter.\n\nWe support the following categories of tools:\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)\n or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about\n [built-in tools](https://developers.openai.com/api/docs/guides/tools).\n- **MCP Tools**: Integrations with third-party systems via custom MCP servers\n or predefined connectors such as Google Drive and SharePoint. Learn more about\n [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code with strongly typed arguments\n and outputs. Learn more about\n [function calling](https://developers.openai.com/api/docs/guides/function-calling). You can also use\n custom tools to call your own code.\n", + "identifier": "ToolsArray" +}) +export type InputItem = + | EasyInputMessage + | { + readonly "type"?: "message" + readonly "role": "user" | "system" | "developer" + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "content": InputMessageContentList + } + | { + readonly "id": string + readonly "type": "message" + readonly "role": "assistant" + readonly "content": ReadonlyArray + readonly "phase"?: MessagePhase | null + readonly "status": "in_progress" | "completed" | "incomplete" + } + | { + readonly "id": string + readonly "type": "file_search_call" + readonly "status": "in_progress" | "searching" | "completed" | "incomplete" | "failed" + readonly "queries": ReadonlyArray + readonly "results"?: + | ReadonlyArray< + { + readonly "file_id"?: string + readonly "text"?: string + readonly "filename"?: string + readonly "attributes"?: VectorStoreFileAttributes + readonly "score"?: number + } + > + | null + } + | { + readonly "type": "computer_call" + readonly "id": string + readonly "call_id": string + readonly "action"?: ComputerAction + readonly "actions"?: ComputerActionList + readonly "pending_safety_checks": ReadonlyArray + readonly "status": "in_progress" | "completed" | "incomplete" + } + | { + readonly "id"?: string | null + readonly "call_id": string + readonly "type": "computer_call_output" + readonly "output": ComputerScreenshotImage + readonly "acknowledged_safety_checks"?: ReadonlyArray | null + readonly "status"?: FunctionCallItemStatus | null + } + | { + readonly "id": string + readonly "type": "web_search_call" + readonly "status": WebSearchCallStatus + readonly "action"?: WebSearchActionSearch | WebSearchActionOpenPage | WebSearchActionFind + } + | { + readonly "id"?: string + readonly "type": "function_call" + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "arguments": string + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "async"?: boolean + } + | { + readonly "id"?: string | null + readonly "call_id"?: string | null + readonly "type": "function_call_output" + readonly "output": + | string + | ReadonlyArray + readonly "name"?: string | null + readonly "namespace"?: string | null + readonly "caller"?: ToolCallCallerParam | null + readonly "status"?: FunctionCallItemStatus | null + } + | { + readonly "id"?: string | null + readonly "call_id"?: string | null + readonly "type": "tool_search_call" + readonly "execution"?: ToolSearchExecutionType + readonly "arguments": EmptyModelParam + readonly "status"?: FunctionCallItemStatus | null + } + | { + readonly "id"?: string | null + readonly "call_id"?: string | null + readonly "type": "tool_search_output" + readonly "execution"?: ToolSearchExecutionType + readonly "tools": ReadonlyArray + readonly "status"?: FunctionCallItemStatus | null + } + | { + readonly "id"?: string | null + readonly "type": "additional_tools" + readonly "role": "developer" + readonly "tools": ReadonlyArray + } + | { + readonly "id"?: string | null + readonly "type": "configuration_update" + readonly "reasoning"?: { readonly "effort"?: ReasoningEffort } + } + | { + readonly "type": "reasoning" + readonly "id": string + readonly "encrypted_content"?: string | null + readonly "summary": ReadonlyArray + readonly "content"?: ReadonlyArray + readonly "status"?: "in_progress" | "completed" | "incomplete" + } + | { readonly "id"?: string | null; readonly "type": "compaction"; readonly "encrypted_content": string } + | { + readonly "type": "image_generation_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "generating" | "failed" + readonly "result": string | null + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" | null + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | null + readonly "action"?: ImageGenActionEnum | null + readonly "background"?: ImageBackground | null + readonly "output_format"?: ImageOutputFormat | null + readonly "revised_prompt"?: string | null + } + | { + readonly "type": "code_interpreter_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "incomplete" | "interpreting" | "failed" + readonly "container_id": string + readonly "code": string | null + readonly "outputs": ReadonlyArray | null + } + | { + readonly "type": "local_shell_call" + readonly "id": string + readonly "call_id": string + readonly "action": LocalShellExecAction + readonly "status": "in_progress" | "completed" | "incomplete" + } + | { + readonly "type": "local_shell_call_output" + readonly "id": string + readonly "output": string + readonly "status"?: "in_progress" | "completed" | "incomplete" | null + readonly "call_id": Schema.Json + } + | { + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "type": "shell_call" + readonly "action": FunctionShellActionParam + readonly "status"?: FunctionShellCallItemStatus | null + readonly "environment"?: LocalEnvironmentParam | ContainerReferenceParam | null + } + | { + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "type": "shell_call_output" + readonly "output": ReadonlyArray + readonly "status"?: FunctionShellCallItemStatus | null + readonly "max_output_length"?: number | null + } + | { + readonly "type": "apply_patch_call" + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "status": ApplyPatchCallStatusParam + readonly "operation": ApplyPatchOperationParam + } + | { + readonly "type": "apply_patch_call_output" + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "status": ApplyPatchCallOutputStatusParam + readonly "output"?: string | null + } + | { + readonly "type": "mcp_list_tools" + readonly "id": string + readonly "server_label": string + readonly "tools": ReadonlyArray + readonly "error"?: string | null + } + | { + readonly "type": "mcp_approval_request" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string + } + | { + readonly "type": "mcp_approval_response" + readonly "id"?: string | null + readonly "approval_request_id": string + readonly "approve": boolean + readonly "reason"?: string | null + readonly "request_id": Schema.Json + } + | { + readonly "type": "mcp_call" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string + readonly "output"?: string | null + readonly "error"?: MCPToolCallError | null + readonly "status"?: MCPToolCallStatus + readonly "approval_request_id"?: string | null + } + | { + readonly "type": "custom_tool_call_output" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: ToolCallCallerParam | null + readonly "output": string | ReadonlyArray + } + | { + readonly "type": "custom_tool_call" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: ToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "input": string + readonly "async"?: boolean + } + | CompactionTriggerItemParam + | ItemReferenceParam + | ProgramItemParam + | ProgramOutputItemParam +export const InputItem = Schema.Union([ + EasyInputMessage, + Schema.Union([ + Schema.Struct({ + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ + "description": "The type of the message input. Always set to `message`.\n" + }) + ), + "role": Schema.Literals(["user", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `system`, or `developer`.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "content": InputMessageContentList + }).annotate({ + "title": "Input message", + "description": + "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role.\n" + }), + Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the output message.\n" }), + "type": Schema.Literal("message").annotate({ + "description": "The type of the output message. Always `message`.\n" + }), + "role": Schema.Literal("assistant").annotate({ + "description": "The role of the output message. Always `assistant`.\n" + }), + "content": Schema.Array(OutputMessageContent).annotate({ "description": "The content of the output message.\n" }), + "phase": Schema.optionalKey(Schema.Union([MessagePhase, Schema.Null])), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + }) + }).annotate({ "title": "Output message", "description": "An output message from the model.\n" }), + Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the file search tool call.\n" }), + "type": Schema.Literal("file_search_call").annotate({ + "description": "The type of the file search tool call. Always `file_search_call`.\n" + }), + "status": Schema.Literals(["in_progress", "searching", "completed", "incomplete", "failed"]).annotate({ + "description": + "The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n" + }), + "queries": Schema.Array(Schema.String).annotate({ "description": "The queries used to search for files.\n" }), + "results": Schema.optionalKey(Schema.Union([ + Schema.Array(Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the file.\n" })), + "text": Schema.optionalKey( + Schema.String.annotate({ "description": "The text that was retrieved from the file.\n" }) + ), + "filename": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the file.\n" })), + "attributes": Schema.optionalKey(VectorStoreFileAttributes), + "score": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The relevance score of the file - a value between 0 and 1.\n", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ) + })).annotate({ "description": "The results of the file search tool call.\n" }), + Schema.Null + ])) + }).annotate({ + "title": "File search tool call", + "description": + "The results of a file search tool call. See the\n[file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.\n" + }), + Schema.Struct({ + "type": Schema.Literal("computer_call").annotate({ + "description": "The type of the computer call. Always `computer_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the computer call." }), + "call_id": Schema.String.annotate({ + "description": "An identifier used when responding to the tool call with output.\n" + }), + "action": Schema.optionalKey(ComputerAction), + "actions": Schema.optionalKey(ComputerActionList), + "pending_safety_checks": Schema.Array(ComputerCallSafetyCheckParam).annotate({ + "description": "The pending safety checks for the computer call.\n" + }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + }).annotate({ + "title": "Computer tool call", + "description": + "A tool call to a computer use tool. See the\n[computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.\n" + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the computer tool call output." }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ "description": "The ID of the computer tool call that produced the output." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "type": Schema.Literal("computer_call_output").annotate({ + "description": "The type of the computer tool call output. Always `computer_call_output`." + }), + "output": ComputerScreenshotImage, + "acknowledged_safety_checks": Schema.optionalKey( + Schema.Union([ + Schema.Array(ComputerCallSafetyCheckParam).annotate({ + "description": "The safety checks reported by the API that have been acknowledged by the developer." + }), + Schema.Null + ]) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => FunctionCallItemStatus).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or `incomplete`. Populated when input items are returned via API." + }), + Schema.Null + ]) + ) + }).annotate({ "title": "Computer tool call output", "description": "The output of a computer tool call." }), + Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique ID of the web search tool call.\n" }), + "type": Schema.Literal("web_search_call").annotate({ + "description": "The type of the web search tool call. Always `web_search_call`.\n" + }), + "status": Schema.suspend((): Schema.Codec => WebSearchCallStatus).annotate({ + "description": "The status of the web search tool call.\n" + }), + "action": Schema.optionalKey( + Schema.Union([WebSearchActionSearch, WebSearchActionOpenPage, WebSearchActionFind], { mode: "oneOf" }).annotate( + { + "description": + "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find_in_page).\n" + } + ) + ) + }).annotate({ + "title": "Web search tool call", + "description": + "The results of a web search tool call. See the\n[web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.\n" + }), + Schema.Struct({ + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" })), + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the function tool call. Always `function_call`.\n" + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the function tool call generated by the model.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the function to run.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the function to run.\n" }), + "arguments": Schema.String.annotate({ + "description": "A JSON string of the arguments to pass to the function.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the function tool call runs asynchronously.\n" }) + ) + }).annotate({ + "title": "Function tool call", + "description": + "A tool call to run a function. See the\n[function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.\n" + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the function tool call output. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the function tool call generated by the model." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The type of the function tool call output. Always `function_call_output`." + }), + "output": Schema.Union([ + Schema.String.annotate({ "description": "A JSON string of the output of the function tool call." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array( + Schema.Union([InputTextContentParam, InputImageContentParamAutoParam, InputFileContentParam], { + mode: "oneOf" + }).annotate({ "description": "A piece of message content, such as text, an image, or a file." }) + ).annotate({ "description": "An array of content outputs (text, image, file) for the function tool call." }) + ], { mode: "oneOf" }).annotate({ "description": "Text, image, or file output of the function tool call." }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the tool that produced the output." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ]) + ), + "namespace": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The namespace of the tool that produced the output." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })).check( + Schema.isPattern(new RegExp("^[a-zA-Z0-9_-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^[a-zA-Z0-9_-]+$" + }) + ), + Schema.Null + ]) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => FunctionCallItemStatus).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API." + }), + Schema.Null + ]) + ) + }).annotate({ "title": "Function tool call output", "description": "The output of a function tool call." }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this tool search call." }), + Schema.Null + ]) + ), + "call_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "type": Schema.Literal("tool_search_call").annotate({ + "description": "The item type. Always `tool_search_call`." + }), + "execution": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }) + ), + "arguments": Schema.suspend((): Schema.Codec => EmptyModelParam).annotate({ + "description": "The arguments supplied to the tool search call." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => FunctionCallItemStatus).annotate({ + "description": "The status of the tool search call." + }), + Schema.Null + ]) + ) + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this tool search output." }), + Schema.Null + ]) + ), + "call_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "type": Schema.Literal("tool_search_output").annotate({ + "description": "The item type. Always `tool_search_output`." + }), + "execution": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }) + ), + "tools": Schema.Array(ToolSearchOutputTool).annotate({ + "description": "The loaded tool definitions returned by the tool search output." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => FunctionCallItemStatus).annotate({ + "description": "The status of the tool search output." + }), + Schema.Null + ]) + ) + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this additional tools item." }), + Schema.Null + ]) + ), + "type": Schema.Literal("additional_tools").annotate({ + "description": "The item type. Always `additional_tools`." + }), + "role": Schema.Literal("developer").annotate({ + "description": "The role that provided the additional tools. Only `developer` is supported." + }), + "tools": Schema.Array(Tool).annotate({ "description": "A list of additional tools made available at this item." }) + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the configuration update item." }), + Schema.Null + ]) + ), + "type": Schema.Literal("configuration_update").annotate({ + "description": "The item type. Always `configuration_update`." + }), + "reasoning": Schema.optionalKey( + Schema.Struct({ + "effort": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ReasoningEffort).annotate({ + "description": + "The reasoning effort to use for subsequent responses until another\nconfiguration update replaces it.\n" + }) + ) + }).annotate({ "description": "Updates to reasoning configuration. Only effort is supported." }) + ) + }).annotate({ + "description": + "An update to the conversation's response configuration. The configuration\nremains in effect for subsequent responses until it is replaced by another\nconfiguration update.\n" + }), + Schema.Struct({ + "type": Schema.Literal("reasoning").annotate({ "description": "The type of the object. Always `reasoning`.\n" }), + "id": Schema.String.annotate({ "description": "The unique identifier of the reasoning content.\n" }), + "encrypted_content": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The encrypted content of the reasoning item. This is populated by default\nfor reasoning items returned by `POST /v1/responses` and WebSocket\n`response.create` requests.\n\nWhen streaming, use the completed reasoning item and its\n`encrypted_content` from the `response.output_item.done` event in\nsubsequent requests. The `encrypted_content` in\n`response.output_item.added` may be incomplete. This is especially\nimportant when `store` is `false` or when using Zero Data Retention.\n" + }), + Schema.Null + ])), + "summary": Schema.Array(SummaryTextContent).annotate({ "description": "Reasoning summary content.\n" }), + "content": Schema.optionalKey( + Schema.Array(ReasoningTextContent).annotate({ "description": "Reasoning text content.\n" }) + ), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ) + }).annotate({ + "title": "Reasoning", + "description": + "A description of the chain of thought used by a reasoning model while generating\na response. Be sure to include these items in your `input` to the Responses API\nfor subsequent turns of a conversation if you are manually\n[managing context](https://developers.openai.com/api/docs/guides/conversation-state).\n" + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The ID of the compaction item." }), Schema.Null]) + ), + "type": Schema.Literal("compaction").annotate({ "description": "The type of the item. Always `compaction`." }), + "encrypted_content": Schema.String.annotate({ "description": "The encrypted content of the compaction summary." }) + .check( + Schema.isMaxCodePoints(104857600).annotate({ "expected": "a string with at most 104857600 code points" }) + ) + }).annotate({ + "title": "Compaction item", + "description": + "A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact)." + }), + Schema.Struct({ + "type": Schema.Literal("image_generation_call").annotate({ + "description": "The type of the image generation call. Always `image_generation_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the image generation call." }), + "status": Schema.Literals(["in_progress", "completed", "generating", "failed"]).annotate({ + "description": "The status of the image generation call." + }), + "result": Schema.Union([ + Schema.String.annotate({ "description": "The generated image encoded in base64." }), + Schema.Null + ]), + "size": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + Schema.Null + ]) + ), + "quality": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": + "The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`." + }), + Schema.Null + ]) + ), + "action": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ImageGenActionEnum).annotate({ + "description": "The action used for image generation." + }), + Schema.Null + ]) + ), + "background": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ImageBackground).annotate({ + "description": "The background setting used for generation." + }), + Schema.Null + ]) + ), + "output_format": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ImageOutputFormat).annotate({ + "description": "The output format used for generation." + }), + Schema.Null + ]) + ), + "revised_prompt": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The prompt that was used after any model prompt rewriting." }), + Schema.Null + ]) + ) + }).annotate({ "title": "Image generation call", "description": "An image generation request made by the model." }), + Schema.Struct({ + "type": Schema.Literal("code_interpreter_call").annotate({ + "description": "The type of the code interpreter tool call. Always `code_interpreter_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the code interpreter tool call.\n" }), + "status": Schema.Literals(["in_progress", "completed", "incomplete", "interpreting", "failed"]).annotate({ + "description": + "The status of the code interpreter tool call. Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and `failed`.\n" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the container used to run the code.\n" }), + "code": Schema.Union([ + Schema.String.annotate({ "description": "The code to run, or null if not available.\n" }), + Schema.Null + ]), + "outputs": Schema.Union([ + Schema.Array(Schema.Union([CodeInterpreterOutputLogs, CodeInterpreterOutputImage], { mode: "oneOf" })).annotate( + { + "description": + "The outputs generated by the code interpreter, such as logs or images.\nCan be null if no outputs are available.\n" + } + ), + Schema.Null + ]) + }).annotate({ "title": "Code interpreter tool call", "description": "A tool call to run code.\n" }), + Schema.Struct({ + "type": Schema.Literal("local_shell_call").annotate({ + "description": "The type of the local shell call. Always `local_shell_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the local shell call.\n" }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "action": LocalShellExecAction, + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the local shell call.\n" + }) + }).annotate({ "title": "Local shell call", "description": "A tool call to run a command on the local shell.\n" }), + Schema.Struct({ + "type": Schema.Literal("local_shell_call_output").annotate({ + "description": "The type of the local shell tool call output. Always `local_shell_call_output`.\n" + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "output": Schema.String.annotate({ + "description": "A JSON string of the output of the local shell tool call.\n" + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the item. One of `in_progress`, `completed`, or `incomplete`.\n" + }), + Schema.Null + ]) + ), + "call_id": Schema.Json.annotate({ "expected": "JSON value" }) + }).annotate({ "title": "Local shell call output", "description": "The output of a local shell tool call.\n" }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The unique ID of the shell tool call. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the shell tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("shell_call").annotate({ "description": "The type of the item. Always `shell_call`." }), + "action": Schema.suspend((): Schema.Codec => FunctionShellActionParam).annotate({ + "description": "The shell commands and limits that describe how to run the tool call." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => FunctionShellCallItemStatus).annotate({ + "description": "The status of the shell call. One of `in_progress`, `completed`, or `incomplete`." + }), + Schema.Null + ]) + ), + "environment": Schema.optionalKey( + Schema.Union([ + Schema.Union([LocalEnvironmentParam, ContainerReferenceParam], { mode: "oneOf" }).annotate({ + "description": "The environment to execute the shell commands in." + }), + Schema.Null + ]) + ) + }).annotate({ + "title": "Shell tool call", + "description": "A tool representing a request to execute one or more shell commands." + }), + Schema.Struct({ + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The unique ID of the shell tool call output. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the shell tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("shell_call_output").annotate({ + "description": "The type of the item. Always `shell_call_output`." + }), + "output": Schema.Array(FunctionShellCallOutputContentParam).annotate({ + "description": "Captured chunks of stdout and stderr output, along with their associated outcomes." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => FunctionShellCallItemStatus).annotate({ + "description": "The status of the shell call output." + }), + Schema.Null + ]) + ), + "max_output_length": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The maximum number of UTF-8 characters captured for this shell call's combined output." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) + }).annotate({ + "title": "Shell tool call output", + "description": "The streamed output items emitted by a shell tool call." + }), + Schema.Struct({ + "type": Schema.Literal("apply_patch_call").annotate({ + "description": "The type of the item. Always `apply_patch_call`." + }), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => ApplyPatchCallStatusParam).annotate({ + "description": "The status of the apply patch tool call. One of `in_progress` or `completed`." + }), + "operation": Schema.suspend((): Schema.Codec => ApplyPatchOperationParam).annotate({ + "description": "The specific create, delete, or update instruction for the apply_patch tool call." + }) + }).annotate({ + "title": "Apply patch tool call", + "description": "A tool call representing a request to create, delete, or update files using diff patches." + }), + Schema.Struct({ + "type": Schema.Literal("apply_patch_call_output").annotate({ + "description": "The type of the item. Always `apply_patch_call_output`." + }), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the apply patch tool call output. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => ApplyPatchCallOutputStatusParam) + .annotate({ "description": "The status of the apply patch tool call output. One of `completed` or `failed`." }), + "output": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "Optional human-readable log text from the apply patch tool (e.g., patch results or errors)." + }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Null + ]) + ) + }).annotate({ + "title": "Apply patch tool call output", + "description": "The streamed output emitted by an apply patch tool call." + }), + Schema.Struct({ + "type": Schema.Literal("mcp_list_tools").annotate({ + "description": "The type of the item. Always `mcp_list_tools`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the list.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server.\n" }), + "tools": Schema.Array(MCPListToolsTool).annotate({ "description": "The tools available on the server.\n" }), + "error": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Error message if the server could not list tools.\n" }), + Schema.Null + ]) + ) + }).annotate({ "title": "MCP list tools", "description": "A list of tools available on an MCP server.\n" }), + Schema.Struct({ + "type": Schema.Literal("mcp_approval_request").annotate({ + "description": "The type of the item. Always `mcp_approval_request`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the approval request.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server making the request.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of arguments for the tool.\n" }) + }).annotate({ + "title": "MCP approval request", + "description": "A request for human approval of a tool invocation.\n" + }), + Schema.Struct({ + "type": Schema.Literal("mcp_approval_response").annotate({ + "description": "The type of the item. Always `mcp_approval_response`.\n" + }), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the approval response\n" }), + Schema.Null + ]) + ), + "approval_request_id": Schema.String.annotate({ + "description": "The ID of the approval request being answered.\n" + }), + "approve": Schema.Boolean.annotate({ "description": "Whether the request was approved.\n" }), + "reason": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional reason for the decision.\n" }), Schema.Null]) + ), + "request_id": Schema.Json.annotate({ "expected": "JSON value" }) + }).annotate({ "title": "MCP approval response", "description": "A response to an MCP approval request.\n" }), + Schema.Struct({ + "type": Schema.Literal("mcp_call").annotate({ "description": "The type of the item. Always `mcp_call`.\n" }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool call.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server running the tool.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool that was run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments passed to the tool.\n" }), + "output": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The output from the tool call.\n" }), Schema.Null]) + ), + "error": Schema.optionalKey( + Schema.Union([MCPToolCallError, Schema.Null]).annotate({ + "description": "The error from the tool call, if any." + }) + ), + "status": Schema.optionalKey( + Schema.suspend((): Schema.Codec => MCPToolCallStatus).annotate({ + "description": + "The status of the tool call. One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`.\n" + }) + ), + "approval_request_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Unique identifier for the MCP tool call approval request.\nInclude this value in a subsequent `mcp_approval_response` input to approve or reject the corresponding tool call.\n" + }), + Schema.Null + ]) + ) + }).annotate({ "title": "MCP tool call", "description": "An invocation of a tool on an MCP server.\n" }), + Schema.Struct({ + "type": Schema.Literal("custom_tool_call_output").annotate({ + "description": "The type of the custom tool call output. Always `custom_tool_call_output`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The unique ID of the custom tool call output in the OpenAI platform.\n" + }) + ), + "call_id": Schema.String.annotate({ + "description": "The call ID, used to map this custom tool call output to a custom tool call.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the custom tool call.\n" + }), + Schema.Array(FunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the custom tool call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the custom tool call generated by your code.\nCan be a string or an list of output content.\n" + }) + }).annotate({ + "title": "Custom tool call output", + "description": "The output of a custom tool call from your code, being sent back to the model.\n" + }), + Schema.Struct({ + "type": Schema.Literal("custom_tool_call").annotate({ + "description": "The type of the custom tool call. Always `custom_tool_call`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the custom tool call in the OpenAI platform.\n" }) + ), + "call_id": Schema.String.annotate({ + "description": "An identifier used to map this custom tool call to a tool call output.\n" + }), + "caller": Schema.optionalKey(Schema.Union([ToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the custom tool being called.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the custom tool being called.\n" }), + "input": Schema.String.annotate({ + "description": "The input for the custom tool call generated by the model.\n" + }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the custom tool call runs asynchronously.\n" }) + ) + }).annotate({ "title": "Custom tool call", "description": "A call to a custom tool created by the model.\n" }) + ], { mode: "oneOf" }).annotate({ + "description": + "An item representing part of the context for the response to be\ngenerated by the model. Can contain text, images, and audio inputs,\nas well as previous assistant responses and tool call outputs.\n", + "title": "Item" + }), + CompactionTriggerItemParam, + ItemReferenceParam, + ProgramItemParam, + ProgramOutputItemParam +], { mode: "oneOf" }).annotate({ "identifier": "InputItem" }) +export type LiveResponsesDelegationParam = { + readonly "type": "responses" + readonly "responses": LiveResponsesDelegationSettingsInputParam +} +export const LiveResponsesDelegationParam = Schema.Struct({ + "type": Schema.Literal("responses").annotate({ + "description": "The delegation owner. Always `responses` for tasks handled by the Responses API." + }), + "responses": Schema.suspend((): Schema.Codec => + LiveResponsesDelegationSettingsInputParam + ).annotate({ + "description": "Backend model, prompt, and tools used when the Live session delegates a task to Responses." + }) +}).annotate({ + "description": "Delegate tasks to a Responses model managed by the Live session.", + "identifier": "LiveResponsesDelegationParam" +}) +export type LiveResponsesDelegationUpdateParam = { + readonly "type": "responses" + readonly "responses"?: LiveResponsesDelegationSettingsUpdateInputParam +} +export const LiveResponsesDelegationUpdateParam = Schema.Struct({ + "type": Schema.Literal("responses").annotate({ + "description": "The delegation owner. Always `responses` for tasks handled by the Responses API." + }), + "responses": Schema.optionalKey( + Schema.suspend((): Schema.Codec => + LiveResponsesDelegationSettingsUpdateInputParam + ).annotate({ "description": "Responses backend settings to update. Omitted settings keep their existing values." }) + ) +}).annotate({ + "description": "Update the Responses backend for an existing Live session without changing delegation ownership.", + "identifier": "LiveResponsesDelegationUpdateParam" +}) +export type ThreadItemListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ThreadItemListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(ThreadItem).annotate({ "description": "A list of items" }), + "first_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the first item in the list." }), + Schema.Null + ]), + "last_id": Schema.Union([ + Schema.String.annotate({ "description": "The ID of the last item in the list." }), + Schema.Null + ]), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }) +}).annotate({ + "title": "Thread Items", + "description": "A paginated list of thread items rendered for the ChatKit API.", + "identifier": "ThreadItemListResource" +}) +export type DecisionInput = string | ReadonlyArray +export const DecisionInput = Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array(Schema.Union([DecisionInputMessage], { mode: "oneOf" })).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(131072).annotate({ "expected": "a value with a length of at most 131072" })) +], { mode: "oneOf" }).annotate({ + "description": + "The text or images to evaluate for every question. Provide a text string or user messages containing text and images. Images can be base64 data URLs or publicly accessible HTTP(S) URLs; at most 128 images are allowed across all messages in one request. Files, audio, tools, and item references are not supported.", + "identifier": "DecisionInput" +}) +export type CreateEnvironmentParam = CreateEnvironmentParamOpenaiHosted +export const CreateEnvironmentParam = Schema.Union([CreateEnvironmentParamOpenaiHosted], { mode: "oneOf" }).annotate({ + "description": "Configuration for a new prewarmed OpenAI-hosted environment.", + "identifier": "CreateEnvironmentParam" +}) +export type EnvironmentParam = EnvironmentParamNone | EnvironmentParamOpenaiHosted | EnvironmentParamSelfHosted +export const EnvironmentParam = Schema.Union([ + EnvironmentParamNone, + EnvironmentParamOpenaiHosted, + EnvironmentParamSelfHosted +], { mode: "oneOf" }).annotate({ + "description": "The execution environment and optional reusable template for a session.", + "identifier": "EnvironmentParam" +}) +export type SessionItemListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SessionItemListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(SessionTurnItemResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of messages, reasoning, and tool calls from a session's item history.", + "identifier": "SessionItemListResource" +}) +export type SessionEventAgentSessionTurnItemAdded = { + readonly "type": "agent.session.turn.item.added" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "output_index": number | null + readonly "item": SessionTurnItemResource +} +export const SessionEventAgentSessionTurnItemAdded = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.item.added").annotate({ + "description": "The type of the object. Always `agent.session.turn.item.added`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "output_index": Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check( + Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" }) + ), + Schema.Null + ]).annotate({ + "description": "The index of the item in the turn output, when the item is agent output.", + "format": "int64" + }), + "item": Schema.suspend((): Schema.Codec => SessionTurnItemResource).annotate({ + "description": "The item that was added." + }) +}).annotate({ + "description": "Emitted when an item is added to a turn.", + "identifier": "SessionEventAgentSessionTurnItemAdded" +}) +export type SessionEventAgentSessionTurnItemDone = { + readonly "type": "agent.session.turn.item.done" + readonly "event_id": string + readonly "session_id": string + readonly "turn_id": string | null + readonly "output_index": number + readonly "item": AgentOutputItemResource +} +export const SessionEventAgentSessionTurnItemDone = Schema.Struct({ + "type": Schema.Literal("agent.session.turn.item.done").annotate({ + "description": "The type of the object. Always `agent.session.turn.item.done`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session_id": Schema.String.annotate({ "description": "The ID of the session associated with the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "turn_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the turn associated with the event, when applicable." }), + "output_index": Schema.Number.annotate({ + "description": "The index of the output item in the turn output.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(4294967295).annotate({ "expected": "a value less than or equal to 4294967295" })), + "item": Schema.suspend((): Schema.Codec => AgentOutputItemResource).annotate({ + "description": "The completed output item." + }) +}).annotate({ + "description": "Emitted when an output item is complete.", + "identifier": "SessionEventAgentSessionTurnItemDone" +}) +export type AgentListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const AgentListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(AgentResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "AgentListResource" +}) +export type SessionResource = { + readonly "spend_control"?: SessionSpendControlResource + readonly "metadata": { readonly [x: string]: string } + readonly "id": string + readonly "object": "agent.session" + readonly "created_at": number + readonly "last_active_at": number + readonly "status": SessionStatusResource + readonly "required_actions": ReadonlyArray + readonly "error": string | null + readonly "agent": SessionAgentResource + readonly "environment": EnvironmentResource + readonly "vault_ids": ReadonlyArray + readonly "usage": TokenUsageResource | null +} +export const SessionResource = Schema.Struct({ + "spend_control": Schema.optionalKey( + Schema.suspend((): Schema.Codec => SessionSpendControlResource).annotate({ + "description": + "Configured spending limit and best-effort consumption, in USD cents. Unlimited sessions omit this object." + }) + ), + "metadata": Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Custom string key-value pairs attached to the session." }).check( + Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "id": Schema.String.annotate({ "description": "The ID of the session." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("agent.session").annotate({ "description": "The object type. Always `agent.session`." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the session was created.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "last_active_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the session was last active.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "status": Schema.suspend((): Schema.Codec => SessionStatusResource).annotate({ + "description": "The current status of the session." + }), + "required_actions": Schema.Array(SessionRequiredActionResource).annotate({ + "description": "Actions that must be completed before the session can continue." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "error": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The error that caused the session to fail, if any." }), + "agent": Schema.suspend((): Schema.Codec => SessionAgentResource).annotate({ + "description": "The agent running in the session." + }), + "environment": Schema.suspend((): Schema.Codec => EnvironmentResource).annotate({ + "description": "The execution environment for the session." + }), + "vault_ids": Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "The IDs of vaults made available to the session." }).check( + Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" }) + ).check(Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" })), + "usage": Schema.Union([TokenUsageResource, Schema.Null]).annotate({ + "description": "Best-effort token usage for the session, or null if unknown. Recorded usage may change." + }) +}).annotate({ "description": "A Managed Agents session.", "identifier": "SessionResource" }) +export type CreateSessionEventsParams = { readonly "events": ReadonlyArray } +export const CreateSessionEventsParams = Schema.Struct({ + "events": Schema.Array(SessionInputParam).annotate({ "description": "The input events to submit to the session." }) + .check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ) +}).annotate({ + "description": "Input events submitted to an existing session.", + "identifier": "CreateSessionEventsParams" +}) +export type VaultCredentialResource = { + readonly "id": string + readonly "object": "vault.credential" + readonly "vault_id": string + readonly "name": string + readonly "auth": VaultCredentialAuthResource + readonly "metadata": { readonly [x: string]: string } + readonly "created_at": number + readonly "updated_at": number +} +export const VaultCredentialResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The ID of the credential." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "object": Schema.Literal("vault.credential").annotate({ + "description": "The object type. Always `vault.credential`." + }), + "vault_id": Schema.String.annotate({ "description": "The ID of the vault containing this credential." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "name": Schema.String.annotate({ "description": "The human-readable name of the credential." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "auth": Schema.suspend((): Schema.Codec => VaultCredentialAuthResource).annotate({ + "description": "The authentication method and non-secret configuration of the credential." + }), + "metadata": Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "description": "Application-defined key-value pairs associated with this credential." }).check( + Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the credential was created.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "updated_at": Schema.Number.annotate({ + "description": "The Unix timestamp, in seconds, when the credential was last updated.", + "format": "int64" + }).check(Schema.isInt().annotate({ "expected": "an integer" })) +}).annotate({ + "description": "Metadata for a stored credential. Secret values are never returned.", + "identifier": "VaultCredentialResource" +}) +export type CreateVaultCredentialParams = { + readonly "name": string + readonly "auth": CreateVaultCredentialAuthParam + readonly "metadata"?: { readonly [x: string]: string } +} +export const CreateVaultCredentialParams = Schema.Struct({ + "name": Schema.String.annotate({ + "description": "The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after trimming." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" }) + ), + "auth": Schema.suspend((): Schema.Codec => CreateVaultCredentialAuthParam).annotate({ + "description": "The authentication method and write-only secret values to store." + }), + "metadata": Schema.optionalKey( + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).annotate({ + "description": + "Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters. Defaults to an empty map." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) + ) +}).annotate({ + "description": "Parameters for storing a credential for an MCP server or an OpenAI-hosted environment.", + "identifier": "CreateVaultCredentialParams" +}) +export type RotateVaultCredentialParams = { + readonly "auth"?: RotateVaultCredentialAuthParam + readonly "metadata"?: { readonly [x: string]: string } +} +export const RotateVaultCredentialParams = Schema.Struct({ + "auth": Schema.optionalKey( + Schema.suspend((): Schema.Codec => RotateVaultCredentialAuthParam).annotate({ + "description": "Replacement values for the credential's existing authentication method." + }) + ), + "metadata": Schema.optionalKey( + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).annotate({ + "description": + "Replaces all metadata. Omit to preserve it, or pass {} to clear it. Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters." + }).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) + ) +}).annotate({ + "description": + "Metadata, secret, expiry, and OAuth refresh scope updates for an existing vault credential. Supply at least one of `auth` or `metadata`." +}).check( + Schema.isMinProperties(1).annotate({ + "expected": "a value with at least 1 entry", + "identifier": "RotateVaultCredentialParams" + }) +) +export type BetaToolsArray = ReadonlyArray +export const BetaToolsArray = Schema.Array(BetaTool).annotate({ + "description": + "An array of tools the model may call while generating a response. You\ncan specify which tool to use by setting the `tool_choice` parameter.\n\nWe support the following categories of tools:\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)\n or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about\n [built-in tools](https://developers.openai.com/api/docs/guides/tools).\n- **MCP Tools**: Integrations with third-party systems via custom MCP servers\n or predefined connectors such as Google Drive and SharePoint. Learn more about\n [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code with strongly typed arguments\n and outputs. Learn more about\n [function calling](https://developers.openai.com/api/docs/guides/function-calling). You can also use\n custom tools to call your own code.\n", + "identifier": "BetaToolsArray" +}) +export type BetaToolSearchOutput = { + readonly "agent"?: BetaAgentTag + readonly "type": "tool_search_output" + readonly "id": string + readonly "call_id": string | null + readonly "execution": BetaToolSearchExecutionType + readonly "tools": ReadonlyArray + readonly "status": BetaFunctionCallOutputStatusEnum + readonly "created_by"?: string +} +export const BetaToolSearchOutput = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("tool_search_output").annotate({ + "description": "The type of the item. Always `tool_search_output`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool search output item." }), + "call_id": Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }), + Schema.Null + ]), + "execution": Schema.suspend((): Schema.Codec => BetaToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }), + "tools": Schema.Array(BetaTool).annotate({ "description": "The loaded tool definitions returned by tool search." }), + "status": Schema.suspend((): Schema.Codec => BetaFunctionCallOutputStatusEnum) + .annotate({ "description": "The status of the tool search output item that was recorded." }), + "created_by": Schema.optionalKey( + Schema.String.annotate({ "description": "The identifier of the actor that created the item." }) + ) +}).annotate({ "identifier": "BetaToolSearchOutput" }) +export type BetaAdditionalTools = { + readonly "agent"?: BetaAgentTag + readonly "type": "additional_tools" + readonly "id": string + readonly "role": BetaMessageRole + readonly "tools": ReadonlyArray +} +export const BetaAdditionalTools = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }) + ), + "type": Schema.Literal("additional_tools").annotate({ + "description": "The type of the item. Always `additional_tools`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the additional tools item." }), + "role": Schema.suspend((): Schema.Codec => BetaMessageRole).annotate({ + "description": "The role that provided the additional tools." + }), + "tools": Schema.Array(BetaTool).annotate({ + "description": "The additional tool definitions made available at this item." + }) +}).annotate({ "identifier": "BetaAdditionalTools" }) +export type BetaInputItem = + | BetaEasyInputMessage + | { + readonly "agent"?: BetaAgentTag | null + readonly "type"?: "message" + readonly "role": "user" | "system" | "developer" + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "content": BetaInputMessageContentList + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "message" + readonly "role": "assistant" + readonly "content": ReadonlyArray + readonly "phase"?: BetaMessagePhase | null + readonly "status": "in_progress" | "completed" | "incomplete" + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "file_search_call" + readonly "status": "in_progress" | "searching" | "completed" | "incomplete" | "failed" + readonly "queries": ReadonlyArray + readonly "results"?: + | ReadonlyArray< + { + readonly "file_id"?: string + readonly "text"?: string + readonly "filename"?: string + readonly "attributes"?: BetaVectorStoreFileAttributes + readonly "score"?: number + } + > + | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "computer_call" + readonly "id": string + readonly "call_id": string + readonly "action"?: BetaComputerAction + readonly "actions"?: BetaComputerActionList + readonly "pending_safety_checks": ReadonlyArray + readonly "status": "in_progress" | "completed" | "incomplete" + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id": string + readonly "type": "computer_call_output" + readonly "output": BetaComputerScreenshotImage + readonly "acknowledged_safety_checks"?: ReadonlyArray | null + readonly "status"?: BetaFunctionCallItemStatus | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "id": string + readonly "type": "web_search_call" + readonly "status": BetaWebSearchCallStatus + readonly "action"?: BetaWebSearchActionSearch | BetaWebSearchActionOpenPage | BetaWebSearchActionFind + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "id"?: string + readonly "type": "function_call" + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "arguments": string + readonly "status"?: "in_progress" | "completed" | "incomplete" + readonly "async"?: boolean + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id"?: string | null + readonly "type": "function_call_output" + readonly "output": + | string + | ReadonlyArray + readonly "name"?: string | null + readonly "namespace"?: string | null + readonly "caller"?: BetaToolCallCallerParam | null + readonly "status"?: BetaFunctionCallItemStatus | null + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "type": "agent_message" + readonly "author": string + readonly "recipient": string + readonly "content": ReadonlyArray< + BetaInputTextContentParam | BetaInputImageContentParamAutoParam | BetaEncryptedContentParam + > + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id": string + readonly "type": "multi_agent_call" + readonly "action": BetaMultiAgentAction1 + readonly "arguments": string + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id": string + readonly "type": "multi_agent_call_output" + readonly "action": BetaMultiAgentAction1 + readonly "output": ReadonlyArray + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id"?: string | null + readonly "type": "tool_search_call" + readonly "execution"?: BetaToolSearchExecutionType + readonly "arguments": BetaEmptyModelParam + readonly "status"?: BetaFunctionCallItemStatus | null + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id"?: string | null + readonly "type": "tool_search_output" + readonly "execution"?: BetaToolSearchExecutionType + readonly "tools": ReadonlyArray + readonly "status"?: BetaFunctionCallItemStatus | null + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "type": "additional_tools" + readonly "role": "developer" + readonly "tools": ReadonlyArray + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "type": "configuration_update" + readonly "reasoning"?: { readonly "effort"?: BetaReasoningEffort } + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "reasoning" + readonly "id": string + readonly "encrypted_content"?: string | null + readonly "summary": ReadonlyArray + readonly "content"?: ReadonlyArray + readonly "status"?: "in_progress" | "completed" | "incomplete" + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "type": "compaction" + readonly "encrypted_content": string + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "image_generation_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "generating" | "failed" + readonly "result": string | null + readonly "size"?: string | "1024x1024" | "1024x1536" | "1536x1024" | null + readonly "quality"?: "low" | "medium" | "high" | "xhigh" | "max" | "auto" | null + readonly "action"?: BetaImageGenActionEnum | null + readonly "background"?: BetaImageBackground | null + readonly "output_format"?: BetaImageOutputFormat | null + readonly "revised_prompt"?: string | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "code_interpreter_call" + readonly "id": string + readonly "status": "in_progress" | "completed" | "incomplete" | "interpreting" | "failed" + readonly "container_id": string + readonly "code": string | null + readonly "outputs": ReadonlyArray | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "local_shell_call" + readonly "id": string + readonly "call_id": string + readonly "action": BetaLocalShellExecAction + readonly "status": "in_progress" | "completed" | "incomplete" + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "local_shell_call_output" + readonly "id": string + readonly "output": string + readonly "status"?: "in_progress" | "completed" | "incomplete" | null + readonly "call_id": Schema.Json + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "type": "shell_call" + readonly "action": BetaFunctionShellActionParam + readonly "status"?: BetaFunctionShellCallItemStatus | null + readonly "environment"?: BetaLocalEnvironmentParam | BetaContainerReferenceParam | null + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "type": "shell_call_output" + readonly "output": ReadonlyArray + readonly "status"?: BetaFunctionShellCallItemStatus | null + readonly "max_output_length"?: number | null + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "type": "apply_patch_call" + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "status": BetaApplyPatchCallStatusParam + readonly "operation": BetaApplyPatchOperationParam + } + | { + readonly "agent"?: Beta_AgentTagParam | null + readonly "type": "apply_patch_call_output" + readonly "id"?: string | null + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "status": BetaApplyPatchCallOutputStatusParam + readonly "output"?: string | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_list_tools" + readonly "id": string + readonly "server_label": string + readonly "tools": ReadonlyArray + readonly "error"?: string | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_approval_request" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_approval_response" + readonly "id"?: string | null + readonly "approval_request_id": string + readonly "approve": boolean + readonly "reason"?: string | null + readonly "request_id": Schema.Json + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "mcp_call" + readonly "id": string + readonly "server_label": string + readonly "name": string + readonly "arguments": string + readonly "output"?: string | null + readonly "error"?: BetaMCPToolCallError | null + readonly "status"?: BetaMCPToolCallStatus + readonly "approval_request_id"?: string | null + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "custom_tool_call_output" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: BetaToolCallCallerParam | null + readonly "output": string | ReadonlyArray + } + | { + readonly "agent"?: BetaAgentTag | null + readonly "type": "custom_tool_call" + readonly "id"?: string + readonly "call_id": string + readonly "caller"?: BetaToolCallCaller | null + readonly "namespace"?: string + readonly "name": string + readonly "input": string + readonly "async"?: boolean + } + | BetaCompactionTriggerItemParam + | BetaItemReferenceParam + | BetaProgramItemParam + | BetaProgramOutputItemParam +export const BetaInputItem = Schema.Union([ + BetaEasyInputMessage, + Schema.Union([ + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.optionalKey( + Schema.Literal("message").annotate({ + "description": "The type of the message input. Always set to `message`.\n" + }) + ), + "role": Schema.Literals(["user", "system", "developer"]).annotate({ + "description": "The role of the message input. One of `user`, `system`, or `developer`.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "content": BetaInputMessageContentList + }).annotate({ + "title": "Input message", + "description": + "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the output message.\n" }), + "type": Schema.Literal("message").annotate({ + "description": "The type of the output message. Always `message`.\n" + }), + "role": Schema.Literal("assistant").annotate({ + "description": "The role of the output message. Always `assistant`.\n" + }), + "content": Schema.Array(BetaOutputMessageContent).annotate({ + "description": "The content of the output message.\n" + }), + "phase": Schema.optionalKey(Schema.Union([BetaMessagePhase, Schema.Null])), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + }) + }).annotate({ "title": "Output message", "description": "An output message from the model.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the file search tool call.\n" }), + "type": Schema.Literal("file_search_call").annotate({ + "description": "The type of the file search tool call. Always `file_search_call`.\n" + }), + "status": Schema.Literals(["in_progress", "searching", "completed", "incomplete", "failed"]).annotate({ + "description": + "The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n" + }), + "queries": Schema.Array(Schema.String).annotate({ "description": "The queries used to search for files.\n" }), + "results": Schema.optionalKey(Schema.Union([ + Schema.Array(Schema.Struct({ + "file_id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the file.\n" })), + "text": Schema.optionalKey( + Schema.String.annotate({ "description": "The text that was retrieved from the file.\n" }) + ), + "filename": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the file.\n" })), + "attributes": Schema.optionalKey(BetaVectorStoreFileAttributes), + "score": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The relevance score of the file - a value between 0 and 1.\n", + "format": "float" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ) + })).annotate({ "description": "The results of the file search tool call.\n" }), + Schema.Null + ])) + }).annotate({ + "title": "File search tool call", + "description": + "The results of a file search tool call. See the\n[file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("computer_call").annotate({ + "description": "The type of the computer call. Always `computer_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the computer call." }), + "call_id": Schema.String.annotate({ + "description": "An identifier used when responding to the tool call with output.\n" + }), + "action": Schema.optionalKey(BetaComputerAction), + "actions": Schema.optionalKey(BetaComputerActionList), + "pending_safety_checks": Schema.Array(BetaComputerCallSafetyCheckParam).annotate({ + "description": "The pending safety checks for the computer call.\n" + }), + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + }).annotate({ + "title": "Computer tool call", + "description": + "A tool call to a computer use tool. See the\n[computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The ID of the computer tool call output." }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ "description": "The ID of the computer tool call that produced the output." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "type": Schema.Literal("computer_call_output").annotate({ + "description": "The type of the computer tool call output. Always `computer_call_output`." + }), + "output": BetaComputerScreenshotImage, + "acknowledged_safety_checks": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaComputerCallSafetyCheckParam).annotate({ + "description": "The safety checks reported by the API that have been acknowledged by the developer." + }), + Schema.Null + ]) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFunctionCallItemStatus).annotate({ + "description": + "The status of the message input. One of `in_progress`, `completed`, or `incomplete`. Populated when input items are returned via API." + }), + Schema.Null + ]) + ) + }).annotate({ "title": "Computer tool call output", "description": "The output of a computer tool call." }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.String.annotate({ "description": "The unique ID of the web search tool call.\n" }), + "type": Schema.Literal("web_search_call").annotate({ + "description": "The type of the web search tool call. Always `web_search_call`.\n" + }), + "status": Schema.suspend((): Schema.Codec => BetaWebSearchCallStatus).annotate({ + "description": "The status of the web search tool call.\n" + }), + "action": Schema.optionalKey( + Schema.Union([BetaWebSearchActionSearch, BetaWebSearchActionOpenPage, BetaWebSearchActionFind], { + mode: "oneOf" + }).annotate({ + "description": + "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find_in_page).\n" + }) + ) + }).annotate({ + "title": "Web search tool call", + "description": + "The results of a web search tool call. See the\n[web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "id": Schema.optionalKey(Schema.String.annotate({ "description": "The unique ID of the function tool call.\n" })), + "type": Schema.Literal("function_call").annotate({ + "description": "The type of the function tool call. Always `function_call`.\n" + }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the function tool call generated by the model.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the function to run.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the function to run.\n" }), + "arguments": Schema.String.annotate({ + "description": "A JSON string of the arguments to pass to the function.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the function tool call runs asynchronously.\n" }) + ) + }).annotate({ + "title": "Function tool call", + "description": + "A tool call to run a function. See the\n[function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the function tool call output. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the function tool call generated by the model." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "type": Schema.Literal("function_call_output").annotate({ + "description": "The type of the function tool call output. Always `function_call_output`." + }), + "output": Schema.Union([ + Schema.String.annotate({ "description": "A JSON string of the output of the function tool call." }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array( + Schema.Union([BetaInputTextContentParam, BetaInputImageContentParamAutoParam, BetaInputFileContentParam], { + mode: "oneOf" + }).annotate({ "description": "A piece of message content, such as text, an image, or a file." }) + ).annotate({ "description": "An array of content outputs (text, image, file) for the function tool call." }) + ], { mode: "oneOf" }).annotate({ "description": "Text, image, or file output of the function tool call." }), + "name": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The name of the tool that produced the output." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ]) + ), + "namespace": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The namespace of the tool that produced the output." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })).check( + Schema.isPattern(new RegExp("^[a-zA-Z0-9_-]+$", "u")).annotate({ + "expected": "a string matching the RegExp ^[a-zA-Z0-9_-]+$" + }) + ), + Schema.Null + ]) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFunctionCallItemStatus).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API." + }), + Schema.Null + ]) + ) + }).annotate({ "title": "Function tool call output", "description": "The output of a function tool call." }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this agent message item." }), + Schema.Null + ]) + ), + "type": Schema.Literal("agent_message").annotate({ "description": "The item type. Always `agent_message`." }), + "author": Schema.String.annotate({ "description": "The sending agent identity." }), + "recipient": Schema.String.annotate({ "description": "The destination agent identity." }), + "content": Schema.Array( + Schema.Union([BetaInputTextContentParam, BetaInputImageContentParamAutoParam, BetaEncryptedContentParam], { + mode: "oneOf" + }).annotate({ "description": "A plaintext, image, or encrypted agent message content part." }) + ).annotate({ "description": "Plaintext, image, or encrypted content sent between agents." }) + }).annotate({ "title": "Agent message", "description": "A message routed between agents." }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this multi-agent call." }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ "description": "The unique ID linking this call to its output." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "type": Schema.Literal("multi_agent_call").annotate({ + "description": "The item type. Always `multi_agent_call`." + }), + "action": Schema.suspend((): Schema.Codec => BetaMultiAgentAction1).annotate({ + "description": "The multi-agent action that was executed." + }), + "arguments": Schema.String.annotate({ "description": "The action arguments as a JSON string." }) + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this multi-agent call output." }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ "description": "The unique ID of the multi-agent call." }).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })), + "type": Schema.Literal("multi_agent_call_output").annotate({ + "description": "The item type. Always `multi_agent_call_output`." + }), + "action": Schema.suspend((): Schema.Codec => BetaMultiAgentAction1).annotate({ + "description": "The multi-agent action that produced this result." + }), + "output": Schema.Array(BetaOutputTextContentParam).annotate({ + "description": "Text output returned by the multi-agent action." + }) + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this tool search call." }), + Schema.Null + ]) + ), + "call_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "type": Schema.Literal("tool_search_call").annotate({ + "description": "The item type. Always `tool_search_call`." + }), + "execution": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }) + ), + "arguments": Schema.suspend((): Schema.Codec => BetaEmptyModelParam).annotate({ + "description": "The arguments supplied to the tool search call." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFunctionCallItemStatus).annotate({ + "description": "The status of the tool search call." + }), + Schema.Null + ]) + ) + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this tool search output." }), + Schema.Null + ]) + ), + "call_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the tool search call generated by the model." }) + .check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "type": Schema.Literal("tool_search_output").annotate({ + "description": "The item type. Always `tool_search_output`." + }), + "execution": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaToolSearchExecutionType).annotate({ + "description": "Whether tool search was executed by the server or by the client." + }) + ), + "tools": Schema.Array(BetaToolSearchOutputTool).annotate({ + "description": "The loaded tool definitions returned by the tool search output." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFunctionCallItemStatus).annotate({ + "description": "The status of the tool search output." + }), + Schema.Null + ]) + ) + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of this additional tools item." }), + Schema.Null + ]) + ), + "type": Schema.Literal("additional_tools").annotate({ + "description": "The item type. Always `additional_tools`." + }), + "role": Schema.Literal("developer").annotate({ + "description": "The role that provided the additional tools. Only `developer` is supported." + }), + "tools": Schema.Array(BetaTool).annotate({ + "description": "A list of additional tools made available at this item." + }) + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the configuration update item." }), + Schema.Null + ]) + ), + "type": Schema.Literal("configuration_update").annotate({ + "description": "The item type. Always `configuration_update`." + }), + "reasoning": Schema.optionalKey( + Schema.Struct({ + "effort": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaReasoningEffort).annotate({ + "description": + "The reasoning effort to use for subsequent responses until another\nconfiguration update replaces it.\n" + }) + ) + }).annotate({ "description": "Updates to reasoning configuration. Only effort is supported." }) + ) + }).annotate({ + "description": + "An update to the conversation's response configuration. The configuration\nremains in effect for subsequent responses until it is replaced by another\nconfiguration update.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("reasoning").annotate({ "description": "The type of the object. Always `reasoning`.\n" }), + "id": Schema.String.annotate({ "description": "The unique identifier of the reasoning content.\n" }), + "encrypted_content": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The encrypted content of the reasoning item. This is populated by default\nfor reasoning items returned by `POST /v1/responses` and WebSocket\n`response.create` requests.\n\nWhen streaming, use the completed reasoning item and its\n`encrypted_content` from the `response.output_item.done` event in\nsubsequent requests. The `encrypted_content` in\n`response.output_item.added` may be incomplete. This is especially\nimportant when `store` is `false` or when using Zero Data Retention.\n" + }), + Schema.Null + ])), + "summary": Schema.Array(BetaSummaryTextContent).annotate({ "description": "Reasoning summary content.\n" }), + "content": Schema.optionalKey( + Schema.Array(BetaReasoningTextContent).annotate({ "description": "Reasoning text content.\n" }) + ), + "status": Schema.optionalKey( + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": + "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + }) + ) + }).annotate({ + "title": "Reasoning", + "description": + "A description of the chain of thought used by a reasoning model while generating\na response. Be sure to include these items in your `input` to the Responses API\nfor subsequent turns of a conversation if you are manually\n[managing context](https://developers.openai.com/api/docs/guides/conversation-state).\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The ID of the compaction item." }), Schema.Null]) + ), + "type": Schema.Literal("compaction").annotate({ "description": "The type of the item. Always `compaction`." }), + "encrypted_content": Schema.String.annotate({ "description": "The encrypted content of the compaction summary." }) + .check( + Schema.isMaxCodePoints(104857600).annotate({ "expected": "a string with at most 104857600 code points" }) + ) + }).annotate({ + "title": "Compaction item", + "description": + "A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact)." + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaAgentTag).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("image_generation_call").annotate({ + "description": "The type of the image generation call. Always `image_generation_call`." + }), + "id": Schema.String.annotate({ "description": "The unique ID of the image generation call." }), + "status": Schema.Literals(["in_progress", "completed", "generating", "failed"]).annotate({ + "description": "The status of the image generation call." + }), + "result": Schema.Union([ + Schema.String.annotate({ "description": "The generated image encoded in base64." }), + Schema.Null + ]), + "size": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.String, Schema.Literals(["1024x1024", "1024x1536", "1536x1024"])]).annotate({ + "description": "The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`." + }), + Schema.Null + ]) + ), + "quality": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["low", "medium", "high", "xhigh", "max", "auto"]).annotate({ + "description": + "The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`." + }), + Schema.Null + ]) + ), + "action": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaImageGenActionEnum).annotate({ + "description": "The action used for image generation." + }), + Schema.Null + ]) + ), + "background": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaImageBackground).annotate({ + "description": "The background setting used for generation." + }), + Schema.Null + ]) + ), + "output_format": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaImageOutputFormat).annotate({ + "description": "The output format used for generation." + }), + Schema.Null + ]) + ), + "revised_prompt": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The prompt that was used after any model prompt rewriting." }), + Schema.Null + ]) + ) + }).annotate({ "title": "Image generation call", "description": "An image generation request made by the model." }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("code_interpreter_call").annotate({ + "description": "The type of the code interpreter tool call. Always `code_interpreter_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the code interpreter tool call.\n" }), + "status": Schema.Literals(["in_progress", "completed", "incomplete", "interpreting", "failed"]).annotate({ + "description": + "The status of the code interpreter tool call. Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and `failed`.\n" + }), + "container_id": Schema.String.annotate({ "description": "The ID of the container used to run the code.\n" }), + "code": Schema.Union([ + Schema.String.annotate({ "description": "The code to run, or null if not available.\n" }), + Schema.Null + ]), + "outputs": Schema.Union([ + Schema.Array(Schema.Union([BetaCodeInterpreterOutputLogs, BetaCodeInterpreterOutputImage], { mode: "oneOf" })) + .annotate({ + "description": + "The outputs generated by the code interpreter, such as logs or images.\nCan be null if no outputs are available.\n" + }), + Schema.Null + ]) + }).annotate({ "title": "Code interpreter tool call", "description": "A tool call to run code.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("local_shell_call").annotate({ + "description": "The type of the local shell call. Always `local_shell_call`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the local shell call.\n" }), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "action": BetaLocalShellExecAction, + "status": Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the local shell call.\n" + }) + }).annotate({ "title": "Local shell call", "description": "A tool call to run a command on the local shell.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("local_shell_call_output").annotate({ + "description": "The type of the local shell tool call output. Always `local_shell_call_output`.\n" + }), + "id": Schema.String.annotate({ + "description": "The unique ID of the local shell tool call generated by the model.\n" + }), + "output": Schema.String.annotate({ + "description": "A JSON string of the output of the local shell tool call.\n" + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.Literals(["in_progress", "completed", "incomplete"]).annotate({ + "description": "The status of the item. One of `in_progress`, `completed`, or `incomplete`.\n" + }), + Schema.Null + ]) + ), + "call_id": Schema.Json.annotate({ "expected": "JSON value" }) + }).annotate({ "title": "Local shell call output", "description": "The output of a local shell tool call.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The unique ID of the shell tool call. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the shell tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("shell_call").annotate({ "description": "The type of the item. Always `shell_call`." }), + "action": Schema.suspend((): Schema.Codec => BetaFunctionShellActionParam).annotate( + { "description": "The shell commands and limits that describe how to run the tool call." } + ), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFunctionShellCallItemStatus).annotate( + { "description": "The status of the shell call. One of `in_progress`, `completed`, or `incomplete`." } + ), + Schema.Null + ]) + ), + "environment": Schema.optionalKey( + Schema.Union([ + Schema.Union([BetaLocalEnvironmentParam, BetaContainerReferenceParam], { mode: "oneOf" }).annotate({ + "description": "The environment to execute the shell commands in." + }), + Schema.Null + ]) + ) + }).annotate({ + "title": "Shell tool call", + "description": "A tool representing a request to execute one or more shell commands." + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The unique ID of the shell tool call output. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the shell tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("shell_call_output").annotate({ + "description": "The type of the item. Always `shell_call_output`." + }), + "output": Schema.Array(BetaFunctionShellCallOutputContentParam).annotate({ + "description": "Captured chunks of stdout and stderr output, along with their associated outcomes." + }), + "status": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaFunctionShellCallItemStatus).annotate( + { "description": "The status of the shell call output." } + ), + Schema.Null + ]) + ), + "max_output_length": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The maximum number of UTF-8 characters captured for this shell call's combined output." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) + }).annotate({ + "title": "Shell tool call output", + "description": "The streamed output items emitted by a shell tool call." + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("apply_patch_call").annotate({ + "description": "The type of the item. Always `apply_patch_call`." + }), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => BetaApplyPatchCallStatusParam) + .annotate({ "description": "The status of the apply patch tool call. One of `in_progress` or `completed`." }), + "operation": Schema.suspend((): Schema.Codec => BetaApplyPatchOperationParam) + .annotate({ + "description": "The specific create, delete, or update instruction for the apply_patch tool call." + }) + }).annotate({ + "title": "Apply patch tool call", + "description": "A tool call representing a request to create, delete, or update files using diff patches." + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Beta_AgentTagParam).annotate({ + "description": "The agent that produced this item." + }), + Schema.Null + ]) + ), + "type": Schema.Literal("apply_patch_call_output").annotate({ + "description": "The type of the item. Always `apply_patch_call_output`." + }), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the apply patch tool call output. Populated when this item is returned via API." + }), + Schema.Null + ]) + ), + "call_id": Schema.String.annotate({ + "description": "The unique ID of the apply patch tool call generated by the model." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + "caller": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolCallCallerParam).annotate({ + "description": "The execution context that produced this tool call." + }), + Schema.Null + ]) + ), + "status": Schema.suspend((): Schema.Codec => + BetaApplyPatchCallOutputStatusParam + ).annotate({ "description": "The status of the apply patch tool call output. One of `completed` or `failed`." }), + "output": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": "Optional human-readable log text from the apply patch tool (e.g., patch results or errors)." + }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Null + ]) + ) + }).annotate({ + "title": "Apply patch tool call output", + "description": "The streamed output emitted by an apply patch tool call." + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_list_tools").annotate({ + "description": "The type of the item. Always `mcp_list_tools`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the list.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server.\n" }), + "tools": Schema.Array(BetaMCPListToolsTool).annotate({ "description": "The tools available on the server.\n" }), + "error": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Error message if the server could not list tools.\n" }), + Schema.Null + ]) + ) + }).annotate({ "title": "MCP list tools", "description": "A list of tools available on an MCP server.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_approval_request").annotate({ + "description": "The type of the item. Always `mcp_approval_request`.\n" + }), + "id": Schema.String.annotate({ "description": "The unique ID of the approval request.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server making the request.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool to run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of arguments for the tool.\n" }) + }).annotate({ + "title": "MCP approval request", + "description": "A request for human approval of a tool invocation.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_approval_response").annotate({ + "description": "The type of the item. Always `mcp_approval_response`.\n" + }), + "id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "The unique ID of the approval response\n" }), + Schema.Null + ]) + ), + "approval_request_id": Schema.String.annotate({ + "description": "The ID of the approval request being answered.\n" + }), + "approve": Schema.Boolean.annotate({ "description": "Whether the request was approved.\n" }), + "reason": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "Optional reason for the decision.\n" }), Schema.Null]) + ), + "request_id": Schema.Json.annotate({ "expected": "JSON value" }) + }).annotate({ "title": "MCP approval response", "description": "A response to an MCP approval request.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("mcp_call").annotate({ "description": "The type of the item. Always `mcp_call`.\n" }), + "id": Schema.String.annotate({ "description": "The unique ID of the tool call.\n" }), + "server_label": Schema.String.annotate({ "description": "The label of the MCP server running the tool.\n" }), + "name": Schema.String.annotate({ "description": "The name of the tool that was run.\n" }), + "arguments": Schema.String.annotate({ "description": "A JSON string of the arguments passed to the tool.\n" }), + "output": Schema.optionalKey( + Schema.Union([Schema.String.annotate({ "description": "The output from the tool call.\n" }), Schema.Null]) + ), + "error": Schema.optionalKey( + Schema.Union([BetaMCPToolCallError, Schema.Null]).annotate({ + "description": "The error from the tool call, if any." + }) + ), + "status": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaMCPToolCallStatus).annotate({ + "description": + "The status of the tool call. One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`.\n" + }) + ), + "approval_request_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Unique identifier for the MCP tool call approval request.\nInclude this value in a subsequent `mcp_approval_response` input to approve or reject the corresponding tool call.\n" + }), + Schema.Null + ]) + ) + }).annotate({ "title": "MCP tool call", "description": "An invocation of a tool on an MCP server.\n" }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("custom_tool_call_output").annotate({ + "description": "The type of the custom tool call output. Always `custom_tool_call_output`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The unique ID of the custom tool call output in the OpenAI platform.\n" + }) + ), + "call_id": Schema.String.annotate({ + "description": "The call ID, used to map this custom tool call output to a custom tool call.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCallerParam, Schema.Null])), + "output": Schema.Union([ + Schema.String.annotate({ + "title": "string output", + "description": "A string of the output of the custom tool call.\n" + }), + Schema.Array(BetaFunctionAndCustomToolCallOutput).annotate({ + "title": "output content list", + "description": "Text, image, or file output of the custom tool call.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "The output from the custom tool call generated by your code.\nCan be a string or an list of output content.\n" + }) + }).annotate({ + "title": "Custom tool call output", + "description": "The output of a custom tool call from your code, being sent back to the model.\n" + }), + Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ "description": "The agent that produced this item." }) + ), + "type": Schema.Literal("custom_tool_call").annotate({ + "description": "The type of the custom tool call. Always `custom_tool_call`.\n" + }), + "id": Schema.optionalKey( + Schema.String.annotate({ "description": "The unique ID of the custom tool call in the OpenAI platform.\n" }) + ), + "call_id": Schema.String.annotate({ + "description": "An identifier used to map this custom tool call to a tool call output.\n" + }), + "caller": Schema.optionalKey(Schema.Union([BetaToolCallCaller, Schema.Null])), + "namespace": Schema.optionalKey( + Schema.String.annotate({ "description": "The namespace of the custom tool being called.\n" }) + ), + "name": Schema.String.annotate({ "description": "The name of the custom tool being called.\n" }), + "input": Schema.String.annotate({ + "description": "The input for the custom tool call generated by the model.\n" + }), + "async": Schema.optionalKey( + Schema.Boolean.annotate({ "description": "Whether the custom tool call runs asynchronously.\n" }) + ) + }).annotate({ "title": "Custom tool call", "description": "A call to a custom tool created by the model.\n" }) + ], { mode: "oneOf" }).annotate({ + "description": + "An item representing part of the context for the response to be\ngenerated by the model. Can contain text, images, and audio inputs,\nas well as previous assistant responses and tool call outputs.\n", + "title": "Item" + }), + BetaCompactionTriggerItemParam, + BetaItemReferenceParam, + BetaProgramItemParam, + BetaProgramOutputItemParam +], { mode: "oneOf" }).annotate({ "identifier": "BetaInputItem" }) +export type AssistantStreamEvent = + | ThreadStreamEvent + | RunStreamEvent + | RunStepStreamEvent + | MessageStreamEvent + | ErrorEvent + | DoneEvent +export const AssistantStreamEvent = Schema.Union([ + ThreadStreamEvent, + RunStreamEvent, + RunStepStreamEvent, + MessageStreamEvent, + ErrorEvent, + DoneEvent +], { mode: "oneOf" }).annotate({ + "description": + "Represents an event emitted when streaming a Run.\n\nEach event in a server-sent events stream has an `event` and `data` property:\n\n```\nevent: thread.created\ndata: {\"id\": \"thread_123\", \"object\": \"thread\", ...}\n```\n\nWe emit events whenever a new object is created, transitions to a new state, or is being\nstreamed in parts (deltas). For example, we emit `thread.run.created` when a new run\nis created, `thread.run.completed` when a run completes, and so on. When an Assistant chooses\nto create a message during a run, we emit a `thread.message.created event`, a\n`thread.message.in_progress` event, many `thread.message.delta` events, and finally a\n`thread.message.completed` event.\n\nWe may add additional events over time, so we recommend handling unknown events gracefully\nin your code. See the [Assistants API quickstart](https://developers.openai.com/api/docs/assistants/migration) to learn how to\nintegrate the Assistants API with streaming.\n", + "identifier": "AssistantStreamEvent" +}) +export type EvalGraderLabelModel = { + readonly "type": "label_model" + readonly "name": string + readonly "model": string + readonly "input": ReadonlyArray + readonly "labels": ReadonlyArray + readonly "passing_labels": ReadonlyArray +} +export const EvalGraderLabelModel = Schema.Struct({ + "type": Schema.Literal("label_model").annotate({ "description": "The object type, which is always `label_model`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "model": Schema.String.annotate({ + "description": "The model to use for the evaluation. Must support structured outputs." + }), + "input": Schema.Array(EvalItem), + "labels": Schema.Array(Schema.String).annotate({ + "description": "The labels to assign to each item in the evaluation." + }), + "passing_labels": Schema.Array(Schema.String).annotate({ + "description": "The labels that indicate a passing result. Must be a subset of labels." + }) +}).annotate({ + "title": "LabelModelGrader", + "description": "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation.\n", + "identifier": "EvalGraderLabelModel" +}) +export type EvalGraderScoreModel = { + readonly "type": "score_model" + readonly "name": string + readonly "model": string + readonly "sampling_params"?: { + readonly "seed"?: number | null + readonly "top_p"?: number | null + readonly "temperature"?: number | null + readonly "max_completions_tokens"?: number | null + readonly "reasoning_effort"?: ReasoningEffort + } + readonly "input": ReadonlyArray + readonly "range"?: ReadonlyArray + readonly "pass_threshold"?: number +} +export const EvalGraderScoreModel = Schema.Struct({ + "type": Schema.Literal("score_model").annotate({ "description": "The object type, which is always `score_model`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "model": Schema.String.annotate({ "description": "The model to use for the evaluation." }), + "sampling_params": Schema.optionalKey( + Schema.Struct({ + "seed": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "A seed value to initialize the randomness, during sampling.\n" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "An alternative to temperature for nucleus sampling; 1.0 includes all tokens.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "A higher temperature increases randomness in the outputs.\n" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "max_completions_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The maximum number of tokens the grader model may generate in its response.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ), + Schema.Null + ]) + ), + "reasoning_effort": Schema.optionalKey(ReasoningEffort) + }).annotate({ "description": "The sampling parameters for the model." }) + ), + "input": Schema.Array(EvalItem).annotate({ + "description": + "The input messages evaluated by the grader. Supports text, output text, input image, and input audio content blocks, and may include template strings.\n" + }), + "range": Schema.optionalKey( + Schema.Array(Schema.Finite).annotate({ + "description": "The service requires two numbers for the score range. Defaults to `[0, 1]`.\n" + }) + ), + "pass_threshold": Schema.optionalKey( + Schema.Number.annotate({ "description": "The threshold for the score." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ) +}).annotate({ "title": "ScoreModelGrader", "identifier": "EvalGraderScoreModel" }) +export type CreateEvalItem = { readonly "role": string; readonly "content": string } | EvalItem +export const CreateEvalItem = Schema.Union([ + Schema.Struct({ + "role": Schema.String.annotate({ + "description": "The role of the message (e.g. \"system\", \"assistant\", \"user\")." + }), + "content": Schema.String.annotate({ "description": "The content of the message." }) + }).annotate({ "title": "SimpleInputMessage" }), + EvalItem +], { mode: "oneOf" }).annotate({ + "title": "CreateEvalItem", + "description": + "A chat message that makes up the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.", + "identifier": "CreateEvalItem" +}) +export type CreateEvalCompletionsRunDataSource = { + readonly "type": "completions" + readonly "input_messages"?: { + readonly "type": "template" + readonly "template": ReadonlyArray + } | { readonly "type": "item_reference"; readonly "item_reference": string } + readonly "sampling_params"?: { + readonly "reasoning_effort"?: ReasoningEffort + readonly "temperature"?: number + readonly "max_completion_tokens"?: number + readonly "top_p"?: number + readonly "seed"?: number + readonly "response_format"?: ResponseFormatText | ResponseFormatJsonSchema | ResponseFormatJsonObject + readonly "tools"?: ReadonlyArray + } + readonly "model"?: string + readonly "source": EvalJsonlFileContentSource | EvalJsonlFileIdSource | EvalStoredCompletionsSource +} +export const CreateEvalCompletionsRunDataSource = Schema.Struct({ + "type": Schema.Literal("completions").annotate({ + "description": "The type of run data source. Always `completions`." + }), + "input_messages": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("template").annotate({ + "description": "The type of input messages. Always `template`." + }), + "template": Schema.Array(Schema.Union([EasyInputMessage, EvalItem], { mode: "oneOf" })).annotate({ + "description": + "A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + }) + }).annotate({ "title": "TemplateInputMessages" }), + Schema.Struct({ + "type": Schema.Literal("item_reference").annotate({ + "description": "The type of input messages. Always `item_reference`." + }), + "item_reference": Schema.String.annotate({ + "description": "A reference to a variable in the `item` namespace. Ie, \"item.input_trajectory\"" + }) + }).annotate({ "title": "ItemReferenceInputMessages" }) + ], { mode: "oneOf" }).annotate({ + "description": + "Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace." + }) + ), + "sampling_params": Schema.optionalKey(Schema.Struct({ + "reasoning_effort": Schema.optionalKey(ReasoningEffort), + "temperature": Schema.optionalKey( + Schema.Number.annotate({ "description": "A higher temperature increases randomness in the outputs." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ), + "max_completion_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum number of tokens in the generated output." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "top_p": Schema.optionalKey( + Schema.Number.annotate({ + "description": "An alternative to temperature for nucleus sampling; 1.0 includes all tokens." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "seed": Schema.optionalKey( + Schema.Number.annotate({ "description": "A seed value to initialize the randomness, during sampling." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "response_format": Schema.optionalKey( + Schema.Union([ResponseFormatText, ResponseFormatJsonSchema, ResponseFormatJsonObject], { mode: "oneOf" }) + .annotate({ + "description": + "An object specifying the format that the model must output.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables\nStructured Outputs which ensures the model will match your supplied JSON\nschema. Learn more in the [Structured Outputs\nguide](https://developers.openai.com/api/docs/guides/structured-outputs).\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n" + }) + ), + "tools": Schema.optionalKey( + Schema.Array(ChatCompletionTool).annotate({ + "description": + "A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported.\n" + }) + ) + })), + "model": Schema.optionalKey( + Schema.String.annotate({ + "description": "The name of the model to use for generating completions (e.g. \"o3-mini\")." + }) + ), + "source": Schema.Union([EvalJsonlFileContentSource, EvalJsonlFileIdSource, EvalStoredCompletionsSource], { + mode: "oneOf" + }).annotate({ "description": "Determines what populates the `item` namespace in this run's data source." }) +}).annotate({ + "title": "CompletionsRunDataSource", + "description": "A CompletionsRunDataSource object describing a model sampling configuration.\n", + "identifier": "CreateEvalCompletionsRunDataSource" +}) +export type CreateEvalResponsesRunDataSource = { + readonly "type": "responses" + readonly "input_messages"?: { + readonly "type": "template" + readonly "template": ReadonlyArray<{ readonly "role": string; readonly "content": string } | EvalItem> + } | { readonly "type": "item_reference"; readonly "item_reference": string } + readonly "sampling_params"?: { + readonly "reasoning_effort"?: ReasoningEffort + readonly "temperature"?: number + readonly "max_completion_tokens"?: number + readonly "top_p"?: number + readonly "seed"?: number + readonly "tools"?: ReadonlyArray + readonly "text"?: { readonly "format"?: TextResponseFormatConfiguration } + } + readonly "model"?: string + readonly "source": EvalJsonlFileContentSource | EvalJsonlFileIdSource | EvalResponsesSource +} +export const CreateEvalResponsesRunDataSource = Schema.Struct({ + "type": Schema.Literal("responses").annotate({ "description": "The type of run data source. Always `responses`." }), + "input_messages": Schema.optionalKey( + Schema.Union([ + Schema.Struct({ + "type": Schema.Literal("template").annotate({ + "description": "The type of input messages. Always `template`." + }), + "template": Schema.Array( + Schema.Union([ + Schema.Struct({ + "role": Schema.String.annotate({ + "description": "The role of the message (e.g. \"system\", \"assistant\", \"user\")." + }), + "content": Schema.String.annotate({ "description": "The content of the message." }) + }).annotate({ "title": "ChatMessage" }), + EvalItem + ], { mode: "oneOf" }) + ).annotate({ + "description": + "A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + }) + }).annotate({ "title": "InputMessagesTemplate" }), + Schema.Struct({ + "type": Schema.Literal("item_reference").annotate({ + "description": "The type of input messages. Always `item_reference`." + }), + "item_reference": Schema.String.annotate({ + "description": "A reference to a variable in the `item` namespace. Ie, \"item.name\"" + }) + }).annotate({ "title": "InputMessagesItemReference" }) + ], { mode: "oneOf" }).annotate({ + "description": + "Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace." + }) + ), + "sampling_params": Schema.optionalKey(Schema.Struct({ + "reasoning_effort": Schema.optionalKey(ReasoningEffort), + "temperature": Schema.optionalKey( + Schema.Number.annotate({ "description": "A higher temperature increases randomness in the outputs." }).check( + Schema.isFinite().annotate({ "expected": "a finite number" }) + ) + ), + "max_completion_tokens": Schema.optionalKey( + Schema.Number.annotate({ "description": "The maximum number of tokens in the generated output." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "top_p": Schema.optionalKey( + Schema.Number.annotate({ + "description": "An alternative to temperature for nucleus sampling; 1.0 includes all tokens." + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })) + ), + "seed": Schema.optionalKey( + Schema.Number.annotate({ "description": "A seed value to initialize the randomness, during sampling." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ), + "tools": Schema.optionalKey( + Schema.Array(Tool).annotate({ + "description": + "An array of tools the model may call while generating a response. You\ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)\n or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about\n [built-in tools](https://developers.openai.com/api/docs/guides/tools).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code. Learn more about\n [function calling](https://developers.openai.com/api/docs/guides/function-calling).\n" + }) + ), + "text": Schema.optionalKey( + Schema.Struct({ "format": Schema.optionalKey(TextResponseFormatConfiguration) }).annotate({ + "description": + "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)\n- [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)\n" + }) + ) + })), + "model": Schema.optionalKey( + Schema.String.annotate({ + "description": "The name of the model to use for generating completions (e.g. \"o3-mini\")." + }) + ), + "source": Schema.Union([EvalJsonlFileContentSource, EvalJsonlFileIdSource, EvalResponsesSource], { mode: "oneOf" }) + .annotate({ "description": "Determines what populates the `item` namespace in this run's data source." }) +}).annotate({ + "title": "ResponsesRunDataSource", + "description": "A ResponsesRunDataSource object describing a model sampling configuration.\n", + "identifier": "CreateEvalResponsesRunDataSource" +}) +export type GraderScoreModel = { + readonly "type": "score_model" + readonly "name": string + readonly "model": string + readonly "sampling_params"?: { + readonly "seed"?: number | null + readonly "top_p"?: number | null + readonly "temperature"?: number | null + readonly "max_completions_tokens"?: number | null + readonly "reasoning_effort"?: ReasoningEffort + } + readonly "input": ReadonlyArray + readonly "range"?: ReadonlyArray +} +export const GraderScoreModel = Schema.Struct({ + "type": Schema.Literal("score_model").annotate({ "description": "The object type, which is always `score_model`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "model": Schema.String.annotate({ "description": "The model to use for the evaluation." }), + "sampling_params": Schema.optionalKey( + Schema.Struct({ + "seed": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "A seed value to initialize the randomness, during sampling.\n" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "An alternative to temperature for nucleus sampling; 1.0 includes all tokens.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ "description": "A higher temperature increases randomness in the outputs.\n" }) + .check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "max_completions_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": "The maximum number of tokens the grader model may generate in its response.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ), + Schema.Null + ]) + ), + "reasoning_effort": Schema.optionalKey(ReasoningEffort) + }).annotate({ "description": "The sampling parameters for the model." }) + ), + "input": Schema.Array(EvalItem).annotate({ + "description": + "The input messages evaluated by the grader. Supports text, output text, input image, and input audio content blocks, and may include template strings.\n" + }), + "range": Schema.optionalKey( + Schema.Array(Schema.Finite).annotate({ + "description": "The service requires two numbers for the score range. Defaults to `[0, 1]`.\n" + }) + ) +}).annotate({ + "title": "ScoreModelGrader", + "description": "A ScoreModelGrader object that uses a model to assign a score to the input.\n", + "identifier": "GraderScoreModel" +}) +export type GraderLabelModel = { + readonly "type": "label_model" + readonly "name": string + readonly "model": string + readonly "input": ReadonlyArray + readonly "labels": ReadonlyArray + readonly "passing_labels": ReadonlyArray +} +export const GraderLabelModel = Schema.Struct({ + "type": Schema.Literal("label_model").annotate({ "description": "The object type, which is always `label_model`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "model": Schema.String.annotate({ + "description": "The model to use for the evaluation. Must support structured outputs." + }), + "input": Schema.Array(EvalItem), + "labels": Schema.Array(Schema.String).annotate({ + "description": "The labels to assign to each item in the evaluation." + }), + "passing_labels": Schema.Array(Schema.String).annotate({ + "description": "The labels that indicate a passing result. Must be a subset of labels." + }) +}).annotate({ + "title": "LabelModelGrader", + "description": "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation.\n", + "identifier": "GraderLabelModel" +}) +export type ConversationItem = + | Message + | FunctionToolCallResource + | FunctionToolCallOutputResource + | FileSearchToolCall + | WebSearchToolCall + | ImageGenToolCall + | ComputerToolCall + | ComputerToolCallOutputResource + | ToolSearchCall + | ToolSearchOutput + | AdditionalTools + | ResponseConfigurationUpdate + | ReasoningItem + | Program + | ProgramOutput + | CompactionBody + | CodeInterpreterToolCall + | LocalShellToolCall + | LocalShellToolCallOutput + | FunctionShellCall + | FunctionShellCallOutput + | ApplyPatchToolCall + | ApplyPatchToolCallOutput + | MCPListTools + | MCPApprovalRequest + | MCPApprovalResponseResource + | MCPToolCall + | CustomToolCall + | CustomToolCallOutput +export const ConversationItem = Schema.Union([ + Message, + FunctionToolCallResource, + FunctionToolCallOutputResource, + FileSearchToolCall, + WebSearchToolCall, + ImageGenToolCall, + ComputerToolCall, + ComputerToolCallOutputResource, + ToolSearchCall, + ToolSearchOutput, + AdditionalTools, + ResponseConfigurationUpdate, + ReasoningItem, + Program, + ProgramOutput, + CompactionBody, + CodeInterpreterToolCall, + LocalShellToolCall, + LocalShellToolCallOutput, + FunctionShellCall, + FunctionShellCallOutput, + ApplyPatchToolCall, + ApplyPatchToolCallOutput, + MCPListTools, + MCPApprovalRequest, + MCPApprovalResponseResource, + MCPToolCall, + CustomToolCall, + CustomToolCallOutput +], { mode: "oneOf" }).annotate({ + "title": "Conversation item", + "description": + "A single item within a conversation. The set of possible types are the same as the `output` type of a [Response object](https://developers.openai.com/api/reference/resources/responses#%28resource%29%20responses%20%3E%20%28model%29%20response%20%3E%20%28schema%29%20%3E%20%28property%29%20output).", + "identifier": "ConversationItem" +}) +export type OutputItem = + | OutputMessage + | FileSearchToolCall + | FunctionToolCall + | FunctionToolCallOutputResource + | WebSearchToolCall + | ComputerToolCall + | ComputerToolCallOutputResource + | ReasoningItem + | Program + | ProgramOutput + | ToolSearchCall + | ToolSearchOutput + | AdditionalTools + | CompactionBody + | ImageGenToolCall + | CodeInterpreterToolCall + | LocalShellToolCall + | LocalShellToolCallOutput + | FunctionShellCall + | FunctionShellCallOutput + | ApplyPatchToolCall + | ApplyPatchToolCallOutput + | MCPToolCall + | MCPListTools + | MCPApprovalRequest + | MCPApprovalResponseResource + | CustomToolCall + | CustomToolCallOutputResource +export const OutputItem = Schema.Union([ + OutputMessage, + FileSearchToolCall, + FunctionToolCall, + FunctionToolCallOutputResource, + WebSearchToolCall, + ComputerToolCall, + ComputerToolCallOutputResource, + ReasoningItem, + Program, + ProgramOutput, + ToolSearchCall, + ToolSearchOutput, + AdditionalTools, + CompactionBody, + ImageGenToolCall, + CodeInterpreterToolCall, + LocalShellToolCall, + LocalShellToolCallOutput, + FunctionShellCall, + FunctionShellCallOutput, + ApplyPatchToolCall, + ApplyPatchToolCallOutput, + MCPToolCall, + MCPListTools, + MCPApprovalRequest, + MCPApprovalResponseResource, + CustomToolCall, + CustomToolCallOutputResource +], { mode: "oneOf" }).annotate({ "identifier": "OutputItem" }) +export type ItemResource = + | InputMessageResource + | OutputMessage + | FileSearchToolCall + | ComputerToolCall + | ComputerToolCallOutputResource + | WebSearchToolCall + | FunctionToolCallResource + | FunctionToolCallOutputResource + | ToolSearchCall + | ToolSearchOutput + | AdditionalTools + | ResponseConfigurationUpdate + | ReasoningItem + | Program + | ProgramOutput + | CompactionBody + | ImageGenToolCall + | CodeInterpreterToolCall + | LocalShellToolCall + | LocalShellToolCallOutput + | FunctionShellCall + | FunctionShellCallOutput + | ApplyPatchToolCall + | ApplyPatchToolCallOutput + | MCPListTools + | MCPApprovalRequest + | MCPApprovalResponseResource + | MCPToolCall + | CustomToolCallResource + | CustomToolCallOutputResource +export const ItemResource = Schema.Union([ + InputMessageResource, + OutputMessage, + FileSearchToolCall, + ComputerToolCall, + ComputerToolCallOutputResource, + WebSearchToolCall, + FunctionToolCallResource, + FunctionToolCallOutputResource, + ToolSearchCall, + ToolSearchOutput, + AdditionalTools, + ResponseConfigurationUpdate, + ReasoningItem, + Program, + ProgramOutput, + CompactionBody, + ImageGenToolCall, + CodeInterpreterToolCall, + LocalShellToolCall, + LocalShellToolCallOutput, + FunctionShellCall, + FunctionShellCallOutput, + ApplyPatchToolCall, + ApplyPatchToolCallOutput, + MCPListTools, + MCPApprovalRequest, + MCPApprovalResponseResource, + MCPToolCall, + CustomToolCallResource, + CustomToolCallOutputResource +], { mode: "oneOf" }).annotate({ + "description": "Content item used to generate a response.\n", + "identifier": "ItemResource" +}) +export type ItemField = + | Message + | Program + | ProgramOutput + | FunctionToolCall + | ToolSearchCall + | ToolSearchOutput + | AdditionalTools + | FunctionToolCallOutput + | FileSearchToolCall + | WebSearchToolCall + | ImageGenToolCall + | ComputerToolCall + | ComputerToolCallOutputResource + | ReasoningItem + | CompactionBody + | CodeInterpreterToolCall + | LocalShellToolCall + | LocalShellToolCallOutput + | FunctionShellCall + | FunctionShellCallOutput + | ApplyPatchToolCall + | ApplyPatchToolCallOutput + | MCPListTools + | MCPApprovalRequest + | MCPApprovalResponseResource + | MCPToolCall + | CustomToolCall + | CustomToolCallOutput +export const ItemField = Schema.Union([ + Message, + Program, + ProgramOutput, + FunctionToolCall, + ToolSearchCall, + ToolSearchOutput, + AdditionalTools, + FunctionToolCallOutput, + FileSearchToolCall, + WebSearchToolCall, + ImageGenToolCall, + ComputerToolCall, + ComputerToolCallOutputResource, + ReasoningItem, + CompactionBody, + CodeInterpreterToolCall, + LocalShellToolCall, + LocalShellToolCallOutput, + FunctionShellCall, + FunctionShellCallOutput, + ApplyPatchToolCall, + ApplyPatchToolCallOutput, + MCPListTools, + MCPApprovalRequest, + MCPApprovalResponseResource, + MCPToolCall, + CustomToolCall, + CustomToolCallOutput +], { mode: "oneOf" }).annotate({ + "description": "An item representing a message, tool call, tool output, reasoning, or other response element.", + "identifier": "ItemField" +}) +export type InputParam = string | ReadonlyArray +export const InputParam = Schema.Union([ + Schema.String.annotate({ + "title": "Text input", + "description": "A text input to the model, equivalent to a text input with the\n`user` role.\n" + }), + Schema.Array(InputItem).annotate({ + "title": "Input item list", + "description": "A list of one or many input items to the model, containing\ndifferent content types.\n" + }) +], { mode: "oneOf" }).annotate({ + "description": + "Text, image, or file inputs to the model, used to generate a response.\n\nLearn more:\n- [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)\n- [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)\n- [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)\n- [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)\n- [Function calling](https://developers.openai.com/api/docs/guides/function-calling)\n", + "identifier": "InputParam" +}) +export type CreateConversationBody = { + readonly "metadata"?: Metadata | null + readonly "items"?: ReadonlyArray | null +} +export const CreateConversationBody = Schema.Struct({ + "metadata": Schema.optionalKey(Schema.Union([ + Schema.suspend((): Schema.Codec => Metadata).annotate({ + "description": + "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.\n Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters." + }), + Schema.Null + ])), + "items": Schema.optionalKey( + Schema.Union([ + Schema.Array(InputItem).annotate({ + "description": "Initial items to include in the conversation context. You may add up to 20 items at a time." + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })), + Schema.Null + ]) + ) +}).annotate({ "identifier": "CreateConversationBody" }) +export type TokenCountsBody = { + readonly "model"?: string | null + readonly "input"?: string | ReadonlyArray | null + readonly "previous_response_id"?: string | null + readonly "tools"?: ReadonlyArray | null + readonly "text"?: ResponseTextParam | null + readonly "reasoning"?: Reasoning | null + readonly "truncation"?: TruncationEnum + readonly "instructions"?: string | null + readonly "personality"?: PersonalityEnum + readonly "conversation"?: ConversationParam | null + readonly "tool_choice"?: ToolChoiceParam | null + readonly "parallel_tool_calls"?: boolean | null +} +export const TokenCountsBody = Schema.Struct({ + "model": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models." + }), + Schema.Null + ])), + "input": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.String.annotate({ + "description": "A text input to the model, equivalent to a text input with the `user` role." + }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array(InputItem).annotate({ + "description": "A list of one or many input items to the model, containing different content types." + }).check(Schema.isMaxLength(131072).annotate({ "expected": "a value with a length of at most 131072" })) + ], { mode: "oneOf" }).annotate({ + "description": "Text, image, or file inputs to the model, used to generate a response" + }), + Schema.Null + ]) + ), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`." + }), + Schema.Null + ])), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array(Tool).annotate({ + "description": + "An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter." + }), + Schema.Null + ]) + ), + "text": Schema.optionalKey(Schema.Union([ResponseTextParam, Schema.Null])), + "reasoning": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Reasoning).annotate({ + "description": + "**gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning)." + }), + Schema.Null + ]) + ), + "truncation": Schema.optionalKey( + Schema.suspend((): Schema.Codec => TruncationEnum).annotate({ + "description": + "The truncation strategy to use for the model response. - `auto`: If the input to this Response exceeds the model's context window size, the model will truncate the response to fit the context window by dropping items from the beginning of the conversation. - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error." + }) + ), + "instructions": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A system (or developer) message inserted into the model's context.\nWhen used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses." + }), + Schema.Null + ])), + "personality": Schema.optionalKey( + Schema.suspend((): Schema.Codec => PersonalityEnum).annotate({ + "description": + "A model-owned style preset to apply to this request. Omit this parameter to use the model's default style. Supported values may expand over time. Values must be at most 64 characters." + }) + ), + "conversation": Schema.optionalKey(Schema.Union([ConversationParam, Schema.Null])), + "tool_choice": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ToolChoiceParam).annotate({ + "description": "Controls which tool the model should use, if any." + }), + Schema.Null + ]) + ), + "parallel_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ "description": "Whether to allow the model to run tool calls in parallel." }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "TokenCountsBody" }) +export type CompactResponseMethodPublicBody = { + readonly "model": ModelIdsCompaction + readonly "input"?: string | ReadonlyArray | null + readonly "previous_response_id"?: string | null + readonly "instructions"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: PromptCacheRetentionEnum | null + readonly "prompt_cache_options"?: PromptCacheOptionsParam | null + readonly "service_tier"?: ServiceTierEnum | null +} +export const CompactResponseMethodPublicBody = Schema.Struct({ + "model": ModelIdsCompaction, + "input": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.String.annotate({ + "description": "A text input to the model, equivalent to a text input with the `user` role." + }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array(InputItem).annotate({ + "description": "A list of one or many input items to the model, containing different content types." + }).check(Schema.isMaxLength(131072).annotate({ "expected": "a value with a length of at most 131072" })) + ], { mode: "oneOf" }).annotate({ + "description": "Text, image, or file inputs to the model, used to generate a response" + }), + Schema.Null + ]) + ), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`." + }), + Schema.Null + ])), + "instructions": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A system (or developer) message inserted into the model's context.\nWhen used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses." + }), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "A key to use when reading from or writing to the prompt cache." }).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => PromptCacheRetentionEnum).annotate({ + "description": "How long to retain a prompt cache entry created by this request." + }), + Schema.Null + ]) + ), + "prompt_cache_options": Schema.optionalKey(Schema.Union([PromptCacheOptionsParam, Schema.Null])), + "service_tier": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ServiceTierEnum).annotate({ + "description": + "Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.\n When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "CompactResponseMethodPublicBody" }) +export type Delegation = LiveClientDelegationParam | LiveResponsesDelegationParam | null +export const Delegation = Schema.Union([ + Schema.Union([LiveClientDelegationParam, LiveResponsesDelegationParam], { mode: "oneOf" }).annotate({ + "description": + "Who handles tasks delegated by the Live model. Omitted or null selects your application; use `responses` to let the API manage a Responses backend." + }), + Schema.Null +]).annotate({ "identifier": "delegation" }) +export type LiveMediaSessionForkParams = { + readonly "store"?: Properties_store + readonly "delegation"?: LiveResponsesDelegationUpdateParam + readonly "client"?: LiveClientConfigParam +} +export const LiveMediaSessionForkParams = Schema.Struct({ + "store": Schema.optionalKey(Properties_store), + "delegation": Schema.optionalKey(LiveResponsesDelegationUpdateParam), + "client": Schema.optionalKey(LiveClientConfigParam) +}).annotate({ + "description": + "Optional overrides for a stored Live session. Omitted settings are inherited. The model, voice, frontend instructions, and prior conversation come from the stored session. WebRTC negotiates its audio format; audio.format is only supported on WebSocket forks.", + "identifier": "LiveMediaSessionForkParams" +}) +export type DecisionRequest = { + readonly "model": string + readonly "input": DecisionInput + readonly "questions": ReadonlyArray + readonly "safety_identifier"?: string | null +} +export const DecisionRequest = Schema.Struct({ + "model": Schema.String.check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ).check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + "input": DecisionInput, + "questions": Schema.Array(QuestionParam).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ).check(Schema.isMaxLength(200).annotate({ "expected": "a value with a length of at most 200" })), + "safety_identifier": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ]).annotate({ + "description": + "Opaque caller-provided end-user identifier, scoped by the verified org. Match Responses' limit; this is never the authenticated user identity." + }) + ) +}).annotate({ "identifier": "DecisionRequest" }) +export type CreateAgentEnvironmentParams = { + readonly "environment": CreateEnvironmentParam + readonly "vault_ids"?: ReadonlyArray | null +} +export const CreateAgentEnvironmentParams = Schema.Struct({ + "environment": Schema.suspend((): Schema.Codec => CreateEnvironmentParam).annotate({ + "description": "The required hosting type and its configuration." + }), + "vault_ids": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(10).annotate({ "expected": "a value with a length of at most 10" }) + ), + Schema.Null + ]).annotate({ "description": "The IDs of up to 10 vaults made available to an OpenAI-hosted environment." }) + ) +}).annotate({ + "description": "Parameters for creating an execution environment before its sessions.", + "identifier": "CreateAgentEnvironmentParams" +}) +export type CreateAgentSessionParams = { + readonly "spend_control"?: SessionSpendControlParam | null + readonly "metadata"?: { readonly [x: string]: string } | null + readonly "agent"?: SessionAgentConfigParam + readonly "agent_id"?: string + readonly "environment": EnvironmentParam + readonly "vault_ids"?: ReadonlyArray | null + readonly "input"?: CreateSessionInputParam | null + readonly "stream"?: boolean +} +export const CreateAgentSessionParams = Schema.Struct({ + "spend_control": Schema.optionalKey( + Schema.Union([SessionSpendControlParam, Schema.Null]).annotate({ + "description": "Optional spending limit in USD cents. Omission or null creates an unlimited session." + }) + ), + "metadata": Schema.optionalKey( + Schema.Union([ + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ), + Schema.Null + ]).annotate({ + "description": + "Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters. Omission or null defaults to an empty map." + }) + ), + "agent": Schema.optionalKey( + Schema.suspend((): Schema.Codec => SessionAgentConfigParam).annotate({ + "description": + "Agent configuration. With `agent_id`, supplied fields override the saved agent for this session. Without `agent_id`, `model` is required." + }) + ), + "agent_id": Schema.optionalKey( + Schema.String.annotate({ + "description": "The ID of a saved reusable agent. Omit `agent` to use its configuration unchanged." + }).check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ) + ), + "environment": Schema.suspend((): Schema.Codec => EnvironmentParam).annotate({ + "description": "An inline execution environment or a reference to an environment template." + }), + "vault_ids": Schema.optionalKey( + Schema.Union([ + Schema.Array( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(16384).annotate({ "expected": "a value with a length of at most 16384" }) + ), + Schema.Null + ]).annotate({ "description": "The IDs of vaults made available to the session." }) + ), + "input": Schema.optionalKey( + Schema.Union([CreateSessionInputParam, Schema.Null]).annotate({ + "description": + "Initial input to submit when the session is created. A string is shorthand for a single user message. Required when `environment.type` is `none`, or when `stream` is `true` for an environment that is not `self_hosted`; optional for self-hosted and non-streaming execution environments." + }) + ), + "stream": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": "Whether to stream session events as server-sent events. Defaults to `false`." + }) + ) +}).annotate({ + "description": "Parameters for creating a Managed Agents session.", + "identifier": "CreateAgentSessionParams" +}) +export type SessionListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const SessionListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(SessionResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "SessionListResource" +}) +export type SessionEventAgentSessionCreated = { + readonly "type": "agent.session.created" + readonly "event_id": string + readonly "session": SessionResource +} +export const SessionEventAgentSessionCreated = Schema.Struct({ + "type": Schema.Literal("agent.session.created").annotate({ + "description": "The type of the object. Always `agent.session.created`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session": Schema.suspend((): Schema.Codec => SessionResource).annotate({ + "description": "The session that was created." + }) +}).annotate({ "description": "Emitted when a session is created.", "identifier": "SessionEventAgentSessionCreated" }) +export type SessionEventAgentSessionIdle = { + readonly "type": "agent.session.idle" + readonly "event_id": string + readonly "session": SessionResource +} +export const SessionEventAgentSessionIdle = Schema.Struct({ + "type": Schema.Literal("agent.session.idle").annotate({ + "description": "The type of the object. Always `agent.session.idle`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session": Schema.suspend((): Schema.Codec => SessionResource).annotate({ + "description": "The session that became idle." + }) +}).annotate({ "description": "Emitted when a session becomes idle.", "identifier": "SessionEventAgentSessionIdle" }) +export type SessionEventAgentSessionInProgress = { + readonly "type": "agent.session.in_progress" + readonly "event_id": string + readonly "session": SessionResource +} +export const SessionEventAgentSessionInProgress = Schema.Struct({ + "type": Schema.Literal("agent.session.in_progress").annotate({ + "description": "The type of the object. Always `agent.session.in_progress`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session": Schema.suspend((): Schema.Codec => SessionResource).annotate({ + "description": "The session that started processing." + }) +}).annotate({ + "description": "Emitted when a session starts processing a turn.", + "identifier": "SessionEventAgentSessionInProgress" +}) +export type SessionEventAgentSessionRequiresAction = { + readonly "type": "agent.session.requires_action" + readonly "event_id": string + readonly "session": SessionResource +} +export const SessionEventAgentSessionRequiresAction = Schema.Struct({ + "type": Schema.Literal("agent.session.requires_action").annotate({ + "description": "The type of the object. Always `agent.session.requires_action`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session": Schema.suspend((): Schema.Codec => SessionResource).annotate({ + "description": "The session and its current required actions." + }) +}).annotate({ + "description": "Emitted when a session is waiting for one or more required actions.", + "identifier": "SessionEventAgentSessionRequiresAction" +}) +export type SessionEventAgentSessionFailed = { + readonly "type": "agent.session.failed" + readonly "event_id": string + readonly "session": SessionResource +} +export const SessionEventAgentSessionFailed = Schema.Struct({ + "type": Schema.Literal("agent.session.failed").annotate({ + "description": "The type of the object. Always `agent.session.failed`." + }), + "event_id": Schema.String.annotate({ "description": "The unique ID of the event." }).check( + Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" }) + ), + "session": Schema.suspend((): Schema.Codec => SessionResource).annotate({ + "description": "The failed session." + }) +}).annotate({ "description": "Emitted when a session fails.", "identifier": "SessionEventAgentSessionFailed" }) +export type VaultCredentialListResource = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const VaultCredentialListResource = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(VaultCredentialResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ + "description": "A page of Agents API resources, with IDs for retrieving additional pages.", + "identifier": "VaultCredentialListResource" +}) +export type BetaOutputItem = + | BetaOutputMessage + | BetaFileSearchToolCall + | BetaFunctionToolCall + | BetaFunctionToolCallOutputResource + | BetaAgentMessage + | BetaMultiAgentCall + | BetaMultiAgentCallOutput + | BetaWebSearchToolCall + | BetaComputerToolCall + | BetaComputerToolCallOutputResource + | BetaReasoningItem + | BetaProgram + | BetaProgramOutput + | BetaToolSearchCall + | BetaToolSearchOutput + | BetaAdditionalTools + | BetaCompactionBody + | BetaImageGenToolCall + | BetaCodeInterpreterToolCall + | BetaLocalShellToolCall + | BetaLocalShellToolCallOutput + | BetaFunctionShellCall + | BetaFunctionShellCallOutput + | BetaApplyPatchToolCall + | BetaApplyPatchToolCallOutput + | BetaMCPToolCall + | BetaMCPListTools + | BetaMCPApprovalRequest + | BetaMCPApprovalResponseResource + | BetaCustomToolCall + | BetaCustomToolCallOutputResource +export const BetaOutputItem = Schema.Union([ + BetaOutputMessage, + BetaFileSearchToolCall, + BetaFunctionToolCall, + BetaFunctionToolCallOutputResource, + BetaAgentMessage, + BetaMultiAgentCall, + BetaMultiAgentCallOutput, + BetaWebSearchToolCall, + BetaComputerToolCall, + BetaComputerToolCallOutputResource, + BetaReasoningItem, + BetaProgram, + BetaProgramOutput, + BetaToolSearchCall, + BetaToolSearchOutput, + BetaAdditionalTools, + BetaCompactionBody, + BetaImageGenToolCall, + BetaCodeInterpreterToolCall, + BetaLocalShellToolCall, + BetaLocalShellToolCallOutput, + BetaFunctionShellCall, + BetaFunctionShellCallOutput, + BetaApplyPatchToolCall, + BetaApplyPatchToolCallOutput, + BetaMCPToolCall, + BetaMCPListTools, + BetaMCPApprovalRequest, + BetaMCPApprovalResponseResource, + BetaCustomToolCall, + BetaCustomToolCallOutputResource +], { mode: "oneOf" }).annotate({ "identifier": "BetaOutputItem" }) +export type BetaItemField = + | BetaMessage + | BetaProgram + | BetaProgramOutput + | BetaFunctionToolCall + | BetaMultiAgentCall + | BetaMultiAgentCallOutput + | BetaToolSearchCall + | BetaToolSearchOutput + | BetaAdditionalTools + | BetaAgentMessage + | BetaFunctionToolCallOutput + | BetaFileSearchToolCall + | BetaWebSearchToolCall + | BetaImageGenToolCall + | BetaComputerToolCall + | BetaComputerToolCallOutputResource + | BetaReasoningItem + | BetaCompactionBody + | BetaCodeInterpreterToolCall + | BetaLocalShellToolCall + | BetaLocalShellToolCallOutput + | BetaFunctionShellCall + | BetaFunctionShellCallOutput + | BetaApplyPatchToolCall + | BetaApplyPatchToolCallOutput + | BetaMCPListTools + | BetaMCPApprovalRequest + | BetaMCPApprovalResponseResource + | BetaMCPToolCall + | BetaCustomToolCall + | BetaCustomToolCallOutput +export const BetaItemField = Schema.Union([ + BetaMessage, + BetaProgram, + BetaProgramOutput, + BetaFunctionToolCall, + BetaMultiAgentCall, + BetaMultiAgentCallOutput, + BetaToolSearchCall, + BetaToolSearchOutput, + BetaAdditionalTools, + BetaAgentMessage, + BetaFunctionToolCallOutput, + BetaFileSearchToolCall, + BetaWebSearchToolCall, + BetaImageGenToolCall, + BetaComputerToolCall, + BetaComputerToolCallOutputResource, + BetaReasoningItem, + BetaCompactionBody, + BetaCodeInterpreterToolCall, + BetaLocalShellToolCall, + BetaLocalShellToolCallOutput, + BetaFunctionShellCall, + BetaFunctionShellCallOutput, + BetaApplyPatchToolCall, + BetaApplyPatchToolCallOutput, + BetaMCPListTools, + BetaMCPApprovalRequest, + BetaMCPApprovalResponseResource, + BetaMCPToolCall, + BetaCustomToolCall, + BetaCustomToolCallOutput +], { mode: "oneOf" }).annotate({ + "description": "An item representing a message, tool call, tool output, reasoning, or other response element.", + "identifier": "BetaItemField" +}) +export type BetaItemResource = + | BetaInputMessageResource + | BetaOutputMessage + | BetaFileSearchToolCall + | BetaComputerToolCall + | BetaComputerToolCallOutputResource + | BetaWebSearchToolCall + | BetaFunctionToolCallResource + | BetaFunctionToolCallOutputResource + | BetaAgentMessage + | BetaMultiAgentCall + | BetaMultiAgentCallOutput + | BetaToolSearchCall + | BetaToolSearchOutput + | BetaAdditionalTools + | BetaResponseConfigurationUpdate + | BetaReasoningItem + | BetaProgram + | BetaProgramOutput + | BetaCompactionBody + | BetaImageGenToolCall + | BetaCodeInterpreterToolCall + | BetaLocalShellToolCall + | BetaLocalShellToolCallOutput + | BetaFunctionShellCall + | BetaFunctionShellCallOutput + | BetaApplyPatchToolCall + | BetaApplyPatchToolCallOutput + | BetaMCPListTools + | BetaMCPApprovalRequest + | BetaMCPApprovalResponseResource + | BetaMCPToolCall + | BetaCustomToolCallResource + | BetaCustomToolCallOutputResource +export const BetaItemResource = Schema.Union([ + BetaInputMessageResource, + BetaOutputMessage, + BetaFileSearchToolCall, + BetaComputerToolCall, + BetaComputerToolCallOutputResource, + BetaWebSearchToolCall, + BetaFunctionToolCallResource, + BetaFunctionToolCallOutputResource, + BetaAgentMessage, + BetaMultiAgentCall, + BetaMultiAgentCallOutput, + BetaToolSearchCall, + BetaToolSearchOutput, + BetaAdditionalTools, + BetaResponseConfigurationUpdate, + BetaReasoningItem, + BetaProgram, + BetaProgramOutput, + BetaCompactionBody, + BetaImageGenToolCall, + BetaCodeInterpreterToolCall, + BetaLocalShellToolCall, + BetaLocalShellToolCallOutput, + BetaFunctionShellCall, + BetaFunctionShellCallOutput, + BetaApplyPatchToolCall, + BetaApplyPatchToolCallOutput, + BetaMCPListTools, + BetaMCPApprovalRequest, + BetaMCPApprovalResponseResource, + BetaMCPToolCall, + BetaCustomToolCallResource, + BetaCustomToolCallOutputResource +], { mode: "oneOf" }).annotate({ + "description": "Content item used to generate a response.\n", + "identifier": "BetaItemResource" +}) +export type BetaInputParam = string | ReadonlyArray +export const BetaInputParam = Schema.Union([ + Schema.String.annotate({ + "title": "Text input", + "description": "A text input to the model, equivalent to a text input with the\n`user` role.\n" + }), + Schema.Array(BetaInputItem).annotate({ + "title": "Input item list", + "description": "A list of one or many input items to the model, containing\ndifferent content types.\n" + }) +], { mode: "oneOf" }).annotate({ + "description": + "Text, image, or file inputs to the model, used to generate a response.\n\nLearn more:\n- [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)\n- [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)\n- [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)\n- [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)\n- [Function calling](https://developers.openai.com/api/docs/guides/function-calling)\n", + "identifier": "BetaInputParam" +}) +export type BetaCompactResponseMethodPublicBody = { + readonly "model": BetaModelIdsCompaction + readonly "input"?: string | ReadonlyArray | null + readonly "previous_response_id"?: string | null + readonly "instructions"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: BetaPromptCacheRetentionEnum | null + readonly "prompt_cache_options"?: BetaPromptCacheOptionsParam | null + readonly "service_tier"?: BetaServiceTierEnum | null +} +export const BetaCompactResponseMethodPublicBody = Schema.Struct({ + "model": BetaModelIdsCompaction, + "input": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.String.annotate({ + "description": "A text input to the model, equivalent to a text input with the `user` role." + }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array(BetaInputItem).annotate({ + "description": "A list of one or many input items to the model, containing different content types." + }).check(Schema.isMaxLength(131072).annotate({ "expected": "a value with a length of at most 131072" })) + ], { mode: "oneOf" }).annotate({ + "description": "Text, image, or file inputs to the model, used to generate a response" + }), + Schema.Null + ]) + ), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`." + }), + Schema.Null + ])), + "instructions": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A system (or developer) message inserted into the model's context.\nWhen used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses." + }), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "A key to use when reading from or writing to the prompt cache." }).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaPromptCacheRetentionEnum).annotate({ + "description": "How long to retain a prompt cache entry created by this request." + }), + Schema.Null + ]) + ), + "prompt_cache_options": Schema.optionalKey(Schema.Union([BetaPromptCacheOptionsParam, Schema.Null])), + "service_tier": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaServiceTierEnum).annotate({ + "description": + "Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.\n When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaCompactResponseMethodPublicBody" }) +export type BetaTokenCountsBody = { + readonly "model"?: string | null + readonly "input"?: string | ReadonlyArray | null + readonly "previous_response_id"?: string | null + readonly "tools"?: ReadonlyArray | null + readonly "text"?: BetaResponseTextParam | null + readonly "reasoning"?: BetaReasoning | null + readonly "truncation"?: BetaTruncationEnum + readonly "instructions"?: string | null + readonly "personality"?: BetaPersonalityEnum + readonly "conversation"?: BetaConversationParam | null + readonly "tool_choice"?: BetaToolChoiceParam | null + readonly "parallel_tool_calls"?: boolean | null +} +export const BetaTokenCountsBody = Schema.Struct({ + "model": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models." + }), + Schema.Null + ])), + "input": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.String.annotate({ + "description": "A text input to the model, equivalent to a text input with the `user` role." + }).check( + Schema.isMaxCodePoints(10485760).annotate({ "expected": "a string with at most 10485760 code points" }) + ), + Schema.Array(BetaInputItem).annotate({ + "description": "A list of one or many input items to the model, containing different content types." + }).check(Schema.isMaxLength(131072).annotate({ "expected": "a value with a length of at most 131072" })) + ], { mode: "oneOf" }).annotate({ + "description": "Text, image, or file inputs to the model, used to generate a response" + }), + Schema.Null + ]) + ), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`." + }), + Schema.Null + ])), + "tools": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaTool).annotate({ + "description": + "An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter." + }), + Schema.Null + ]) + ), + "text": Schema.optionalKey(Schema.Union([BetaResponseTextParam, Schema.Null])), + "reasoning": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaReasoning).annotate({ + "description": + "**gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning)." + }), + Schema.Null + ]) + ), + "truncation": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaTruncationEnum).annotate({ + "description": + "The truncation strategy to use for the model response. - `auto`: If the input to this Response exceeds the model's context window size, the model will truncate the response to fit the context window by dropping items from the beginning of the conversation. - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error." + }) + ), + "instructions": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A system (or developer) message inserted into the model's context.\nWhen used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses." + }), + Schema.Null + ])), + "personality": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaPersonalityEnum).annotate({ + "description": + "A model-owned style preset to apply to this request. Omit this parameter to use the model's default style. Supported values may expand over time. Values must be at most 64 characters." + }) + ), + "conversation": Schema.optionalKey(Schema.Union([BetaConversationParam, Schema.Null])), + "tool_choice": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaToolChoiceParam).annotate({ + "description": "Controls which tool the model should use, if any." + }), + Schema.Null + ]) + ), + "parallel_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ "description": "Whether to allow the model to run tool calls in parallel." }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaTokenCountsBody" }) +export type Eval = { + readonly "object": "eval" + readonly "id": string + readonly "name": string + readonly "data_source_config": + | EvalCustomDataSourceConfig + | EvalLogsDataSourceConfig + | EvalStoredCompletionsDataSourceConfig + readonly "testing_criteria": ReadonlyArray< + EvalGraderLabelModel | EvalGraderStringCheck | EvalGraderTextSimilarity | EvalGraderPython | EvalGraderScoreModel + > + readonly "created_at": number + readonly "metadata": Metadata +} +export const Eval = Schema.Struct({ + "object": Schema.Literal("eval").annotate({ "description": "The object type." }), + "id": Schema.String.annotate({ "description": "Unique identifier for the evaluation." }), + "name": Schema.String.annotate({ "description": "The name of the evaluation." }), + "data_source_config": Schema.Union([ + EvalCustomDataSourceConfig, + EvalLogsDataSourceConfig, + EvalStoredCompletionsDataSourceConfig + ], { mode: "oneOf" }).annotate({ "description": "Configuration of data sources used in runs of the evaluation." }), + "testing_criteria": Schema.Array( + Schema.Union([ + EvalGraderLabelModel, + EvalGraderStringCheck, + EvalGraderTextSimilarity, + EvalGraderPython, + EvalGraderScoreModel + ], { mode: "oneOf" }) + ).annotate({ "description": "A list of testing criteria." }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the eval was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "metadata": Metadata +}).annotate({ + "title": "Eval", + "description": + "An Eval object with a data source config and testing criteria.\nAn Eval represents a task to be done for your LLM integration.\nLike:\n - Improve the quality of my chatbot\n - See how well my chatbot handles customer support\n - Check if o4-mini is better at my usecase than gpt-6-astra\n", + "identifier": "Eval" +}) +export type CreateEvalLabelModelGrader = { + readonly "type": "label_model" + readonly "name": string + readonly "model": string + readonly "input": ReadonlyArray + readonly "labels": ReadonlyArray + readonly "passing_labels": ReadonlyArray +} +export const CreateEvalLabelModelGrader = Schema.Struct({ + "type": Schema.Literal("label_model").annotate({ "description": "The object type, which is always `label_model`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "model": Schema.String.annotate({ + "description": "The model to use for the evaluation. Must support structured outputs." + }), + "input": Schema.Array(CreateEvalItem).annotate({ + "description": + "A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + }), + "labels": Schema.Array(Schema.String).annotate({ + "description": "The labels to classify to each item in the evaluation." + }), + "passing_labels": Schema.Array(Schema.String).annotate({ + "description": "The labels that indicate a passing result. Must be a subset of labels." + }) +}).annotate({ + "title": "LabelModelGrader", + "description": "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation.\n", + "identifier": "CreateEvalLabelModelGrader" +}) +export type EvalRun = { + readonly "object": "eval.run" + readonly "id": string + readonly "eval_id": string + readonly "status": string + readonly "model": string | null + readonly "name": string | null + readonly "created_at": number + readonly "report_url": string + readonly "result_counts": { + readonly "total": number + readonly "errored": number + readonly "failed": number + readonly "passed": number + } + readonly "per_model_usage": + | ReadonlyArray< + { + readonly "model_name": string + readonly "invocation_count": number + readonly "prompt_tokens": number + readonly "completion_tokens": number + readonly "total_tokens": number + readonly "cached_tokens": number + } + > + | null + readonly "per_testing_criteria_results": + | ReadonlyArray<{ readonly "testing_criteria": string; readonly "passed": number; readonly "failed": number }> + | null + readonly "data_source": + | CreateEvalJsonlRunDataSource + | CreateEvalCompletionsRunDataSource + | CreateEvalResponsesRunDataSource + readonly "metadata": Metadata + readonly "error": EvalApiError | null +} +export const EvalRun = Schema.Struct({ + "object": Schema.Literal("eval.run").annotate({ "description": "The type of the object. Always \"eval.run\"." }), + "id": Schema.String.annotate({ "description": "Unique identifier for the evaluation run." }), + "eval_id": Schema.String.annotate({ "description": "The identifier of the associated evaluation." }), + "status": Schema.String.annotate({ "description": "The status of the evaluation run." }), + "model": Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "The model that is evaluated, if applicable." + }), + "name": Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The name of the evaluation run." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the evaluation run was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "report_url": Schema.String.annotate({ + "description": "The URL to the rendered evaluation run report on the UI dashboard.", + "format": "uri" + }), + "result_counts": Schema.Struct({ + "total": Schema.Number.annotate({ "description": "Total number of executed output items." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "errored": Schema.Number.annotate({ "description": "Number of output items that resulted in an error." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "failed": Schema.Number.annotate({ "description": "Number of output items that failed to pass the evaluation." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "passed": Schema.Number.annotate({ "description": "Number of output items that passed the evaluation." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + }).annotate({ "description": "Counters summarizing the outcomes of the evaluation run." }), + "per_model_usage": Schema.Union([ + Schema.Array(Schema.Struct({ + "model_name": Schema.String.annotate({ "description": "The name of the model." }), + "invocation_count": Schema.Number.annotate({ "description": "The number of invocations." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "prompt_tokens": Schema.Number.annotate({ "description": "The number of prompt tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "completion_tokens": Schema.Number.annotate({ "description": "The number of completion tokens generated." }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "total_tokens": Schema.Number.annotate({ "description": "The total number of tokens used." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "cached_tokens": Schema.Number.annotate({ "description": "The number of tokens retrieved from cache." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + })), + Schema.Null + ]).annotate({ "description": "Usage statistics for each model during the evaluation run." }), + "per_testing_criteria_results": Schema.Union([ + Schema.Array(Schema.Struct({ + "testing_criteria": Schema.String.annotate({ "description": "A description of the testing criteria." }), + "passed": Schema.Number.annotate({ "description": "Number of tests passed for this criteria." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "failed": Schema.Number.annotate({ "description": "Number of tests failed for this criteria." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + })), + Schema.Null + ]).annotate({ "description": "Results per testing criteria applied during the evaluation run." }), + "data_source": Schema.Union([ + CreateEvalJsonlRunDataSource, + CreateEvalCompletionsRunDataSource, + CreateEvalResponsesRunDataSource + ], { mode: "oneOf" }).annotate({ "description": "Information about the run's data source." }), + "metadata": Metadata, + "error": Schema.Union([EvalApiError, Schema.Null]) +}).annotate({ + "title": "EvalRun", + "description": "A schema representing an evaluation run.\n", + "identifier": "EvalRun" +}) +export type CreateEvalRunRequest = { + readonly "name"?: string + readonly "metadata"?: Metadata + readonly "data_source": + | CreateEvalJsonlRunDataSource + | CreateEvalCompletionsRunDataSource + | CreateEvalResponsesRunDataSource +} +export const CreateEvalRunRequest = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the run." })), + "metadata": Schema.optionalKey(Metadata), + "data_source": Schema.Union([ + CreateEvalJsonlRunDataSource, + CreateEvalCompletionsRunDataSource, + CreateEvalResponsesRunDataSource + ], { mode: "oneOf" }).annotate({ "description": "Details about the run's data source." }) +}).annotate({ "title": "CreateEvalRunRequest", "identifier": "CreateEvalRunRequest" }) +export type GraderMulti = { + readonly "type": "multi" + readonly "name": string + readonly "graders": { + readonly [x: string]: GraderStringCheck | GraderTextSimilarity | GraderPython | GraderScoreModel | GraderLabelModel + } + readonly "calculate_output": string +} +export const GraderMulti = Schema.Struct({ + "type": Schema.Literal("multi").annotate({ "description": "The object type, which is always `multi`." }), + "name": Schema.String.annotate({ "description": "The name of the grader." }), + "graders": Schema.Record( + Schema.String, + Schema.Union([GraderStringCheck, GraderTextSimilarity, GraderPython, GraderScoreModel, GraderLabelModel], { + mode: "oneOf" + }) + ).check(Schema.isMinProperties(1).annotate({ "expected": "a value with at least 1 entry" })), + "calculate_output": Schema.String.annotate({ + "description": "A formula to calculate the output based on grader results." + }) +}).annotate({ + "title": "MultiGrader", + "description": "A MultiGrader object combines the output of multiple graders to produce a single score.", + "identifier": "GraderMulti" +}) +export type ConversationItemList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "first_id": string + readonly "last_id": string +} +export const ConversationItemList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(ConversationItem).annotate({ "description": "A list of conversation items." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }), + "first_id": Schema.String.annotate({ "description": "The ID of the first item in the list." }), + "last_id": Schema.String.annotate({ "description": "The ID of the last item in the list." }) +}).annotate({ + "title": "The conversation item list", + "description": "A list of Conversation items.", + "identifier": "ConversationItemList" +}) +export type Response = { + readonly "metadata": Metadata + readonly "top_logprobs"?: number | null + readonly "temperature": number | null + readonly "top_p": number | null + readonly "user"?: string | null + readonly "safety_identifier"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: "in_memory" | "24h" | null + readonly "previous_response_id"?: string | null + readonly "model": ModelIdsResponses + readonly "background"?: boolean | null + readonly "max_tool_calls"?: number | null + readonly "text"?: ResponseTextParam + readonly "tools": ToolsArray + readonly "tool_choice": ToolChoiceParam + readonly "prompt"?: Prompt + readonly "service_tier"?: ServiceTierResponses + readonly "truncation"?: "auto" | "disabled" | null + readonly "id": string + readonly "object": "response" + readonly "status"?: "completed" | "failed" | "in_progress" | "cancelled" | "queued" | "incomplete" + readonly "access_programs"?: AccessProgramsBody | null + readonly "created_at": number + readonly "completed_at"?: number | null + readonly "error": ResponseError + readonly "incomplete_details": { + readonly "reason"?: "max_output_tokens" | "max_messages" | "content_filter" | "steered" + } | null + readonly "output": ReadonlyArray + readonly "reasoning"?: Reasoning | null + readonly "instructions": string | ReadonlyArray | null + readonly "output_text"?: string | null + readonly "usage"?: ResponseUsage | null + readonly "prompt_cache_options"?: PromptCacheOptions + readonly "prompt_cache_diagnostics"?: PromptCacheDiagnostics + readonly "moderation"?: Moderation | null + readonly "parallel_tool_calls": boolean + readonly "conversation"?: ResponseConversation | null + readonly "max_output_tokens"?: number | null +} +export const Response = Schema.Struct({ + "metadata": Metadata, + "top_logprobs": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" })), + Schema.Null + ]) + ), + "temperature": Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]), + "top_p": Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ]), + "user": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.\nA stable identifier for your end-users.\nUsed to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ), + "safety_identifier": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.\nThe IDs should be a string that uniquely identifies each user, with a maximum length of 128 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey(Schema.Union([ + Schema.Literals(["in_memory", "24h"]).annotate({ + "description": + "Deprecated. Use `prompt_cache_options.ttl` instead.\n\nThe retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).\nThis field expresses a maximum retention policy, while\n`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two\nfields are independent and do not interact.\nFor `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.\n\nFor older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:\n - Organizations without ZDR enabled default to `24h`.\n - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.\n" + }), + Schema.Null + ])), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.\n" + }), + Schema.Null + ])), + "model": Schema.suspend((): Schema.Codec => ModelIdsResponses).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra`. OpenAI\noffers a wide range of models with different capabilities, performance\ncharacteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)\nto browse and compare available models.\n" + }), + "background": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to run the model response in the background.\n[Learn more](https://developers.openai.com/api/docs/guides/background).\n" + }), + Schema.Null + ]) + ), + "max_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "text": Schema.optionalKey(ResponseTextParam), + "tools": ToolsArray, + "tool_choice": ToolChoiceParam, + "prompt": Schema.optionalKey(Prompt), + "service_tier": Schema.optionalKey(ServiceTierResponses), + "truncation": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "disabled"]).annotate({ + "description": + "The truncation strategy to use for the model response.\n- `auto`: If the input to this Response exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping items from the beginning of the conversation.\n- `disabled` (default): If the input size will exceed the context window\n size for a model, the request will fail with a 400 error.\n" + }), + Schema.Null + ])), + "id": Schema.String.annotate({ "description": "Unique identifier for this Response.\n" }), + "object": Schema.Literal("response").annotate({ + "description": "The object type of this resource - always set to `response`.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["completed", "failed", "in_progress", "cancelled", "queued", "incomplete"]).annotate({ + "description": + "The status of the response generation. One of `completed`, `failed`,\n`in_progress`, `cancelled`, `queued`, or `incomplete`.\n" + }) + ), + "access_programs": Schema.optionalKey(Schema.Union([AccessProgramsBody, Schema.Null])), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) of when this Response was created.\n", + "format": "unixtime" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "completed_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "Unix timestamp (in seconds) of when this Response was completed.\nOnly present when the status is `completed`.\n", + "format": "unixtime" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "error": ResponseError, + "incomplete_details": Schema.Union([ + Schema.Struct({ + "reason": Schema.optionalKey( + Schema.Literals(["max_output_tokens", "max_messages", "content_filter", "steered"]).annotate({ + "description": + "The reason why the response is incomplete. `steered` means\nthe response stopped at a safe output boundary after a\nWebSocket `response.steer` event. The server can then create\na successor response automatically with the queued input.\n" + }) + ) + }).annotate({ "description": "Details about why the response is incomplete.\n" }), + Schema.Null + ]), + "output": Schema.Array(OutputItem).annotate({ + "description": + "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n" + }), + "reasoning": Schema.optionalKey(Schema.Union([Reasoning, Schema.Null])), + "instructions": Schema.Union([ + Schema.Union([ + Schema.String.annotate({ + "description": "A text input to the model, equivalent to a text input with the\n`developer` role.\n" + }), + Schema.Array(InputItem).annotate({ + "title": "Input item list", + "description": "A list of one or many input items to the model, containing\ndifferent content types.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n" + }), + Schema.Null + ]), + "output_text": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n" + }), + Schema.Null + ]) + ), + "usage": Schema.optionalKey(Schema.Union([ResponseUsage, Schema.Null])), + "prompt_cache_options": Schema.optionalKey(PromptCacheOptions), + "prompt_cache_diagnostics": Schema.optionalKey(PromptCacheDiagnostics), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => Moderation).annotate({ + "description": + "Moderation results for the response input and output, if moderated completions were requested.\n" + }), + Schema.Null + ]) + ), + "parallel_tool_calls": Schema.Boolean.annotate({ + "description": "Whether to allow the model to run tool calls in parallel.\n" + }), + "conversation": Schema.optionalKey(Schema.Union([ResponseConversation, Schema.Null])), + "max_output_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) +}).annotate({ "title": "The response object", "identifier": "Response" }) +export type ResponseOutputItemAddedEvent = { + readonly "type": "response.output_item.added" + readonly "output_index": number + readonly "sequence_number": number + readonly "item": OutputItem +} +export const ResponseOutputItemAddedEvent = Schema.Struct({ + "type": Schema.Literal("response.output_item.added").annotate({ + "description": "The type of the event. Always `response.output_item.added`.\n" + }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was added.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item": Schema.suspend((): Schema.Codec => OutputItem).annotate({ + "description": + "The output item that was added. For reasoning items, `encrypted_content`\nmay be incomplete while the item is in progress. Use the reasoning item\nfrom the corresponding `response.output_item.done` event when passing it\nas input to a subsequent request.\n" + }) +}).annotate({ "description": "Emitted when a new output item is added.", "identifier": "ResponseOutputItemAddedEvent" }) +export type ResponseOutputItemDoneEvent = { + readonly "type": "response.output_item.done" + readonly "output_index": number + readonly "sequence_number": number + readonly "item": OutputItem +} +export const ResponseOutputItemDoneEvent = Schema.Struct({ + "type": Schema.Literal("response.output_item.done").annotate({ + "description": "The type of the event. Always `response.output_item.done`.\n" + }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was marked done.\n" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item": Schema.suspend((): Schema.Codec => OutputItem).annotate({ + "description": "The output item that was marked done.\n" + }) +}).annotate({ + "description": "Emitted when an output item is marked done.", + "identifier": "ResponseOutputItemDoneEvent" +}) +export type ResponseItemList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "first_id": string + readonly "last_id": string +} +export const ResponseItemList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(ItemResource).annotate({ "description": "A list of items used to generate this response." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }), + "first_id": Schema.String.annotate({ "description": "The ID of the first item in the list." }), + "last_id": Schema.String.annotate({ "description": "The ID of the last item in the list." }) +}).annotate({ "description": "A list of Response items.", "identifier": "ResponseItemList" }) +export type CompactResource = { + readonly "id": string + readonly "object": "response.compaction" + readonly "output": ReadonlyArray + readonly "created_at": number + readonly "usage": ResponseUsage +} +export const CompactResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique identifier for the compacted response." }), + "object": Schema.Literal("response.compaction").annotate({ + "description": "The object type. Always `response.compaction`." + }), + "output": Schema.Array(ItemField).annotate({ "description": "The compacted list of output items." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the compacted conversation was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "usage": Schema.suspend((): Schema.Codec => ResponseUsage).annotate({ + "description": "Token accounting for the compaction pass, including cached, reasoning, and total tokens." + }) +}).annotate({ "title": "The compacted response object", "identifier": "CompactResource" }) +export type CreateResponse = { + readonly "metadata"?: Metadata + readonly "top_logprobs"?: number + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "user"?: string | null + readonly "safety_identifier"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: "in_memory" | "24h" | null + readonly "prompt_cache_options"?: { + readonly "ttl"?: PromptCacheTTLEnum + readonly "mode"?: PromptCacheModeEnum + readonly "prewarm"?: boolean + readonly "comparison_response_id"?: string | null + } + readonly "previous_response_id"?: string | null + readonly "model"?: ModelIdsResponses + readonly "background"?: boolean | null + readonly "max_tool_calls"?: number | null + readonly "text"?: ResponseTextParam + readonly "tools"?: ToolsArray + readonly "tool_choice"?: ToolChoiceParam + readonly "prompt"?: Prompt + readonly "access_programs"?: AccessProgramsParam + readonly "service_tier"?: ServiceTierResponses + readonly "truncation"?: "auto" | "disabled" | null + readonly "reasoning"?: Reasoning | null + readonly "input"?: InputParam + readonly "include"?: ReadonlyArray | null + readonly "parallel_tool_calls"?: boolean | null + readonly "store"?: boolean | null + readonly "instructions"?: string | null + readonly "moderation"?: ModerationParam | null + readonly "stream"?: boolean | null + readonly "stream_options"?: ResponseStreamOptions + readonly "conversation"?: ConversationParam | null + readonly "context_management"?: ReadonlyArray | null + readonly "max_output_tokens"?: number | null +} +export const CreateResponse = Schema.Struct({ + "metadata": Schema.optionalKey(Metadata), + "top_logprobs": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" })).check( + Schema.makeFilterGroup([ + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }), + Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" }) + ]).annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }) + ) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ])), + "user": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.\nA stable identifier for your end-users.\nUsed to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ), + "safety_identifier": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.\nThe IDs should be a string that uniquely identifies each user, with a maximum length of 128 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey(Schema.Union([ + Schema.Literals(["in_memory", "24h"]).annotate({ + "description": + "Deprecated. Use `prompt_cache_options.ttl` instead.\n\nThe retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).\nThis field expresses a maximum retention policy, while\n`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two\nfields are independent and do not interact.\nFor `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.\n\nFor older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:\n - Organizations without ZDR enabled default to `24h`.\n - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.\n" + }), + Schema.Null + ])), + "prompt_cache_options": Schema.optionalKey(Schema.Struct({ + "ttl": Schema.optionalKey( + Schema.suspend((): Schema.Codec => PromptCacheTTLEnum).annotate({ + "description": + "The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer." + }) + ), + "mode": Schema.optionalKey( + Schema.suspend((): Schema.Codec => PromptCacheModeEnum).annotate({ + "description": + "Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching." + }) + ), + "prewarm": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Prepares the prompt cache without generating output. Defaults to `false`. When set to `true`, overrides the `generate` field to `false`." + }) + ), + "comparison_response_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled." + }), + Schema.Null + ]) + ) + })), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.\n" + }), + Schema.Null + ])), + "model": Schema.optionalKey( + Schema.suspend((): Schema.Codec => ModelIdsResponses).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra`. OpenAI\noffers a wide range of models with different capabilities, performance\ncharacteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)\nto browse and compare available models.\n" + }) + ), + "background": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to run the model response in the background.\n[Learn more](https://developers.openai.com/api/docs/guides/background).\n" + }), + Schema.Null + ]) + ), + "max_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "text": Schema.optionalKey(ResponseTextParam), + "tools": Schema.optionalKey(ToolsArray), + "tool_choice": Schema.optionalKey(ToolChoiceParam), + "prompt": Schema.optionalKey(Prompt), + "access_programs": Schema.optionalKey(AccessProgramsParam), + "service_tier": Schema.optionalKey(ServiceTierResponses), + "truncation": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "disabled"]).annotate({ + "description": + "The truncation strategy to use for the model response.\n- `auto`: If the input to this Response exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping items from the beginning of the conversation.\n- `disabled` (default): If the input size will exceed the context window\n size for a model, the request will fail with a 400 error.\n" + }), + Schema.Null + ])), + "reasoning": Schema.optionalKey(Schema.Union([Reasoning, Schema.Null])), + "input": Schema.optionalKey(InputParam), + "include": Schema.optionalKey(Schema.Union([ + Schema.Array(IncludeEnum).annotate({ + "description": + "Specify additional output data to include in the model response. Currently supported values are:\n- `web_search_call.action.sources`: Include the sources of the web search tool call.\n- `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n- `file_search_call.results`: Include the search results of the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `message.output_text.logprobs`: Include logprobs with assistant messages.\n- `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program)." + }), + Schema.Null + ])), + "parallel_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ "description": "Whether to allow the model to run tool calls in parallel.\n" }), + Schema.Null + ]) + ), + "store": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to store the generated model response for later retrieval via\nAPI.\nDefaults to true when omitted.\nIf set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).\n" + }), + Schema.Null + ])), + "instructions": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n" + }), + Schema.Null + ])), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => ModerationParam).annotate({ + "description": "Configuration for running moderation on the input and output of this response.\n" + }), + Schema.Null + ]) + ), + "stream": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)\nfor more information.\n" + }), + Schema.Null + ])), + "stream_options": Schema.optionalKey(ResponseStreamOptions), + "conversation": Schema.optionalKey(Schema.Union([ConversationParam, Schema.Null])), + "context_management": Schema.optionalKey( + Schema.Union([ + Schema.Array(ContextManagementParam).annotate({ + "description": "Context management configuration for this request.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + Schema.Null + ]) + ), + "max_output_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(16).annotate({ "expected": "a value greater than or equal to 16" }) + ), + Schema.Null + ]) + ) +}).annotate({ "identifier": "CreateResponse" }) +export type LiveMediaSessionCreateParams = { + readonly "model": ModelIdsLive + readonly "instructions"?: Instructions + readonly "audio"?: LiveMediaSessionAudioParam + readonly "delegation"?: Delegation + readonly "store"?: Store + readonly "input"?: Input + readonly "client"?: LiveClientConfigParam +} +export const LiveMediaSessionCreateParams = Schema.Struct({ + "model": ModelIdsLive, + "instructions": Schema.optionalKey(Instructions), + "audio": Schema.optionalKey( + Schema.suspend((): Schema.Codec => LiveMediaSessionAudioParam).annotate({ + "description": "Startup audio configuration. WebRTC and SIP negotiate their audio format on the media transport." + }) + ), + "delegation": Schema.optionalKey(Delegation), + "store": Schema.optionalKey(Store), + "input": Schema.optionalKey(Input), + "client": Schema.optionalKey(LiveClientConfigParam) +}).annotate({ + "description": + "Startup configuration for a Live media session. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend instructions and the backend prompt under delegation.responses.instructions.", + "identifier": "LiveMediaSessionCreateParams" +}) +export type LiveCallAcceptSession = { + readonly "model": string | "gpt-live-1" + readonly "instructions"?: Instructions + readonly "audio"?: LiveMediaSessionAudioParam + readonly "delegation"?: Delegation + readonly "store"?: Store + readonly "input"?: Input + readonly "type": "live" +} +export const LiveCallAcceptSession = Schema.Struct({ + "model": Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" }) + ), + Schema.Literal("gpt-live-1") + ]).annotate({ "description": "The Live model to use for the accepted call." }), + "instructions": Schema.optionalKey(Instructions), + "audio": Schema.optionalKey( + Schema.suspend((): Schema.Codec => LiveMediaSessionAudioParam).annotate({ + "description": + "Startup audio output configuration. SIP negotiates the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup." + }) + ), + "delegation": Schema.optionalKey(Delegation), + "store": Schema.optionalKey(Store), + "input": Schema.optionalKey(Input), + "type": Schema.Literal("live").annotate({ "description": "The session type. Always `live`." }) +}).annotate({ + "description": + "Startup configuration for accepting an incoming Live SIP call. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend and backend instructions.", + "identifier": "LiveCallAcceptSession" +}) +export type LiveForkRequest = { + readonly "session"?: LiveMediaSessionForkParams + readonly "transport": LiveWebRTCTransport +} +export const LiveForkRequest = Schema.Struct({ + "session": Schema.optionalKey( + Schema.suspend((): Schema.Codec => LiveMediaSessionForkParams).annotate({ + "description": + "Optional configuration overrides for the new Live session. Omit this object or send an empty object to inherit the stored session's settings." + }) + ), + "transport": Schema.suspend((): Schema.Codec => LiveWebRTCTransport).annotate({ + "description": "WebRTC transport with an SDP offer for the new connection to the forked session." + }) +}).annotate({ + "description": + "Fork a stored Live session onto a new WebRTC connection. Omit session or send an empty object to inherit its configuration.", + "identifier": "LiveForkRequest" +}) +export type SessionEvent = + | SessionEventError + | SessionEventAgentSessionEnvironmentReady + | SessionEventAgentSessionEnvironmentSuspended + | SessionEventAgentSessionEnvironmentExpired + | SessionEventAgentSessionEnvironmentReset + | SessionEventAgentOutputCommandExecutionOutputDelta + | SessionEventAgentSessionCreated + | SessionEventAgentSessionTurnCreated + | SessionEventAgentSessionTurnInProgress + | SessionEventAgentSessionTurnCompleted + | SessionEventAgentSessionTurnFailed + | SessionEventAgentSessionTurnCancelled + | SessionEventAgentSessionTurnItemAdded + | SessionEventAgentSessionIdle + | SessionEventAgentSessionInProgress + | SessionEventAgentSessionRequiresAction + | SessionEventAgentSessionFailed + | SessionEventAgentSessionEnvironmentPending + | SessionEventAgentSessionEnvironmentConnected + | SessionEventAgentSessionEnvironmentDisconnected + | SessionEventAgentSessionEnvironmentFailed + | SessionEventAgentSessionSubagentCreated + | SessionEventAgentSessionSubagentActive + | SessionEventAgentSessionSubagentClosed + | SessionEventAgentSessionTurnItemDone + | SessionEventAgentSessionTurnContentPartAdded + | SessionEventAgentSessionTurnContentPartDone + | SessionEventAgentSessionTurnOutputTextDelta + | SessionEventAgentSessionTurnOutputTextDone + | SessionEventAgentSessionTurnReasoningSummaryPartAdded + | SessionEventAgentSessionTurnReasoningSummaryPartDone + | SessionEventAgentSessionTurnReasoningSummaryTextDelta + | SessionEventAgentSessionTurnReasoningSummaryTextDone +export const SessionEvent = Schema.Union([ + SessionEventError, + SessionEventAgentSessionEnvironmentReady, + SessionEventAgentSessionEnvironmentSuspended, + SessionEventAgentSessionEnvironmentExpired, + SessionEventAgentSessionEnvironmentReset, + SessionEventAgentOutputCommandExecutionOutputDelta, + SessionEventAgentSessionCreated, + SessionEventAgentSessionTurnCreated, + SessionEventAgentSessionTurnInProgress, + SessionEventAgentSessionTurnCompleted, + SessionEventAgentSessionTurnFailed, + SessionEventAgentSessionTurnCancelled, + SessionEventAgentSessionTurnItemAdded, + SessionEventAgentSessionIdle, + SessionEventAgentSessionInProgress, + SessionEventAgentSessionRequiresAction, + SessionEventAgentSessionFailed, + SessionEventAgentSessionEnvironmentPending, + SessionEventAgentSessionEnvironmentConnected, + SessionEventAgentSessionEnvironmentDisconnected, + SessionEventAgentSessionEnvironmentFailed, + SessionEventAgentSessionSubagentCreated, + SessionEventAgentSessionSubagentActive, + SessionEventAgentSessionSubagentClosed, + SessionEventAgentSessionTurnItemDone, + SessionEventAgentSessionTurnContentPartAdded, + SessionEventAgentSessionTurnContentPartDone, + SessionEventAgentSessionTurnOutputTextDelta, + SessionEventAgentSessionTurnOutputTextDone, + SessionEventAgentSessionTurnReasoningSummaryPartAdded, + SessionEventAgentSessionTurnReasoningSummaryPartDone, + SessionEventAgentSessionTurnReasoningSummaryTextDelta, + SessionEventAgentSessionTurnReasoningSummaryTextDone +], { mode: "oneOf" }).annotate({ + "description": "An event emitted by a Managed Agents session.", + "identifier": "SessionEvent" +}) +export type BetaResponse = { + readonly "metadata": BetaMetadata + readonly "top_logprobs"?: number | null + readonly "temperature": number | null + readonly "top_p": number | null + readonly "user"?: string | null + readonly "safety_identifier"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: "in_memory" | "24h" | null + readonly "previous_response_id"?: string | null + readonly "model": BetaModelIdsResponses + readonly "background"?: boolean | null + readonly "max_tool_calls"?: number | null + readonly "text"?: BetaResponseTextParam + readonly "tools": BetaToolsArray + readonly "tool_choice": BetaToolChoiceParam + readonly "prompt"?: BetaPrompt + readonly "service_tier"?: BetaServiceTierResponses + readonly "truncation"?: "auto" | "disabled" | null + readonly "id": string + readonly "object": "response" + readonly "status"?: "completed" | "failed" | "in_progress" | "cancelled" | "queued" | "incomplete" + readonly "access_programs": BetaAccessProgramsBody | null + readonly "created_at": number + readonly "completed_at"?: number | null + readonly "error": BetaResponseError + readonly "incomplete_details": { + readonly "reason"?: "max_output_tokens" | "max_messages" | "content_filter" | "steered" + } | null + readonly "output": ReadonlyArray + readonly "reasoning"?: BetaReasoning | null + readonly "instructions": string | ReadonlyArray | null + readonly "output_text"?: string | null + readonly "usage"?: BetaResponseUsage | null + readonly "prompt_cache_options"?: BetaPromptCacheOptions + readonly "prompt_cache_diagnostics"?: BetaPromptCacheDiagnostics + readonly "moderation"?: BetaModeration | null + readonly "parallel_tool_calls": boolean + readonly "conversation"?: BetaResponseConversation | null + readonly "max_output_tokens"?: number | null +} +export const BetaResponse = Schema.Struct({ + "metadata": BetaMetadata, + "top_logprobs": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" })), + Schema.Null + ]) + ), + "temperature": Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]), + "top_p": Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ]), + "user": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.\nA stable identifier for your end-users.\nUsed to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ), + "safety_identifier": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.\nThe IDs should be a string that uniquely identifies each user, with a maximum length of 128 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey(Schema.Union([ + Schema.Literals(["in_memory", "24h"]).annotate({ + "description": + "Deprecated. Use `prompt_cache_options.ttl` instead.\n\nThe retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).\nThis field expresses a maximum retention policy, while\n`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two\nfields are independent and do not interact.\nFor `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.\n\nFor older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:\n - Organizations without ZDR enabled default to `24h`.\n - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.\n" + }), + Schema.Null + ])), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.\n" + }), + Schema.Null + ])), + "model": Schema.suspend((): Schema.Codec => BetaModelIdsResponses).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra`. OpenAI\noffers a wide range of models with different capabilities, performance\ncharacteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)\nto browse and compare available models.\n" + }), + "background": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to run the model response in the background.\n[Learn more](https://developers.openai.com/api/docs/guides/background).\n" + }), + Schema.Null + ]) + ), + "max_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "text": Schema.optionalKey(BetaResponseTextParam), + "tools": BetaToolsArray, + "tool_choice": BetaToolChoiceParam, + "prompt": Schema.optionalKey(BetaPrompt), + "service_tier": Schema.optionalKey(BetaServiceTierResponses), + "truncation": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "disabled"]).annotate({ + "description": + "The truncation strategy to use for the model response.\n- `auto`: If the input to this Response exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping items from the beginning of the conversation.\n- `disabled` (default): If the input size will exceed the context window\n size for a model, the request will fail with a 400 error.\n" + }), + Schema.Null + ])), + "id": Schema.String.annotate({ "description": "Unique identifier for this Response.\n" }), + "object": Schema.Literal("response").annotate({ + "description": "The object type of this resource - always set to `response`.\n" + }), + "status": Schema.optionalKey( + Schema.Literals(["completed", "failed", "in_progress", "cancelled", "queued", "incomplete"]).annotate({ + "description": + "The status of the response generation. One of `completed`, `failed`,\n`in_progress`, `cancelled`, `queued`, or `incomplete`.\n" + }) + ), + "access_programs": Schema.Union([BetaAccessProgramsBody, Schema.Null]), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) of when this Response was created.\n", + "format": "unixtime" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + "completed_at": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "Unix timestamp (in seconds) of when this Response was completed.\nOnly present when the status is `completed`.\n", + "format": "unixtime" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })), + Schema.Null + ]) + ), + "error": BetaResponseError, + "incomplete_details": Schema.Union([ + Schema.Struct({ + "reason": Schema.optionalKey( + Schema.Literals(["max_output_tokens", "max_messages", "content_filter", "steered"]).annotate({ + "description": + "The reason why the response is incomplete. `steered` means\nthe response stopped at a safe output boundary after a\nWebSocket `response.steer` event. The server can then create\na successor response automatically with the queued input.\n" + }) + ) + }).annotate({ "description": "Details about why the response is incomplete.\n" }), + Schema.Null + ]), + "output": Schema.Array(BetaOutputItem).annotate({ + "description": + "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n" + }), + "reasoning": Schema.optionalKey(Schema.Union([BetaReasoning, Schema.Null])), + "instructions": Schema.Union([ + Schema.Union([ + Schema.String.annotate({ + "description": "A text input to the model, equivalent to a text input with the\n`developer` role.\n" + }), + Schema.Array(BetaInputItem).annotate({ + "title": "Input item list", + "description": "A list of one or many input items to the model, containing\ndifferent content types.\n" + }) + ], { mode: "oneOf" }).annotate({ + "description": + "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n" + }), + Schema.Null + ]), + "output_text": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n" + }), + Schema.Null + ]) + ), + "usage": Schema.optionalKey(Schema.Union([BetaResponseUsage, Schema.Null])), + "prompt_cache_options": Schema.optionalKey(BetaPromptCacheOptions), + "prompt_cache_diagnostics": Schema.optionalKey(BetaPromptCacheDiagnostics), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaModeration).annotate({ + "description": + "Moderation results for the response input and output, if moderated completions were requested.\n" + }), + Schema.Null + ]) + ), + "parallel_tool_calls": Schema.Boolean.annotate({ + "description": "Whether to allow the model to run tool calls in parallel.\n" + }), + "conversation": Schema.optionalKey(Schema.Union([BetaResponseConversation, Schema.Null])), + "max_output_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ) +}).annotate({ "title": "The response object", "identifier": "BetaResponse" }) +export type BetaResponseOutputItemAddedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.output_item.added" + readonly "output_index": number + readonly "sequence_number": number + readonly "item": BetaOutputItem +} +export const BetaResponseOutputItemAddedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.output_item.added").annotate({ + "description": "The type of the event. Always `response.output_item.added`.\n" + }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was added.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item": Schema.suspend((): Schema.Codec => BetaOutputItem).annotate({ + "description": + "The output item that was added. For reasoning items, `encrypted_content`\nmay be incomplete while the item is in progress. Use the reasoning item\nfrom the corresponding `response.output_item.done` event when passing it\nas input to a subsequent request.\n" + }) +}).annotate({ + "description": "Emitted when a new output item is added.", + "identifier": "BetaResponseOutputItemAddedEvent" +}) +export type BetaResponseOutputItemDoneEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.output_item.done" + readonly "output_index": number + readonly "sequence_number": number + readonly "item": BetaOutputItem +} +export const BetaResponseOutputItemDoneEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.output_item.done").annotate({ + "description": "The type of the event. Always `response.output_item.done`.\n" + }), + "output_index": Schema.Number.annotate({ "description": "The index of the output item that was marked done.\n" }) + .check(Schema.isInt().annotate({ "expected": "an integer" })), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event.\n" }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "item": Schema.suspend((): Schema.Codec => BetaOutputItem).annotate({ + "description": "The output item that was marked done.\n" + }) +}).annotate({ + "description": "Emitted when an output item is marked done.", + "identifier": "BetaResponseOutputItemDoneEvent" +}) +export type BetaCompactResource = { + readonly "id": string + readonly "object": "response.compaction" + readonly "output": ReadonlyArray + readonly "created_at": number + readonly "usage": BetaResponseUsage +} +export const BetaCompactResource = Schema.Struct({ + "id": Schema.String.annotate({ "description": "The unique identifier for the compacted response." }), + "object": Schema.Literal("response.compaction").annotate({ + "description": "The object type. Always `response.compaction`." + }), + "output": Schema.Array(BetaItemField).annotate({ "description": "The compacted list of output items." }), + "created_at": Schema.Number.annotate({ + "description": "Unix timestamp (in seconds) when the compacted conversation was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "usage": Schema.suspend((): Schema.Codec => BetaResponseUsage).annotate({ + "description": "Token accounting for the compaction pass, including cached, reasoning, and total tokens." + }) +}).annotate({ "title": "The compacted response object", "identifier": "BetaCompactResource" }) +export type BetaResponseItemList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "first_id": string + readonly "last_id": string +} +export const BetaResponseItemList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The type of object returned, must be `list`." }), + "data": Schema.Array(BetaItemResource).annotate({ "description": "A list of items used to generate this response." }), + "has_more": Schema.Boolean.annotate({ "description": "Whether there are more items available." }), + "first_id": Schema.String.annotate({ "description": "The ID of the first item in the list." }), + "last_id": Schema.String.annotate({ "description": "The ID of the last item in the list." }) +}).annotate({ "description": "A list of Response items.", "identifier": "BetaResponseItemList" }) +export type BetaCreateResponse = { + readonly "metadata"?: BetaMetadata + readonly "top_logprobs"?: number + readonly "temperature"?: number | null + readonly "top_p"?: number | null + readonly "user"?: string | null + readonly "safety_identifier"?: string | null + readonly "prompt_cache_key"?: string | null + readonly "prompt_cache_retention"?: "in_memory" | "24h" | null + readonly "prompt_cache_options"?: { + readonly "ttl"?: BetaPromptCacheTTLEnum + readonly "mode"?: BetaPromptCacheModeEnum + readonly "prewarm"?: boolean + readonly "comparison_response_id"?: string | null + } + readonly "previous_response_id"?: string | null + readonly "model"?: BetaModelIdsResponses + readonly "background"?: boolean | null + readonly "max_tool_calls"?: number | null + readonly "text"?: BetaResponseTextParam + readonly "tools"?: BetaToolsArray + readonly "tool_choice"?: BetaToolChoiceParam + readonly "prompt"?: BetaPrompt + readonly "access_programs"?: BetaAccessProgramsParam + readonly "service_tier"?: BetaServiceTierResponses + readonly "truncation"?: "auto" | "disabled" | null + readonly "reasoning"?: BetaReasoning | null + readonly "input"?: BetaInputParam + readonly "include"?: ReadonlyArray | null + readonly "parallel_tool_calls"?: boolean | null + readonly "store"?: boolean | null + readonly "instructions"?: string | null + readonly "moderation"?: BetaModerationParam | null + readonly "stream"?: boolean | null + readonly "stream_options"?: BetaResponseStreamOptions + readonly "conversation"?: BetaConversationParam | null + readonly "context_management"?: ReadonlyArray | null + readonly "max_output_tokens"?: number | null + readonly "multi_agent"?: BetaMultiAgentParam | null +} +export const BetaCreateResponse = Schema.Struct({ + "metadata": Schema.optionalKey(BetaMetadata), + "top_logprobs": Schema.optionalKey( + Schema.Number.annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" })).check( + Schema.makeFilterGroup([ + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }), + Schema.isLessThanOrEqualTo(20).annotate({ "expected": "a value less than or equal to 20" }) + ]).annotate({ + "description": + "An integer between 0 and 20 specifying the maximum number of most likely\ntokens to return at each token position, each with an associated log\nprobability. In some cases, the number of returned tokens may be fewer than\nrequested.\n" + }) + ) + ), + "temperature": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(2).annotate({ "expected": "a value less than or equal to 2" })), + Schema.Null + ]) + ), + "top_p": Schema.optionalKey(Schema.Union([ + Schema.Number.annotate({ + "description": + "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }).check(Schema.isFinite().annotate({ "expected": "a finite number" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1).annotate({ "expected": "a value less than or equal to 1" })), + Schema.Null + ])), + "user": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.\nA stable identifier for your end-users.\nUsed to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }) + ), + "safety_identifier": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.\nThe IDs should be a string that uniquely identifies each user, with a maximum length of 128 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).\n" + }).check(Schema.isMaxCodePoints(128).annotate({ "expected": "a string with at most 128 code points" })), + Schema.Null + ])), + "prompt_cache_key": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).\n" + }), + Schema.Null + ]) + ), + "prompt_cache_retention": Schema.optionalKey(Schema.Union([ + Schema.Literals(["in_memory", "24h"]).annotate({ + "description": + "Deprecated. Use `prompt_cache_options.ttl` instead.\n\nThe retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).\nThis field expresses a maximum retention policy, while\n`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two\nfields are independent and do not interact.\nFor `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.\n\nFor older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:\n - Organizations without ZDR enabled default to `24h`.\n - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.\n" + }), + Schema.Null + ])), + "prompt_cache_options": Schema.optionalKey(Schema.Struct({ + "ttl": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaPromptCacheTTLEnum).annotate({ + "description": + "The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer." + }) + ), + "mode": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaPromptCacheModeEnum).annotate({ + "description": + "Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching." + }) + ), + "prewarm": Schema.optionalKey( + Schema.Boolean.annotate({ + "description": + "Prepares the prompt cache without generating output. Defaults to `false`. When set to `true`, overrides the `generate` field to `false`." + }) + ), + "comparison_response_id": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled." + }), + Schema.Null + ]) + ) + })), + "previous_response_id": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.\n" + }), + Schema.Null + ])), + "model": Schema.optionalKey( + Schema.suspend((): Schema.Codec => BetaModelIdsResponses).annotate({ + "description": + "Model ID used to generate the response, like `gpt-6-astra`. OpenAI\noffers a wide range of models with different capabilities, performance\ncharacteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)\nto browse and compare available models.\n" + }) + ), + "background": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to run the model response in the background.\n[Learn more](https://developers.openai.com/api/docs/guides/background).\n" + }), + Schema.Null + ]) + ), + "max_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored.\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "text": Schema.optionalKey(BetaResponseTextParam), + "tools": Schema.optionalKey(BetaToolsArray), + "tool_choice": Schema.optionalKey(BetaToolChoiceParam), + "prompt": Schema.optionalKey(BetaPrompt), + "access_programs": Schema.optionalKey(BetaAccessProgramsParam), + "service_tier": Schema.optionalKey(BetaServiceTierResponses), + "truncation": Schema.optionalKey(Schema.Union([ + Schema.Literals(["auto", "disabled"]).annotate({ + "description": + "The truncation strategy to use for the model response.\n- `auto`: If the input to this Response exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping items from the beginning of the conversation.\n- `disabled` (default): If the input size will exceed the context window\n size for a model, the request will fail with a 400 error.\n" + }), + Schema.Null + ])), + "reasoning": Schema.optionalKey(Schema.Union([BetaReasoning, Schema.Null])), + "input": Schema.optionalKey(BetaInputParam), + "include": Schema.optionalKey(Schema.Union([ + Schema.Array(BetaIncludeEnum).annotate({ + "description": + "Specify additional output data to include in the model response. Currently supported values are:\n- `web_search_call.action.sources`: Include the sources of the web search tool call.\n- `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n- `file_search_call.results`: Include the search results of the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `message.output_text.logprobs`: Include logprobs with assistant messages.\n- `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program)." + }), + Schema.Null + ])), + "parallel_tool_calls": Schema.optionalKey( + Schema.Union([ + Schema.Boolean.annotate({ "description": "Whether to allow the model to run tool calls in parallel.\n" }), + Schema.Null + ]) + ), + "store": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "Whether to store the generated model response for later retrieval via\nAPI.\nDefaults to true when omitted.\nIf set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).\n" + }), + Schema.Null + ])), + "instructions": Schema.optionalKey(Schema.Union([ + Schema.String.annotate({ + "description": + "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n" + }), + Schema.Null + ])), + "moderation": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaModerationParam).annotate({ + "description": "Configuration for running moderation on the input and output of this response.\n" + }), + Schema.Null + ]) + ), + "stream": Schema.optionalKey(Schema.Union([ + Schema.Boolean.annotate({ + "description": + "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)\nfor more information.\n" + }), + Schema.Null + ])), + "stream_options": Schema.optionalKey(BetaResponseStreamOptions), + "conversation": Schema.optionalKey(Schema.Union([BetaConversationParam, Schema.Null])), + "context_management": Schema.optionalKey( + Schema.Union([ + Schema.Array(BetaContextManagementParam).annotate({ + "description": "Context management configuration for this request.\n" + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })), + Schema.Null + ]) + ), + "max_output_tokens": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).\n" + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(16).annotate({ "expected": "a value greater than or equal to 16" }) + ), + Schema.Null + ]) + ), + "multi_agent": Schema.optionalKey( + Schema.Union([ + Schema.suspend((): Schema.Codec => BetaMultiAgentParam).annotate({ + "description": "Configuration for server-hosted multi-agent execution." + }), + Schema.Null + ]) + ) +}).annotate({ "identifier": "BetaCreateResponse" }) +export type EvalList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const EvalList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ + "description": "The type of this object. It is always set to \"list\".\n" + }), + "data": Schema.Array(Eval).annotate({ "description": "An array of eval objects.\n" }), + "first_id": Schema.String.annotate({ "description": "The identifier of the first eval in the data array." }), + "last_id": Schema.String.annotate({ "description": "The identifier of the last eval in the data array." }), + "has_more": Schema.Boolean.annotate({ "description": "Indicates whether there are more evals available." }) +}).annotate({ + "title": "EvalList", + "description": "An object representing a list of evals.\n", + "identifier": "EvalList" +}) +export type CreateEvalRequest = { + readonly "name"?: string + readonly "metadata"?: Metadata + readonly "data_source_config": + | CreateEvalCustomDataSourceConfig + | CreateEvalLogsDataSourceConfig + | CreateEvalStoredCompletionsDataSourceConfig + readonly "testing_criteria": ReadonlyArray< + | CreateEvalLabelModelGrader + | EvalGraderStringCheck + | EvalGraderTextSimilarity + | EvalGraderPython + | EvalGraderScoreModel + > +} +export const CreateEvalRequest = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "The name of the evaluation." })), + "metadata": Schema.optionalKey(Metadata), + "data_source_config": Schema.Union([ + CreateEvalCustomDataSourceConfig, + CreateEvalLogsDataSourceConfig, + CreateEvalStoredCompletionsDataSourceConfig + ], { mode: "oneOf" }).annotate({ + "description": + "The configuration for the data source used for the evaluation runs. Dictates the schema of the data used in the evaluation." + }), + "testing_criteria": Schema.Array( + Schema.Union([ + CreateEvalLabelModelGrader, + EvalGraderStringCheck, + EvalGraderTextSimilarity, + EvalGraderPython, + EvalGraderScoreModel + ], { mode: "oneOf" }) + ).annotate({ + "description": + "A list of graders for all eval runs in this group. Graders can reference variables in the data source using double curly braces notation, like `{{item.variable_name}}`. To reference the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`)." + }) +}).annotate({ "title": "CreateEvalRequest", "identifier": "CreateEvalRequest" }) +export type EvalRunList = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string + readonly "last_id": string + readonly "has_more": boolean +} +export const EvalRunList = Schema.Struct({ + "object": Schema.Literal("list").annotate({ + "description": "The type of this object. It is always set to \"list\".\n" + }), + "data": Schema.Array(EvalRun).annotate({ "description": "An array of eval run objects.\n" }), + "first_id": Schema.String.annotate({ "description": "The identifier of the first eval run in the data array." }), + "last_id": Schema.String.annotate({ "description": "The identifier of the last eval run in the data array." }), + "has_more": Schema.Boolean.annotate({ "description": "Indicates whether there are more evals available." }) +}).annotate({ + "title": "EvalRunList", + "description": "An object representing a list of runs for an evaluation.\n", + "identifier": "EvalRunList" +}) +export type RunGraderRequest = { + readonly "grader": GraderStringCheck | GraderTextSimilarity | GraderPython | GraderScoreModel | GraderMulti + readonly "item"?: { readonly [x: string]: Schema.Json } + readonly "model_sample": string +} +export const RunGraderRequest = Schema.Struct({ + "grader": Schema.Union([GraderStringCheck, GraderTextSimilarity, GraderPython, GraderScoreModel, GraderMulti], { + mode: "oneOf" + }).annotate({ "description": "The grader used for the fine-tuning job." }), + "item": Schema.optionalKey( + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).annotate({ + "description": + "The dataset item provided to the grader. This will be used to populate\nthe `item` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.\n" + }) + ), + "model_sample": Schema.String.annotate({ + "description": + "The model sample to be evaluated. This value will be used to populate\nthe `sample` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.\nThe `output_json` variable will be populated if the model sample is a\nvalid JSON string.\n" + }) +}).annotate({ "title": "RunGraderRequest", "identifier": "RunGraderRequest" }) +export type ValidateGraderRequest = { + readonly "grader": GraderStringCheck | GraderTextSimilarity | GraderPython | GraderScoreModel | GraderMulti +} +export const ValidateGraderRequest = Schema.Struct({ + "grader": Schema.Union([GraderStringCheck, GraderTextSimilarity, GraderPython, GraderScoreModel, GraderMulti], { + mode: "oneOf" + }).annotate({ "description": "The grader used for the fine-tuning job." }) +}).annotate({ "title": "ValidateGraderRequest", "identifier": "ValidateGraderRequest" }) +export type ValidateGraderResponse = { + readonly "grader"?: GraderStringCheck | GraderTextSimilarity | GraderPython | GraderScoreModel | GraderMulti +} +export const ValidateGraderResponse = Schema.Struct({ + "grader": Schema.optionalKey( + Schema.Union([GraderStringCheck, GraderTextSimilarity, GraderPython, GraderScoreModel, GraderMulti], { + mode: "oneOf" + }).annotate({ "description": "The grader used for the fine-tuning job." }) + ) +}).annotate({ "title": "ValidateGraderResponse", "identifier": "ValidateGraderResponse" }) +export type FineTuneReinforcementMethod = { + readonly "grader": GraderStringCheck | GraderTextSimilarity | GraderPython | GraderScoreModel | GraderMulti + readonly "hyperparameters"?: FineTuneReinforcementHyperparameters +} +export const FineTuneReinforcementMethod = Schema.Struct({ + "grader": Schema.Union([GraderStringCheck, GraderTextSimilarity, GraderPython, GraderScoreModel, GraderMulti], { + mode: "oneOf" + }).annotate({ "description": "The grader used for the fine-tuning job." }), + "hyperparameters": Schema.optionalKey(FineTuneReinforcementHyperparameters) +}).annotate({ + "description": "Configuration for the reinforcement fine-tuning method.", + "identifier": "FineTuneReinforcementMethod" +}) +export type ResponseCompletedEvent = { + readonly "type": "response.completed" + readonly "response": Response + readonly "sequence_number": number +} +export const ResponseCompletedEvent = Schema.Struct({ + "type": Schema.Literal("response.completed").annotate({ + "description": "The type of the event. Always `response.completed`.\n" + }), + "response": Schema.suspend((): Schema.Codec => Response).annotate({ + "description": "Properties of the completed response.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when the model response is complete.", "identifier": "ResponseCompletedEvent" }) +export type ResponseCreatedEvent = { + readonly "type": "response.created" + readonly "response": Response + readonly "sequence_number": number +} +export const ResponseCreatedEvent = Schema.Struct({ + "type": Schema.Literal("response.created").annotate({ + "description": "The type of the event. Always `response.created`.\n" + }), + "response": Schema.suspend((): Schema.Codec => Response).annotate({ + "description": "The response that was created.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "An event that is emitted when a response is created.\n", + "identifier": "ResponseCreatedEvent" +}) +export type ResponseInProgressEvent = { + readonly "type": "response.in_progress" + readonly "response": Response + readonly "sequence_number": number +} +export const ResponseInProgressEvent = Schema.Struct({ + "type": Schema.Literal("response.in_progress").annotate({ + "description": "The type of the event. Always `response.in_progress`.\n" + }), + "response": Schema.suspend((): Schema.Codec => Response).annotate({ + "description": "The response that is in progress.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when the response is in progress.", "identifier": "ResponseInProgressEvent" }) +export type ResponseFailedEvent = { + readonly "type": "response.failed" + readonly "sequence_number": number + readonly "response": Response +} +export const ResponseFailedEvent = Schema.Struct({ + "type": Schema.Literal("response.failed").annotate({ + "description": "The type of the event. Always `response.failed`.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response": Schema.suspend((): Schema.Codec => Response).annotate({ + "description": "The response that failed.\n" + }) +}).annotate({ "description": "An event that is emitted when a response fails.\n", "identifier": "ResponseFailedEvent" }) +export type ResponseIncompleteEvent = { + readonly "type": "response.incomplete" + readonly "response": Response + readonly "sequence_number": number +} +export const ResponseIncompleteEvent = Schema.Struct({ + "type": Schema.Literal("response.incomplete").annotate({ + "description": "The type of the event. Always `response.incomplete`.\n" + }), + "response": Schema.suspend((): Schema.Codec => Response).annotate({ + "description": "The response that was incomplete.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": + "An event that is emitted when a response finishes as incomplete.\n\nOver WebSocket, steering can finish a response with\n`response.incomplete_details.reason` set to `steered`, followed automatically\nby a successor `response.created` that commits the queued steering input.\n", + "identifier": "ResponseIncompleteEvent" +}) +export type ResponseQueuedEvent = { + readonly "type": "response.queued" + readonly "response": Response + readonly "sequence_number": number +} +export const ResponseQueuedEvent = Schema.Struct({ + "type": Schema.Literal("response.queued").annotate({ + "description": "The type of the event. Always 'response.queued'." + }), + "response": Schema.suspend((): Schema.Codec => Response).annotate({ + "description": "The full response object that is queued." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseQueuedEvent", + "description": "Emitted when a response is queued and waiting to be processed.\n", + "identifier": "ResponseQueuedEvent" +}) +export type LiveSessionCreateRequest = { + readonly "session": LiveMediaSessionCreateParams + readonly "transport": LiveWebRTCTransport | LiveSIPTransport +} +export const LiveSessionCreateRequest = Schema.Struct({ + "session": Schema.suspend((): Schema.Codec => LiveMediaSessionCreateParams).annotate({ + "description": "Startup configuration for the Live session." + }), + "transport": Schema.Union([LiveWebRTCTransport, LiveSIPTransport], { mode: "oneOf" }).annotate({ + "description": + "WebRTC transport with an SDP offer, or SIP transport with a destination and per-call trunk credentials." + }) +}).annotate({ + "description": + "Create a Live session with a WebRTC SDP offer or place an outbound SIP call. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before choosing frontend and backend instructions. The request starts the session; do not send session.start again on the data channel or sideband.", + "identifier": "LiveSessionCreateRequest" +}) +export type LiveCallAcceptRequest = { readonly "session": LiveCallAcceptSession } +export const LiveCallAcceptRequest = Schema.Struct({ + "session": Schema.suspend((): Schema.Codec => LiveCallAcceptSession).annotate({ + "description": "Model and startup configuration for the Live session that answers the incoming SIP call." + }) +}).annotate({ + "description": "Accept an incoming SIP call with Live startup configuration.", + "identifier": "LiveCallAcceptRequest" +}) +export type BetaResponseCompletedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.completed" + readonly "response": BetaResponse + readonly "sequence_number": number +} +export const BetaResponseCompletedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.completed").annotate({ + "description": "The type of the event. Always `response.completed`.\n" + }), + "response": Schema.suspend((): Schema.Codec => BetaResponse).annotate({ + "description": "Properties of the completed response.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "Emitted when the model response is complete.", + "identifier": "BetaResponseCompletedEvent" +}) +export type BetaResponseCreatedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.created" + readonly "response": BetaResponse + readonly "sequence_number": number +} +export const BetaResponseCreatedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.created").annotate({ + "description": "The type of the event. Always `response.created`.\n" + }), + "response": Schema.suspend((): Schema.Codec => BetaResponse).annotate({ + "description": "The response that was created.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": "An event that is emitted when a response is created.\n", + "identifier": "BetaResponseCreatedEvent" +}) +export type BetaResponseInProgressEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.in_progress" + readonly "response": BetaResponse + readonly "sequence_number": number +} +export const BetaResponseInProgressEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.in_progress").annotate({ + "description": "The type of the event. Always `response.in_progress`.\n" + }), + "response": Schema.suspend((): Schema.Codec => BetaResponse).annotate({ + "description": "The response that is in progress.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ "description": "Emitted when the response is in progress.", "identifier": "BetaResponseInProgressEvent" }) +export type BetaResponseFailedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.failed" + readonly "sequence_number": number + readonly "response": BetaResponse +} +export const BetaResponseFailedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.failed").annotate({ + "description": "The type of the event. Always `response.failed`.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ), + "response": Schema.suspend((): Schema.Codec => BetaResponse).annotate({ + "description": "The response that failed.\n" + }) +}).annotate({ + "description": "An event that is emitted when a response fails.\n", + "identifier": "BetaResponseFailedEvent" +}) +export type BetaResponseIncompleteEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.incomplete" + readonly "response": BetaResponse + readonly "sequence_number": number +} +export const BetaResponseIncompleteEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.incomplete").annotate({ + "description": "The type of the event. Always `response.incomplete`.\n" + }), + "response": Schema.suspend((): Schema.Codec => BetaResponse).annotate({ + "description": "The response that was incomplete.\n" + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number of this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "description": + "An event that is emitted when a response finishes as incomplete.\n\nOver WebSocket, steering can finish a response with\n`response.incomplete_details.reason` set to `steered`, followed automatically\nby a successor `response.created` that commits the queued steering input.\n", + "identifier": "BetaResponseIncompleteEvent" +}) +export type BetaResponseQueuedEvent = { + readonly "agent"?: BetaAgentTag | null + readonly "type": "response.queued" + readonly "response": BetaResponse + readonly "sequence_number": number +} +export const BetaResponseQueuedEvent = Schema.Struct({ + "agent": Schema.optionalKey( + Schema.Union([BetaAgentTag, Schema.Null]).annotate({ + "description": "The agent that owns this multi-agent streaming event." + }) + ), + "type": Schema.Literal("response.queued").annotate({ + "description": "The type of the event. Always 'response.queued'." + }), + "response": Schema.suspend((): Schema.Codec => BetaResponse).annotate({ + "description": "The full response object that is queued." + }), + "sequence_number": Schema.Number.annotate({ "description": "The sequence number for this event." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) +}).annotate({ + "title": "ResponseQueuedEvent", + "description": "Emitted when a response is queued and waiting to be processed.\n", + "identifier": "BetaResponseQueuedEvent" +}) +export type FineTuneMethod = { + readonly "type": "supervised" | "dpo" | "reinforcement" + readonly "supervised"?: FineTuneSupervisedMethod + readonly "dpo"?: FineTuneDPOMethod + readonly "reinforcement"?: FineTuneReinforcementMethod +} +export const FineTuneMethod = Schema.Struct({ + "type": Schema.Literals(["supervised", "dpo", "reinforcement"]).annotate({ + "description": "The type of method. Is either `supervised`, `dpo`, or `reinforcement`." + }), + "supervised": Schema.optionalKey(FineTuneSupervisedMethod), + "dpo": Schema.optionalKey(FineTuneDPOMethod), + "reinforcement": Schema.optionalKey(FineTuneReinforcementMethod) +}).annotate({ "description": "The method used for fine-tuning.", "identifier": "FineTuneMethod" }) +export type ResponseStreamEvent = + | ResponseAudioDeltaEvent + | ResponseAudioDoneEvent + | ResponseAudioTranscriptDeltaEvent + | ResponseAudioTranscriptDoneEvent + | ResponseCodeInterpreterCallCodeDeltaEvent + | ResponseCodeInterpreterCallCodeDoneEvent + | ResponseCodeInterpreterCallCompletedEvent + | ResponseCodeInterpreterCallInProgressEvent + | ResponseCodeInterpreterCallInterpretingEvent + | ResponseCompactionCompactingStreamingEvent + | ResponseCompletedEvent + | ResponseContentPartAddedEvent + | ResponseContentPartDoneEvent + | ResponseCreatedEvent + | ResponseErrorEvent + | ResponseFileSearchCallCompletedEvent + | ResponseFileSearchCallInProgressEvent + | ResponseFileSearchCallSearchingEvent + | ResponseFunctionCallArgumentsDeltaEvent + | ResponseFunctionCallArgumentsDoneEvent + | ResponseShellCallCommandAddedStreamingEvent + | ResponseShellCallCommandDeltaStreamingEvent + | ResponseShellCallCommandDoneStreamingEvent + | ResponseShellCallOutputContentDeltaStreamingEvent + | ResponseShellCallOutputContentDoneStreamingEvent + | ResponseInProgressEvent + | ResponseFailedEvent + | ResponseIncompleteEvent + | ResponseOutputItemAddedEvent + | ResponseOutputItemDoneEvent + | ResponseReasoningSummaryPartAddedEvent + | ResponseReasoningSummaryPartDoneEvent + | ResponseReasoningSummaryTextDeltaEvent + | ResponseReasoningSummaryTextDoneEvent + | ResponseReasoningTextDeltaEvent + | ResponseReasoningTextDoneEvent + | ResponseRefusalDeltaEvent + | ResponseRefusalDoneEvent + | ResponseTextDeltaEvent + | ResponseTextDoneEvent + | ResponseWebSearchCallCompletedEvent + | ResponseWebSearchCallInProgressEvent + | ResponseWebSearchCallSearchingEvent + | ResponseImageGenCallCompletedEvent + | ResponseImageGenCallGeneratingEvent + | ResponseImageGenCallInProgressEvent + | ResponseImageGenCallPartialImageEvent + | ResponseMCPCallArgumentsDeltaEvent + | ResponseMCPCallArgumentsDoneEvent + | ResponseMCPCallCompletedEvent + | ResponseMCPCallFailedEvent + | ResponseMCPCallInProgressEvent + | ResponseMCPListToolsCompletedEvent + | ResponseMCPListToolsFailedEvent + | ResponseMCPListToolsInProgressEvent + | ResponseOutputTextAnnotationAddedEvent + | ResponseQueuedEvent + | ResponseCustomToolCallInputDeltaEvent + | ResponseCustomToolCallInputDoneEvent + | ResponseKeepAliveEvent + | ResponseApplyPatchCallOperationDiffDeltaEvent + | ResponseApplyPatchCallOperationDiffDoneEvent +export const ResponseStreamEvent = Schema.Union([ + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCompactionCompactingStreamingEvent, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseShellCallCommandAddedStreamingEvent, + ResponseShellCallCommandDeltaStreamingEvent, + ResponseShellCallCommandDoneStreamingEvent, + ResponseShellCallOutputContentDeltaStreamingEvent, + ResponseShellCallOutputContentDoneStreamingEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ResponseImageGenCallCompletedEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallInProgressEvent, + ResponseImageGenCallPartialImageEvent, + ResponseMCPCallArgumentsDeltaEvent, + ResponseMCPCallArgumentsDoneEvent, + ResponseMCPCallCompletedEvent, + ResponseMCPCallFailedEvent, + ResponseMCPCallInProgressEvent, + ResponseMCPListToolsCompletedEvent, + ResponseMCPListToolsFailedEvent, + ResponseMCPListToolsInProgressEvent, + ResponseOutputTextAnnotationAddedEvent, + ResponseQueuedEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ResponseKeepAliveEvent, + ResponseApplyPatchCallOperationDiffDeltaEvent, + ResponseApplyPatchCallOperationDiffDoneEvent +]).annotate({ "description": "Event emitted while a response is streamed.", "identifier": "ResponseStreamEvent" }) +export type BetaResponseStreamEvent = + | BetaResponseAudioDeltaEvent + | BetaResponseAudioDoneEvent + | BetaResponseAudioTranscriptDeltaEvent + | BetaResponseAudioTranscriptDoneEvent + | BetaResponseCodeInterpreterCallCodeDeltaEvent + | BetaResponseCodeInterpreterCallCodeDoneEvent + | BetaResponseCodeInterpreterCallCompletedEvent + | BetaResponseCodeInterpreterCallInProgressEvent + | BetaResponseCodeInterpreterCallInterpretingEvent + | BetaResponseCompactionCompactingStreamingEvent + | BetaResponseCompletedEvent + | BetaResponseContentPartAddedEvent + | BetaResponseContentPartDoneEvent + | BetaResponseCreatedEvent + | BetaResponseErrorEvent + | BetaResponseFileSearchCallCompletedEvent + | BetaResponseFileSearchCallInProgressEvent + | BetaResponseFileSearchCallSearchingEvent + | BetaResponseFunctionCallArgumentsDeltaEvent + | BetaResponseFunctionCallArgumentsDoneEvent + | BetaResponseShellCallCommandAddedStreamingEvent + | BetaResponseShellCallCommandDeltaStreamingEvent + | BetaResponseShellCallCommandDoneStreamingEvent + | BetaResponseShellCallOutputContentDeltaStreamingEvent + | BetaResponseShellCallOutputContentDoneStreamingEvent + | BetaResponseInProgressEvent + | BetaResponseFailedEvent + | BetaResponseIncompleteEvent + | BetaResponseOutputItemAddedEvent + | BetaResponseOutputItemDoneEvent + | BetaResponseReasoningSummaryPartAddedEvent + | BetaResponseReasoningSummaryPartDoneEvent + | BetaResponseReasoningSummaryTextDeltaEvent + | BetaResponseReasoningSummaryTextDoneEvent + | BetaResponseReasoningTextDeltaEvent + | BetaResponseReasoningTextDoneEvent + | BetaResponseRefusalDeltaEvent + | BetaResponseRefusalDoneEvent + | BetaResponseTextDeltaEvent + | BetaResponseTextDoneEvent + | BetaResponseWebSearchCallCompletedEvent + | BetaResponseWebSearchCallInProgressEvent + | BetaResponseWebSearchCallSearchingEvent + | BetaResponseImageGenCallCompletedEvent + | BetaResponseImageGenCallGeneratingEvent + | BetaResponseImageGenCallInProgressEvent + | BetaResponseImageGenCallPartialImageEvent + | BetaResponseMCPCallArgumentsDeltaEvent + | BetaResponseMCPCallArgumentsDoneEvent + | BetaResponseMCPCallCompletedEvent + | BetaResponseMCPCallFailedEvent + | BetaResponseMCPCallInProgressEvent + | BetaResponseMCPListToolsCompletedEvent + | BetaResponseMCPListToolsFailedEvent + | BetaResponseMCPListToolsInProgressEvent + | BetaResponseOutputTextAnnotationAddedEvent + | BetaResponseQueuedEvent + | BetaResponseCustomToolCallInputDeltaEvent + | BetaResponseCustomToolCallInputDoneEvent +export const BetaResponseStreamEvent = Schema.Union([ + BetaResponseAudioDeltaEvent, + BetaResponseAudioDoneEvent, + BetaResponseAudioTranscriptDeltaEvent, + BetaResponseAudioTranscriptDoneEvent, + BetaResponseCodeInterpreterCallCodeDeltaEvent, + BetaResponseCodeInterpreterCallCodeDoneEvent, + BetaResponseCodeInterpreterCallCompletedEvent, + BetaResponseCodeInterpreterCallInProgressEvent, + BetaResponseCodeInterpreterCallInterpretingEvent, + BetaResponseCompactionCompactingStreamingEvent, + BetaResponseCompletedEvent, + BetaResponseContentPartAddedEvent, + BetaResponseContentPartDoneEvent, + BetaResponseCreatedEvent, + BetaResponseErrorEvent, + BetaResponseFileSearchCallCompletedEvent, + BetaResponseFileSearchCallInProgressEvent, + BetaResponseFileSearchCallSearchingEvent, + BetaResponseFunctionCallArgumentsDeltaEvent, + BetaResponseFunctionCallArgumentsDoneEvent, + BetaResponseShellCallCommandAddedStreamingEvent, + BetaResponseShellCallCommandDeltaStreamingEvent, + BetaResponseShellCallCommandDoneStreamingEvent, + BetaResponseShellCallOutputContentDeltaStreamingEvent, + BetaResponseShellCallOutputContentDoneStreamingEvent, + BetaResponseInProgressEvent, + BetaResponseFailedEvent, + BetaResponseIncompleteEvent, + BetaResponseOutputItemAddedEvent, + BetaResponseOutputItemDoneEvent, + BetaResponseReasoningSummaryPartAddedEvent, + BetaResponseReasoningSummaryPartDoneEvent, + BetaResponseReasoningSummaryTextDeltaEvent, + BetaResponseReasoningSummaryTextDoneEvent, + BetaResponseReasoningTextDeltaEvent, + BetaResponseReasoningTextDoneEvent, + BetaResponseRefusalDeltaEvent, + BetaResponseRefusalDoneEvent, + BetaResponseTextDeltaEvent, + BetaResponseTextDoneEvent, + BetaResponseWebSearchCallCompletedEvent, + BetaResponseWebSearchCallInProgressEvent, + BetaResponseWebSearchCallSearchingEvent, + BetaResponseImageGenCallCompletedEvent, + BetaResponseImageGenCallGeneratingEvent, + BetaResponseImageGenCallInProgressEvent, + BetaResponseImageGenCallPartialImageEvent, + BetaResponseMCPCallArgumentsDeltaEvent, + BetaResponseMCPCallArgumentsDoneEvent, + BetaResponseMCPCallCompletedEvent, + BetaResponseMCPCallFailedEvent, + BetaResponseMCPCallInProgressEvent, + BetaResponseMCPListToolsCompletedEvent, + BetaResponseMCPListToolsFailedEvent, + BetaResponseMCPListToolsInProgressEvent, + BetaResponseOutputTextAnnotationAddedEvent, + BetaResponseQueuedEvent, + BetaResponseCustomToolCallInputDeltaEvent, + BetaResponseCustomToolCallInputDoneEvent +]).annotate({ "description": "Event emitted while a response is streamed.", "identifier": "BetaResponseStreamEvent" }) +export type FineTuningJob = { + readonly "id": string + readonly "created_at": number + readonly "error": { readonly "code": string; readonly "message": string; readonly "param": string | null } | null | { + readonly [x: string]: Schema.Json + } + readonly "fine_tuned_model": string | null + readonly "finished_at": number | null + readonly "hyperparameters"?: + | { + readonly "batch_size"?: "auto" | number | null + readonly "learning_rate_multiplier"?: "auto" | number + readonly "n_epochs"?: "auto" | number + } & { readonly [x: string]: Schema.Json } + | null + readonly "model": string + readonly "object": "fine_tuning.job" + readonly "organization_id": string + readonly "result_files": ReadonlyArray + readonly "status": + | "validating_files" + | "queued" + | "running" + | "succeeded" + | "failed" + | "cancelled" + | "pausing" + | "paused" + readonly "trained_tokens": number | null + readonly "training_file": string + readonly "validation_file": string | null + readonly "integrations"?: ReadonlyArray | null + readonly "seed": number | null + readonly "estimated_finish"?: number | null + readonly "method": { + readonly "type": "dpo" | "reinforcement" + readonly "supervised"?: FineTuneSupervisedMethod + readonly "dpo"?: FineTuneDPOMethod + readonly "reinforcement"?: FineTuneReinforcementMethod + } + readonly "metadata"?: Metadata +} | { + readonly "id": string + readonly "created_at": number + readonly "error": { readonly "code": string; readonly "message": string; readonly "param": string | null } | null | { + readonly [x: string]: Schema.Json + } + readonly "fine_tuned_model": string | null + readonly "finished_at": number | null + readonly "hyperparameters": + | { + readonly "batch_size"?: "auto" | number | null + readonly "learning_rate_multiplier"?: "auto" | number + readonly "n_epochs"?: "auto" | number + } & { readonly [x: string]: Schema.Json } + | null + readonly "model": string + readonly "object": "fine_tuning.job" + readonly "organization_id": string + readonly "result_files": ReadonlyArray + readonly "status": + | "validating_files" + | "queued" + | "running" + | "succeeded" + | "failed" + | "cancelled" + | "pausing" + | "paused" + readonly "trained_tokens": number | null + readonly "training_file": string + readonly "validation_file": string | null + readonly "integrations"?: ReadonlyArray | null + readonly "seed": number | null + readonly "estimated_finish"?: number | null + readonly "method"?: FineTuneMethod | null + readonly "metadata"?: Metadata +} +export const FineTuningJob = Schema.Union([ + Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The object identifier, which can be referenced in the API endpoints." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the fine-tuning job was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "error": Schema.Union([ + Schema.Struct({ + "code": Schema.String.annotate({ "description": "A machine-readable error code." }), + "message": Schema.String.annotate({ "description": "A human-readable error message." }), + "param": Schema.Union([ + Schema.String.annotate({ + "description": + "The parameter that was invalid, usually `training_file` or `validation_file`. This field will be null if the failure was not parameter-specific." + }), + Schema.Null + ]) + }).annotate({ + "description": + "For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure." + }), + Schema.Null, + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).check( + Schema.isMaxProperties(0).annotate({ "expected": "a value with at most 0 entries" }) + ) + ]), + "fine_tuned_model": Schema.Union([ + Schema.String.annotate({ + "description": + "The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running." + }), + Schema.Null + ]), + "finished_at": Schema.Union([ + Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "hyperparameters": Schema.optionalKey( + Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "batch_size": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(256).annotate({ "expected": "a value less than or equal to 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.\n" + }), + Schema.Null + ]) + ), + "learning_rate_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.\n" + }) + ), + "n_epochs": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.\n" + }) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": + "The hyperparameters used for the fine-tuning job. This value will only be returned when running `supervised` jobs." + }) + ), + "model": Schema.String.annotate({ "description": "The base model that is being fine-tuned." }), + "object": Schema.Literal("fine_tuning.job").annotate({ + "description": "The object type, which is always \"fine_tuning.job\"." + }), + "organization_id": Schema.String.annotate({ "description": "The organization that owns the fine-tuning job." }), + "result_files": Schema.Array(Schema.String).annotate({ + "description": + "The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content)." + }), + "status": Schema.Literals([ + "validating_files", + "queued", + "running", + "succeeded", + "failed", + "cancelled", + "pausing", + "paused" + ]).annotate({ + "description": + "The current status of the fine-tuning job, which can be `validating_files`, `queued`, `running`, `pausing`, `paused`, `succeeded`, `failed`, or `cancelled`." + }), + "trained_tokens": Schema.Union([ + Schema.Number.annotate({ + "description": + "The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "training_file": Schema.String.annotate({ + "description": + "The file ID used for training. You can retrieve the training data with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content)." + }), + "validation_file": Schema.Union([ + Schema.String.annotate({ + "description": + "The file ID used for validation. You can retrieve the validation results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content)." + }), + Schema.Null + ]), + "integrations": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.Union([FineTuningIntegration], { mode: "oneOf" })).annotate({ + "description": "A list of integrations to enable for this fine-tuning job." + }).check(Schema.isMaxLength(5).annotate({ "expected": "a value with a length of at most 5" })), + Schema.Null + ]) + ), + "seed": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The seed used for the fine-tuning job." }), + "estimated_finish": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when the fine-tuning job is estimated to finish. The value will be null if the fine-tuning job is not running.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "method": Schema.Union([ + Schema.Struct({ + "type": Schema.Union([ + Schema.Literal("dpo").annotate({ + "description": "The type of method. Is either `supervised`, `dpo`, or `reinforcement`." + }), + Schema.Literal("reinforcement").annotate({ + "description": "The type of method. Is either `supervised`, `dpo`, or `reinforcement`." + }) + ]), + "supervised": Schema.optionalKey(FineTuneSupervisedMethod), + "dpo": Schema.optionalKey(FineTuneDPOMethod), + "reinforcement": Schema.optionalKey(FineTuneReinforcementMethod) + }).annotate({ "description": "The method used for fine-tuning." }) + ]), + "metadata": Schema.optionalKey(Metadata) + }), + Schema.Struct({ + "id": Schema.String.annotate({ + "description": "The object identifier, which can be referenced in the API endpoints." + }), + "created_at": Schema.Number.annotate({ + "description": "The Unix timestamp (in seconds) for when the fine-tuning job was created.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + "error": Schema.Union([ + Schema.Struct({ + "code": Schema.String.annotate({ "description": "A machine-readable error code." }), + "message": Schema.String.annotate({ "description": "A human-readable error message." }), + "param": Schema.Union([ + Schema.String.annotate({ + "description": + "The parameter that was invalid, usually `training_file` or `validation_file`. This field will be null if the failure was not parameter-specific." + }), + Schema.Null + ]) + }).annotate({ + "description": + "For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure." + }), + Schema.Null, + Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" })).check( + Schema.isMaxProperties(0).annotate({ "expected": "a value with at most 0 entries" }) + ) + ]), + "fine_tuned_model": Schema.Union([ + Schema.String.annotate({ + "description": + "The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running." + }), + Schema.Null + ]), + "finished_at": Schema.Union([ + Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "hyperparameters": Schema.Union([ + Schema.StructWithRest( + Schema.Struct({ + "batch_size": Schema.optionalKey( + Schema.Union([ + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(256).annotate({ "expected": "a value less than or equal to 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.\n" + }), + Schema.Null + ]) + ), + "learning_rate_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.\n" + }) + ), + "n_epochs": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.\n" + }) + ) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Null + ]).annotate({ + "description": + "The hyperparameters used for the fine-tuning job. This value will only be returned when running `supervised` jobs." + }), + "model": Schema.String.annotate({ "description": "The base model that is being fine-tuned." }), + "object": Schema.Literal("fine_tuning.job").annotate({ + "description": "The object type, which is always \"fine_tuning.job\"." + }), + "organization_id": Schema.String.annotate({ "description": "The organization that owns the fine-tuning job." }), + "result_files": Schema.Array(Schema.String).annotate({ + "description": + "The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content)." + }), + "status": Schema.Literals([ + "validating_files", + "queued", + "running", + "succeeded", + "failed", + "cancelled", + "pausing", + "paused" + ]).annotate({ + "description": + "The current status of the fine-tuning job, which can be `validating_files`, `queued`, `running`, `pausing`, `paused`, `succeeded`, `failed`, or `cancelled`." + }), + "trained_tokens": Schema.Union([ + Schema.Number.annotate({ + "description": + "The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running." + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]), + "training_file": Schema.String.annotate({ + "description": + "The file ID used for training. You can retrieve the training data with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content)." + }), + "validation_file": Schema.Union([ + Schema.String.annotate({ + "description": + "The file ID used for validation. You can retrieve the validation results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content)." + }), + Schema.Null + ]), + "integrations": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.Union([FineTuningIntegration], { mode: "oneOf" })).annotate({ + "description": "A list of integrations to enable for this fine-tuning job." + }).check(Schema.isMaxLength(5).annotate({ "expected": "a value with a length of at most 5" })), + Schema.Null + ]) + ), + "seed": Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + .annotate({ "description": "The seed used for the fine-tuning job." }), + "estimated_finish": Schema.optionalKey( + Schema.Union([ + Schema.Number.annotate({ + "description": + "The Unix timestamp (in seconds) for when the fine-tuning job is estimated to finish. The value will be null if the fine-tuning job is not running.", + "format": "unixtime" + }).check(Schema.isInt().annotate({ "expected": "an integer" })), + Schema.Null + ]) + ), + "method": Schema.optionalKey(Schema.Union([FineTuneMethod, Schema.Null])), + "metadata": Schema.optionalKey(Metadata) + }) +]).annotate({ + "title": "FineTuningJob", + "description": "The `fine_tuning.job` object represents a fine-tuning job that has been created through the API.\n", + "identifier": "FineTuningJob" +}) +export type CreateFineTuningJobRequest = { + readonly "model": string | "babbage-002" | "davinci-002" | "gpt-3.5-turbo" | "gpt-4o-mini" + readonly "training_file": string + readonly "hyperparameters"?: { + readonly "batch_size"?: "auto" | number + readonly "learning_rate_multiplier"?: "auto" | number + readonly "n_epochs"?: "auto" | number + } + readonly "suffix"?: string | null + readonly "validation_file"?: string | null + readonly "integrations"?: + | ReadonlyArray< + { + readonly "type": "wandb" + readonly "wandb": { + readonly "project": string + readonly "name"?: string | null + readonly "entity"?: string | null + readonly "tags"?: ReadonlyArray + } + } + > + | null + readonly "seed"?: number | null + readonly "method"?: FineTuneMethod + readonly "metadata"?: Metadata +} +export const CreateFineTuningJobRequest = Schema.Struct({ + "model": Schema.Union([ + Schema.String, + Schema.Literals(["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]) + ]).annotate({ + "description": + "The name of the model to fine-tune. You can select one of the\n[supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).\n" + }), + "training_file": Schema.String.annotate({ + "description": + "The ID of an uploaded file that contains training data.\n\nSee [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.\n\nYour dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`.\n\nThe contents of the file should differ depending on if the model uses the [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) format, or if the fine-tuning method uses the [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) format.\n\nSee the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.\n" + }), + "hyperparameters": Schema.optionalKey( + Schema.Struct({ + "batch_size": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(256).annotate({ "expected": "a value less than or equal to 256" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.\n" + }) + ), + "learning_rate_multiplier": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Finite.check(Schema.isGreaterThan(0).annotate({ "expected": "a value greater than 0" })) + ], { mode: "oneOf" }).annotate({ + "description": + "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.\n" + }) + ), + "n_epochs": Schema.optionalKey( + Schema.Union([ + Schema.Literal("auto"), + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(50).annotate({ "expected": "a value less than or equal to 50" })) + ], { mode: "oneOf" }).annotate({ + "description": + "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.\n" + }) + ) + }).annotate({ + "description": + "The hyperparameters used for the fine-tuning job.\nThis value is now deprecated in favor of `method`, and should be passed in under the `method` parameter.\n" + }) + ), + "suffix": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" }) + ), + Schema.Null + ]).annotate({ + "description": + "A string of up to 64 characters that will be added to your fine-tuned model name.\n\nFor example, a `suffix` of \"custom-model-name\" would produce a model name like `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`.\n" + }) + ), + "validation_file": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "The ID of an uploaded file that contains validation data.\n\nIf you provide this file, the data is used to generate validation\nmetrics periodically during fine-tuning. These metrics can be viewed in\nthe fine-tuning results file.\nThe same data should not be present in both train and validation files.\n\nYour dataset must be formatted as a JSONL file. You must upload your file with the purpose `fine-tune`.\n\nSee the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.\n" + }) + ), + "integrations": Schema.optionalKey( + Schema.Union([ + Schema.Array(Schema.Struct({ + "type": Schema.Union([Schema.Literal("wandb")], { mode: "oneOf" }).annotate({ + "description": + "The type of integration to enable. Currently, only \"wandb\" (Weights and Biases) is supported.\n" + }), + "wandb": Schema.Struct({ + "project": Schema.String.annotate({ + "description": "The name of the project that the new run will be created under.\n" + }), + "name": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "A display name to set for the run. If not set, we will use the Job ID as the name.\n" + }) + ), + "entity": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": + "The entity to use for the run. This allows you to set the team or username of the WandB user that you would\nlike associated with the run. If not set, the default entity for the registered WandB API key is used.\n" + }) + ), + "tags": Schema.optionalKey( + Schema.Array(Schema.String).annotate({ + "description": + "A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some\ndefault tags are generated by OpenAI: \"openai/finetune\", \"openai/{base-model}\", \"openai/{ftjob-abcdef}\".\n" + }) + ) + }).annotate({ + "description": + "The settings for your integration with Weights and Biases. This payload specifies the project that\nmetrics will be sent to. Optionally, you can set an explicit display name for your run, add tags\nto your run, and set a default entity (team, username, etc) to be associated with your run.\n" + }) + })), + Schema.Null + ]).annotate({ "description": "A list of integrations to enable for your fine-tuning job." }) + ), + "seed": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check( + Schema.isLessThanOrEqualTo(2147483647).annotate({ "expected": "a value less than or equal to 2147483647" }) + ), + Schema.Null + ]).annotate({ + "description": + "The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases.\nIf a seed is not specified, one will be generated for you.\n" + }) + ), + "method": Schema.optionalKey(FineTuneMethod), + "metadata": Schema.optionalKey(Metadata) +}).annotate({ "identifier": "CreateFineTuningJobRequest" }) +export type ListPaginatedFineTuningJobsResponse = { + readonly "data": ReadonlyArray + readonly "has_more": boolean + readonly "object": "list" +} +export const ListPaginatedFineTuningJobsResponse = Schema.Struct({ + "data": Schema.Array(FineTuningJob), + "has_more": Schema.Boolean, + "object": Schema.Literal("list") +}).annotate({ "identifier": "ListPaginatedFineTuningJobsResponse" }) +// recursive definitions +const __recursive_CompoundFilter = Schema.Struct({ + "type": Schema.Literals(["and", "or"]).annotate({ "description": "Type of operation: `and` or `or`." }), + "filters": Schema.Array( + Schema.Union([ComparisonFilter, Schema.suspend((): Schema.Codec => CompoundFilter)], { + mode: "oneOf" + }) + ).annotate({ "description": "Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`." }) +}).annotate({ + "title": "Compound Filter", + "description": "Combine multiple filters using `and` or `or`.", + "identifier": "CompoundFilter" +}) +const __recursive_BetaCompoundFilter = Schema.Struct({ + "type": Schema.Literals(["and", "or"]).annotate({ "description": "Type of operation: `and` or `or`." }), + "filters": Schema.Array( + Schema.Union([BetaComparisonFilter, Schema.suspend((): Schema.Codec => BetaCompoundFilter)], { + mode: "oneOf" + }) + ).annotate({ "description": "Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`." }) +}).annotate({ + "title": "Compound Filter", + "description": "Combine multiple filters using `and` or `or`.", + "identifier": "BetaCompoundFilter" +}) +// schemas +export type ListAssistantsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string +} +export const ListAssistantsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListAssistants200 = ListAssistantsResponse +export const ListAssistants200 = ListAssistantsResponse +export type ListAssistants429 = ErrorResponse +export const ListAssistants429 = ErrorResponse +export type CreateAssistantRequestJson = CreateAssistantRequest +export const CreateAssistantRequestJson = CreateAssistantRequest +export type CreateAssistant200 = AssistantObject +export const CreateAssistant200 = AssistantObject +export type CreateAssistant429 = ErrorResponse +export const CreateAssistant429 = ErrorResponse +export type GetAssistant200 = AssistantObject +export const GetAssistant200 = AssistantObject +export type GetAssistant429 = ErrorResponse +export const GetAssistant429 = ErrorResponse +export type ModifyAssistantRequestJson = ModifyAssistantRequest +export const ModifyAssistantRequestJson = ModifyAssistantRequest +export type ModifyAssistant200 = AssistantObject +export const ModifyAssistant200 = AssistantObject +export type ModifyAssistant429 = ErrorResponse +export const ModifyAssistant429 = ErrorResponse +export type DeleteAssistant200 = DeleteAssistantResponse +export const DeleteAssistant200 = DeleteAssistantResponse +export type DeleteAssistant429 = ErrorResponse +export const DeleteAssistant429 = ErrorResponse +export type CreateSpeechRequestJson = CreateSpeechRequest +export const CreateSpeechRequestJson = CreateSpeechRequest +export type CreateSpeech200Sse = CreateSpeechResponseStreamEvent +export const CreateSpeech200Sse = CreateSpeechResponseStreamEvent +export type CreateSpeech400 = ErrorResponse +export const CreateSpeech400 = ErrorResponse +export type CreateSpeech401 = ErrorResponse +export const CreateSpeech401 = ErrorResponse +export type CreateSpeech429 = ErrorResponse +export const CreateSpeech429 = ErrorResponse +export type CreateSpeech500 = ErrorResponse +export const CreateSpeech500 = ErrorResponse +export type CreateSpeech503 = ErrorResponse +export const CreateSpeech503 = ErrorResponse +export type CreateTranscriptionRequestFormData = CreateTranscriptionRequestMultipart +export const CreateTranscriptionRequestFormData = CreateTranscriptionRequestMultipart +export type CreateTranscription200 = + | CreateTranscriptionResponseJson + | CreateTranscriptionResponseDiarizedJson + | CreateTranscriptionResponseVerboseJson +export const CreateTranscription200 = Schema.Union([ + CreateTranscriptionResponseJson, + CreateTranscriptionResponseDiarizedJson, + CreateTranscriptionResponseVerboseJson +]) +export type CreateTranscription200Sse = CreateTranscriptionResponseStreamEvent +export const CreateTranscription200Sse = CreateTranscriptionResponseStreamEvent +export type CreateTranscription400 = ErrorResponse +export const CreateTranscription400 = ErrorResponse +export type CreateTranscription401 = ErrorResponse +export const CreateTranscription401 = ErrorResponse +export type CreateTranscription413 = ErrorResponse +export const CreateTranscription413 = ErrorResponse +export type CreateTranscription429 = ErrorResponse +export const CreateTranscription429 = ErrorResponse +export type CreateTranscription500 = ErrorResponse +export const CreateTranscription500 = ErrorResponse +export type CreateTranscription502 = ErrorResponse +export const CreateTranscription502 = ErrorResponse +export type CreateTranscription503 = ErrorResponse +export const CreateTranscription503 = ErrorResponse +export type CreateTranslationRequestFormData = CreateTranslationRequestMultipart +export const CreateTranslationRequestFormData = CreateTranslationRequestMultipart +export type CreateTranslation200 = CreateTranslationResponseJson | CreateTranslationResponseVerboseJson +export const CreateTranslation200 = Schema.Union( + [CreateTranslationResponseJson, CreateTranslationResponseVerboseJson], + { mode: "oneOf" } +) +export type CreateTranslation400 = ErrorResponse +export const CreateTranslation400 = ErrorResponse +export type CreateTranslation401 = ErrorResponse +export const CreateTranslation401 = ErrorResponse +export type CreateTranslation413 = ErrorResponse +export const CreateTranslation413 = ErrorResponse +export type CreateTranslation429 = ErrorResponse +export const CreateTranslation429 = ErrorResponse +export type CreateTranslation500 = ErrorResponse +export const CreateTranslation500 = ErrorResponse +export type CreateTranslation502 = ErrorResponse +export const CreateTranslation502 = ErrorResponse +export type CreateTranslation503 = ErrorResponse +export const CreateTranslation503 = ErrorResponse +export type ListVoiceConsentsParams = { readonly "after"?: string; readonly "limit"?: number } +export const ListVoiceConsentsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) +}) +export type ListVoiceConsents200 = VoiceConsentListResource +export const ListVoiceConsents200 = VoiceConsentListResource +export type ListVoiceConsents400 = ErrorResponse +export const ListVoiceConsents400 = ErrorResponse +export type ListVoiceConsents404 = ErrorResponse +export const ListVoiceConsents404 = ErrorResponse +export type CreateVoiceConsentRequestFormData = CreateVoiceConsentRequestMultipart +export const CreateVoiceConsentRequestFormData = CreateVoiceConsentRequestMultipart +export type CreateVoiceConsent200 = VoiceConsentResource +export const CreateVoiceConsent200 = VoiceConsentResource +export type CreateVoiceConsent400 = ErrorResponse +export const CreateVoiceConsent400 = ErrorResponse +export type CreateVoiceConsent404 = ErrorResponse +export const CreateVoiceConsent404 = ErrorResponse +export type GetVoiceConsent200 = VoiceConsentResource +export const GetVoiceConsent200 = VoiceConsentResource +export type GetVoiceConsent400 = ErrorResponse +export const GetVoiceConsent400 = ErrorResponse +export type GetVoiceConsent404 = ErrorResponse +export const GetVoiceConsent404 = ErrorResponse +export type UpdateVoiceConsentRequestJson = UpdateVoiceConsentRequest +export const UpdateVoiceConsentRequestJson = UpdateVoiceConsentRequest +export type UpdateVoiceConsent200 = VoiceConsentResource +export const UpdateVoiceConsent200 = VoiceConsentResource +export type UpdateVoiceConsent400 = ErrorResponse +export const UpdateVoiceConsent400 = ErrorResponse +export type UpdateVoiceConsent404 = ErrorResponse +export const UpdateVoiceConsent404 = ErrorResponse +export type DeleteVoiceConsent200 = VoiceConsentDeletedResource +export const DeleteVoiceConsent200 = VoiceConsentDeletedResource +export type DeleteVoiceConsent400 = ErrorResponse +export const DeleteVoiceConsent400 = ErrorResponse +export type DeleteVoiceConsent404 = ErrorResponse +export const DeleteVoiceConsent404 = ErrorResponse +export type CreateVoiceRequestFormData = CreateVoiceRequestMultipart +export const CreateVoiceRequestFormData = CreateVoiceRequestMultipart +export type CreateVoice200 = VoiceResource +export const CreateVoice200 = VoiceResource +export type CreateVoice400 = ErrorResponse +export const CreateVoice400 = ErrorResponse +export type CreateVoice404 = ErrorResponse +export const CreateVoice404 = ErrorResponse +export type CreateVoice500 = ErrorResponse +export const CreateVoice500 = ErrorResponse +export type ListBatchesParams = { readonly "after"?: string; readonly "limit"?: number } +export const ListBatchesParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) +}) +export type ListBatches200 = ListBatchesResponse +export const ListBatches200 = ListBatchesResponse +export type ListBatches400 = ErrorResponse +export const ListBatches400 = ErrorResponse +export type ListBatches404 = ErrorResponse +export const ListBatches404 = ErrorResponse +export type ListBatches429 = ErrorResponse +export const ListBatches429 = ErrorResponse +export type CreateBatchRequestJson = CreateBatchRequest +export const CreateBatchRequestJson = CreateBatchRequest +export type CreateBatch200 = Batch +export const CreateBatch200 = Batch +export type CreateBatch400 = ErrorResponse +export const CreateBatch400 = ErrorResponse +export type CreateBatch429 = ErrorResponse +export const CreateBatch429 = ErrorResponse +export type CreateBatch500 = ErrorResponse +export const CreateBatch500 = ErrorResponse +export type CreateBatch503 = ErrorResponse +export const CreateBatch503 = ErrorResponse +export type RetrieveBatch200 = Batch +export const RetrieveBatch200 = Batch +export type RetrieveBatch400 = ErrorResponse +export const RetrieveBatch400 = ErrorResponse +export type RetrieveBatch404 = ErrorResponse +export const RetrieveBatch404 = ErrorResponse +export type RetrieveBatch429 = ErrorResponse +export const RetrieveBatch429 = ErrorResponse +export type CancelBatch200 = Batch +export const CancelBatch200 = Batch +export type CancelBatch400 = ErrorResponse +export const CancelBatch400 = ErrorResponse +export type CancelBatch404 = ErrorResponse +export const CancelBatch404 = ErrorResponse +export type CancelBatch409 = ErrorResponse +export const CancelBatch409 = ErrorResponse +export type CancelBatch429 = ErrorResponse +export const CancelBatch429 = ErrorResponse +export type ListChatCompletionsParams = { + readonly "model"?: string + readonly "metadata"?: Metadata + readonly "after"?: string + readonly "limit"?: number + readonly "order"?: "asc" | "desc" +} +export const ListChatCompletionsParams = Schema.Struct({ + "model": Schema.optionalKey(Schema.String), + "metadata": Schema.optionalKey(Metadata), + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListChatCompletions200 = ChatCompletionList +export const ListChatCompletions200 = ChatCompletionList +export type ListChatCompletions400 = ErrorResponse +export const ListChatCompletions400 = ErrorResponse +export type ListChatCompletions429 = ErrorResponse +export const ListChatCompletions429 = ErrorResponse +export type ListChatCompletions500 = ErrorResponse +export const ListChatCompletions500 = ErrorResponse +export type CreateChatCompletionRequestJson = CreateChatCompletionRequest +export const CreateChatCompletionRequestJson = CreateChatCompletionRequest +export type CreateChatCompletion200 = CreateChatCompletionResponse +export const CreateChatCompletion200 = CreateChatCompletionResponse +export type CreateChatCompletion200Sse = CreateChatCompletionStreamResponse +export const CreateChatCompletion200Sse = CreateChatCompletionStreamResponse +export type CreateChatCompletion400 = ErrorResponse +export const CreateChatCompletion400 = ErrorResponse +export type CreateChatCompletion401 = ErrorResponse +export const CreateChatCompletion401 = ErrorResponse +export type CreateChatCompletion404 = ErrorResponse +export const CreateChatCompletion404 = ErrorResponse +export type CreateChatCompletion429 = ErrorResponse +export const CreateChatCompletion429 = ErrorResponse +export type CreateChatCompletion500 = ErrorResponse +export const CreateChatCompletion500 = ErrorResponse +export type CreateChatCompletion503 = ErrorResponse +export const CreateChatCompletion503 = ErrorResponse +export type GetChatCompletion200 = CreateChatCompletionResponse +export const GetChatCompletion200 = CreateChatCompletionResponse +export type GetChatCompletion404 = ErrorResponse +export const GetChatCompletion404 = ErrorResponse +export type GetChatCompletion429 = ErrorResponse +export const GetChatCompletion429 = ErrorResponse +export type GetChatCompletion500 = ErrorResponse +export const GetChatCompletion500 = ErrorResponse +export type UpdateChatCompletionRequestJson = { readonly "metadata": Metadata } +export const UpdateChatCompletionRequestJson = Schema.Struct({ "metadata": Metadata }) +export type UpdateChatCompletion200 = CreateChatCompletionResponse +export const UpdateChatCompletion200 = CreateChatCompletionResponse +export type UpdateChatCompletion400 = ErrorResponse +export const UpdateChatCompletion400 = ErrorResponse +export type UpdateChatCompletion404 = ErrorResponse +export const UpdateChatCompletion404 = ErrorResponse +export type UpdateChatCompletion429 = ErrorResponse +export const UpdateChatCompletion429 = ErrorResponse +export type UpdateChatCompletion500 = ErrorResponse +export const UpdateChatCompletion500 = ErrorResponse +export type DeleteChatCompletion200 = ChatCompletionDeleted +export const DeleteChatCompletion200 = ChatCompletionDeleted +export type DeleteChatCompletion404 = ErrorResponse +export const DeleteChatCompletion404 = ErrorResponse +export type DeleteChatCompletion429 = ErrorResponse +export const DeleteChatCompletion429 = ErrorResponse +export type DeleteChatCompletion500 = ErrorResponse +export const DeleteChatCompletion500 = ErrorResponse +export type GetChatCompletionMessagesParams = { + readonly "after"?: string + readonly "limit"?: number + readonly "order"?: "asc" | "desc" +} +export const GetChatCompletionMessagesParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type GetChatCompletionMessages200 = ChatCompletionMessageList +export const GetChatCompletionMessages200 = ChatCompletionMessageList +export type GetChatCompletionMessages400 = ErrorResponse +export const GetChatCompletionMessages400 = ErrorResponse +export type GetChatCompletionMessages404 = ErrorResponse +export const GetChatCompletionMessages404 = ErrorResponse +export type GetChatCompletionMessages429 = ErrorResponse +export const GetChatCompletionMessages429 = ErrorResponse +export type GetChatCompletionMessages500 = ErrorResponse +export const GetChatCompletionMessages500 = ErrorResponse +export type CreateCompletionRequestJson = CreateCompletionRequest +export const CreateCompletionRequestJson = CreateCompletionRequest +export type CreateCompletion200 = CreateCompletionResponse +export const CreateCompletion200 = CreateCompletionResponse +export type CreateCompletion400 = ErrorResponse +export const CreateCompletion400 = ErrorResponse +export type CreateCompletion401 = ErrorResponse +export const CreateCompletion401 = ErrorResponse +export type CreateCompletion404 = ErrorResponse +export const CreateCompletion404 = ErrorResponse +export type CreateCompletion429 = ErrorResponse +export const CreateCompletion429 = ErrorResponse +export type CreateCompletion500 = ErrorResponse +export const CreateCompletion500 = ErrorResponse +export type CreateCompletion503 = ErrorResponse +export const CreateCompletion503 = ErrorResponse +export type ListContainersParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "name"?: string +} +export const ListContainersParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "name": Schema.optionalKey(Schema.String) +}) +export type ListContainers200 = ContainerListResource +export const ListContainers200 = ContainerListResource +export type ListContainers404 = ErrorResponse +export const ListContainers404 = ErrorResponse +export type ListContainers429 = ErrorResponse +export const ListContainers429 = ErrorResponse +export type CreateContainerRequestJson = CreateContainerBody +export const CreateContainerRequestJson = CreateContainerBody +export type CreateContainer200 = ContainerResource +export const CreateContainer200 = ContainerResource +export type CreateContainer400 = ErrorResponse +export const CreateContainer400 = ErrorResponse +export type CreateContainer429 = ErrorResponse +export const CreateContainer429 = ErrorResponse +export type RetrieveContainer200 = ContainerResource +export const RetrieveContainer200 = ContainerResource +export type RetrieveContainer404 = ErrorResponse +export const RetrieveContainer404 = ErrorResponse +export type RetrieveContainer429 = ErrorResponse +export const RetrieveContainer429 = ErrorResponse +export type DeleteContainer200 = { + readonly "object": "container.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const DeleteContainer200 = Schema.Struct({ + "object": Schema.Literal("container.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String +}) +export type DeleteContainer404 = ErrorResponse +export const DeleteContainer404 = ErrorResponse +export type DeleteContainer429 = ErrorResponse +export const DeleteContainer429 = ErrorResponse +export type ListContainerFilesParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string +} +export const ListContainerFilesParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String) +}) +export type ListContainerFiles200 = ContainerFileListResource +export const ListContainerFiles200 = ContainerFileListResource +export type ListContainerFiles404 = ErrorResponse +export const ListContainerFiles404 = ErrorResponse +export type ListContainerFiles429 = ErrorResponse +export const ListContainerFiles429 = ErrorResponse +export type CreateContainerFileRequestJson = CreateContainerFileBody +export const CreateContainerFileRequestJson = CreateContainerFileBody +export type CreateContainerFileRequestFormData = CreateContainerFileBodyMultipart +export const CreateContainerFileRequestFormData = CreateContainerFileBodyMultipart +export type CreateContainerFile200 = ContainerFileResource +export const CreateContainerFile200 = ContainerFileResource +export type CreateContainerFile400 = ErrorResponse +export const CreateContainerFile400 = ErrorResponse +export type CreateContainerFile404 = ErrorResponse +export const CreateContainerFile404 = ErrorResponse +export type CreateContainerFile429 = ErrorResponse +export const CreateContainerFile429 = ErrorResponse +export type RetrieveContainerFile200 = ContainerFileResource +export const RetrieveContainerFile200 = ContainerFileResource +export type RetrieveContainerFile404 = ErrorResponse +export const RetrieveContainerFile404 = ErrorResponse +export type RetrieveContainerFile429 = ErrorResponse +export const RetrieveContainerFile429 = ErrorResponse +export type DeleteContainerFile200 = { + readonly "object": "container.file.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const DeleteContainerFile200 = Schema.Struct({ + "object": Schema.Literal("container.file.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String +}) +export type DeleteContainerFile404 = ErrorResponse +export const DeleteContainerFile404 = ErrorResponse +export type DeleteContainerFile429 = ErrorResponse +export const DeleteContainerFile429 = ErrorResponse +export type RetrieveContainerFileContent404 = ErrorResponse +export const RetrieveContainerFileContent404 = ErrorResponse +export type RetrieveContainerFileContent429 = ErrorResponse +export const RetrieveContainerFileContent429 = ErrorResponse +export type ListConversationItemsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "include"?: ReadonlyArray +} +export const ListConversationItemsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "include": Schema.optionalKey(Schema.Array(IncludeEnum)) +}) +export type ListConversationItems200 = ConversationItemList +export const ListConversationItems200 = ConversationItemList +export type ListConversationItems400 = ErrorResponse +export const ListConversationItems400 = ErrorResponse +export type ListConversationItems404 = ErrorResponse +export const ListConversationItems404 = ErrorResponse +export type ListConversationItems429 = ErrorResponse +export const ListConversationItems429 = ErrorResponse +export type CreateConversationItemsParams = { readonly "include"?: ReadonlyArray } +export const CreateConversationItemsParams = Schema.Struct({ "include": Schema.optionalKey(Schema.Array(IncludeEnum)) }) +export type CreateConversationItemsRequestJson = + | null + | string + | number + | boolean + | { readonly "items": ReadonlyArray } & { readonly [x: string]: Schema.Json } + | ReadonlyArray +export const CreateConversationItemsRequestJson = Schema.Union([ + Schema.Null, + Schema.String, + Schema.Finite, + Schema.Boolean, + Schema.StructWithRest( + Schema.Struct({ + "items": Schema.Array(InputItem).annotate({ + "description": "The items to add to the conversation. You may add up to 20 items at a time.\n" + }).check(Schema.isMaxLength(20).annotate({ "expected": "a value with a length of at most 20" })) + }), + [Schema.Record(Schema.String, Schema.Json.annotate({ "expected": "JSON value" }))] + ), + Schema.Array(Schema.Json.annotate({ "expected": "JSON value" })) +]) +export type CreateConversationItems200 = ConversationItemList +export const CreateConversationItems200 = ConversationItemList +export type CreateConversationItems400 = ErrorResponse +export const CreateConversationItems400 = ErrorResponse +export type CreateConversationItems404 = ErrorResponse +export const CreateConversationItems404 = ErrorResponse +export type CreateConversationItems429 = ErrorResponse +export const CreateConversationItems429 = ErrorResponse +export type CreateConversationItems500 = ErrorResponse +export const CreateConversationItems500 = ErrorResponse +export type GetConversationItemParams = { readonly "include"?: ReadonlyArray } +export const GetConversationItemParams = Schema.Struct({ "include": Schema.optionalKey(Schema.Array(IncludeEnum)) }) +export type GetConversationItem200 = ConversationItem +export const GetConversationItem200 = ConversationItem +export type GetConversationItem404 = ErrorResponse +export const GetConversationItem404 = ErrorResponse +export type GetConversationItem429 = ErrorResponse +export const GetConversationItem429 = ErrorResponse +export type DeleteConversationItem200 = ConversationResource +export const DeleteConversationItem200 = ConversationResource +export type DeleteConversationItem400 = ErrorResponse +export const DeleteConversationItem400 = ErrorResponse +export type DeleteConversationItem404 = ErrorResponse +export const DeleteConversationItem404 = ErrorResponse +export type DeleteConversationItem429 = ErrorResponse +export const DeleteConversationItem429 = ErrorResponse +export type DeleteConversationItem500 = ErrorResponse +export const DeleteConversationItem500 = ErrorResponse +export type CreateEmbeddingRequestJson = CreateEmbeddingRequest +export const CreateEmbeddingRequestJson = CreateEmbeddingRequest +export type CreateEmbedding200 = CreateEmbeddingResponse +export const CreateEmbedding200 = CreateEmbeddingResponse +export type CreateEmbedding400 = ErrorResponse +export const CreateEmbedding400 = ErrorResponse +export type CreateEmbedding401 = ErrorResponse +export const CreateEmbedding401 = ErrorResponse +export type CreateEmbedding429 = ErrorResponse +export const CreateEmbedding429 = ErrorResponse +export type CreateEmbedding500 = ErrorResponse +export const CreateEmbedding500 = ErrorResponse +export type CreateEmbedding503 = ErrorResponse +export const CreateEmbedding503 = ErrorResponse +export type ListEvalsParams = { + readonly "after"?: string + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "order_by"?: "created_at" | "updated_at" +} +export const ListEvalsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "order_by": Schema.optionalKey(Schema.Literals(["created_at", "updated_at"])) +}) +export type ListEvals200 = EvalList +export const ListEvals200 = EvalList +export type ListEvals429 = ErrorResponse +export const ListEvals429 = ErrorResponse +export type CreateEvalRequestJson = CreateEvalRequest +export const CreateEvalRequestJson = CreateEvalRequest +export type CreateEval201 = Eval +export const CreateEval201 = Eval +export type CreateEval400 = ErrorResponse +export const CreateEval400 = ErrorResponse +export type CreateEval429 = ErrorResponse +export const CreateEval429 = ErrorResponse +export type GetEval200 = Eval +export const GetEval200 = Eval +export type GetEval404 = ErrorResponse +export const GetEval404 = ErrorResponse +export type GetEval429 = ErrorResponse +export const GetEval429 = ErrorResponse +export type UpdateEvalRequestJson = { readonly "name"?: string; readonly "metadata"?: Metadata } +export const UpdateEvalRequestJson = Schema.Struct({ + "name": Schema.optionalKey(Schema.String.annotate({ "description": "Rename the evaluation." })), + "metadata": Schema.optionalKey(Metadata) +}) +export type UpdateEval200 = Eval +export const UpdateEval200 = Eval +export type UpdateEval400 = ErrorResponse +export const UpdateEval400 = ErrorResponse +export type UpdateEval404 = ErrorResponse +export const UpdateEval404 = ErrorResponse +export type UpdateEval429 = ErrorResponse +export const UpdateEval429 = ErrorResponse +export type DeleteEval200 = { readonly "object": string; readonly "deleted": boolean; readonly "eval_id": string } +export const DeleteEval200 = Schema.Struct({ + "object": Schema.String, + "deleted": Schema.Boolean, + "eval_id": Schema.String +}) +export type DeleteEval400 = ErrorResponse +export const DeleteEval400 = ErrorResponse +export type DeleteEval404 = ErrorResponse +export const DeleteEval404 = ErrorResponse +export type DeleteEval429 = ErrorResponse +export const DeleteEval429 = ErrorResponse +export type GetEvalRunsParams = { + readonly "after"?: string + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "status"?: "queued" | "in_progress" | "completed" | "canceled" | "failed" +} +export const GetEvalRunsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "status": Schema.optionalKey(Schema.Literals(["queued", "in_progress", "completed", "canceled", "failed"])) +}) +export type GetEvalRuns200 = EvalRunList +export const GetEvalRuns200 = EvalRunList +export type GetEvalRuns400 = ErrorResponse +export const GetEvalRuns400 = ErrorResponse +export type GetEvalRuns404 = ErrorResponse +export const GetEvalRuns404 = ErrorResponse +export type GetEvalRuns429 = ErrorResponse +export const GetEvalRuns429 = ErrorResponse +export type CreateEvalRunRequestJson = CreateEvalRunRequest +export const CreateEvalRunRequestJson = CreateEvalRunRequest +export type CreateEvalRun201 = EvalRun +export const CreateEvalRun201 = EvalRun +export type CreateEvalRun400 = ErrorResponse +export const CreateEvalRun400 = ErrorResponse +export type CreateEvalRun404 = ErrorResponse +export const CreateEvalRun404 = ErrorResponse +export type CreateEvalRun429 = ErrorResponse +export const CreateEvalRun429 = ErrorResponse +export type CreateEvalRun500 = ErrorResponse +export const CreateEvalRun500 = ErrorResponse +export type GetEvalRun200 = EvalRun +export const GetEvalRun200 = EvalRun +export type GetEvalRun400 = ErrorResponse +export const GetEvalRun400 = ErrorResponse +export type GetEvalRun404 = ErrorResponse +export const GetEvalRun404 = ErrorResponse +export type GetEvalRun429 = ErrorResponse +export const GetEvalRun429 = ErrorResponse +export type DeleteEvalRun200 = { readonly "object"?: string; readonly "deleted"?: boolean; readonly "run_id"?: string } +export const DeleteEvalRun200 = Schema.Struct({ + "object": Schema.optionalKey(Schema.String), + "deleted": Schema.optionalKey(Schema.Boolean), + "run_id": Schema.optionalKey(Schema.String) +}) +export type DeleteEvalRun400 = ErrorResponse +export const DeleteEvalRun400 = ErrorResponse +export type DeleteEvalRun404 = ErrorResponse +export const DeleteEvalRun404 = ErrorResponse +export type DeleteEvalRun429 = ErrorResponse +export const DeleteEvalRun429 = ErrorResponse +export type CancelEvalRun200 = EvalRun +export const CancelEvalRun200 = EvalRun +export type CancelEvalRun429 = ErrorResponse +export const CancelEvalRun429 = ErrorResponse +export type GetEvalRunOutputItemsParams = { + readonly "after"?: string + readonly "limit"?: number + readonly "status"?: "fail" | "pass" + readonly "order"?: "asc" | "desc" +} +export const GetEvalRunOutputItemsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "status": Schema.optionalKey(Schema.Literals(["fail", "pass"])), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type GetEvalRunOutputItems200 = EvalRunOutputItemList +export const GetEvalRunOutputItems200 = EvalRunOutputItemList +export type GetEvalRunOutputItems400 = ErrorResponse +export const GetEvalRunOutputItems400 = ErrorResponse +export type GetEvalRunOutputItems404 = ErrorResponse +export const GetEvalRunOutputItems404 = ErrorResponse +export type GetEvalRunOutputItems429 = ErrorResponse +export const GetEvalRunOutputItems429 = ErrorResponse +export type GetEvalRunOutputItem200 = EvalRunOutputItem +export const GetEvalRunOutputItem200 = EvalRunOutputItem +export type GetEvalRunOutputItem400 = ErrorResponse +export const GetEvalRunOutputItem400 = ErrorResponse +export type GetEvalRunOutputItem404 = ErrorResponse +export const GetEvalRunOutputItem404 = ErrorResponse +export type GetEvalRunOutputItem429 = ErrorResponse +export const GetEvalRunOutputItem429 = ErrorResponse +export type ListFilesParams = { + readonly "purpose"?: string + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string +} +export const ListFilesParams = Schema.Struct({ + "purpose": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String) +}) +export type ListFiles200 = ListFilesResponse +export const ListFiles200 = ListFilesResponse +export type ListFiles400 = ErrorResponse +export const ListFiles400 = ErrorResponse +export type ListFiles404 = ErrorResponse +export const ListFiles404 = ErrorResponse +export type ListFiles429 = ErrorResponse +export const ListFiles429 = ErrorResponse +export type CreateFileRequestFormData = CreateFileRequestMultipart +export const CreateFileRequestFormData = CreateFileRequestMultipart +export type CreateFile200 = OpenAIFile +export const CreateFile200 = OpenAIFile +export type CreateFile400 = ErrorResponse +export const CreateFile400 = ErrorResponse +export type CreateFile429 = ErrorResponse +export const CreateFile429 = ErrorResponse +export type CreateFile500 = ErrorResponse +export const CreateFile500 = ErrorResponse +export type RetrieveFile200 = OpenAIFile +export const RetrieveFile200 = OpenAIFile +export type RetrieveFile404 = ErrorResponse +export const RetrieveFile404 = ErrorResponse +export type RetrieveFile429 = ErrorResponse +export const RetrieveFile429 = ErrorResponse +export type DeleteFile200 = DeleteFileResponse +export const DeleteFile200 = DeleteFileResponse +export type DeleteFile404 = ErrorResponse +export const DeleteFile404 = ErrorResponse +export type DeleteFile429 = ErrorResponse +export const DeleteFile429 = ErrorResponse +export type DownloadFile400 = ErrorResponse +export const DownloadFile400 = ErrorResponse +export type DownloadFile404 = ErrorResponse +export const DownloadFile404 = ErrorResponse +export type DownloadFile429 = ErrorResponse +export const DownloadFile429 = ErrorResponse +export type DownloadFile500 = ErrorResponse +export const DownloadFile500 = ErrorResponse +export type RunGraderRequestJson = RunGraderRequest +export const RunGraderRequestJson = RunGraderRequest +export type RunGrader200 = RunGraderResponse +export const RunGrader200 = RunGraderResponse +export type RunGrader400 = ErrorResponse +export const RunGrader400 = ErrorResponse +export type RunGrader429 = ErrorResponse +export const RunGrader429 = ErrorResponse +export type ValidateGraderRequestJson = ValidateGraderRequest +export const ValidateGraderRequestJson = ValidateGraderRequest +export type ValidateGrader200 = ValidateGraderResponse +export const ValidateGrader200 = ValidateGraderResponse +export type ValidateGrader400 = ErrorResponse +export const ValidateGrader400 = ErrorResponse +export type ValidateGrader429 = ErrorResponse +export const ValidateGrader429 = ErrorResponse +export type ListFineTuningCheckpointPermissionsParams = { + readonly "project_id"?: string + readonly "after"?: string + readonly "limit"?: number + readonly "order"?: "ascending" | "descending" +} +export const ListFineTuningCheckpointPermissionsParams = Schema.Struct({ + "project_id": Schema.optionalKey(Schema.String), + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["ascending", "descending"])) +}) +export type ListFineTuningCheckpointPermissions200 = ListFineTuningCheckpointPermissionResponse +export const ListFineTuningCheckpointPermissions200 = ListFineTuningCheckpointPermissionResponse +export type ListFineTuningCheckpointPermissions400 = ErrorResponse +export const ListFineTuningCheckpointPermissions400 = ErrorResponse +export type ListFineTuningCheckpointPermissions429 = ErrorResponse +export const ListFineTuningCheckpointPermissions429 = ErrorResponse +export type CreateFineTuningCheckpointPermissionRequestJson = CreateFineTuningCheckpointPermissionRequest +export const CreateFineTuningCheckpointPermissionRequestJson = CreateFineTuningCheckpointPermissionRequest +export type CreateFineTuningCheckpointPermission200 = ListFineTuningCheckpointPermissionResponse +export const CreateFineTuningCheckpointPermission200 = ListFineTuningCheckpointPermissionResponse +export type CreateFineTuningCheckpointPermission400 = ErrorResponse +export const CreateFineTuningCheckpointPermission400 = ErrorResponse +export type CreateFineTuningCheckpointPermission404 = ErrorResponse +export const CreateFineTuningCheckpointPermission404 = ErrorResponse +export type CreateFineTuningCheckpointPermission429 = ErrorResponse +export const CreateFineTuningCheckpointPermission429 = ErrorResponse +export type DeleteFineTuningCheckpointPermission200 = DeleteFineTuningCheckpointPermissionResponse +export const DeleteFineTuningCheckpointPermission200 = DeleteFineTuningCheckpointPermissionResponse +export type DeleteFineTuningCheckpointPermission400 = ErrorResponse +export const DeleteFineTuningCheckpointPermission400 = ErrorResponse +export type DeleteFineTuningCheckpointPermission404 = ErrorResponse +export const DeleteFineTuningCheckpointPermission404 = ErrorResponse +export type DeleteFineTuningCheckpointPermission429 = ErrorResponse +export const DeleteFineTuningCheckpointPermission429 = ErrorResponse +export type ListPaginatedFineTuningJobsParams = { + readonly "after"?: string + readonly "limit"?: number + readonly "metadata"?: { readonly [x: string]: string } | null +} +export const ListPaginatedFineTuningJobsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "metadata": Schema.optionalKey(Schema.Union([Schema.Record(Schema.String, Schema.String), Schema.Null])) +}) +export type ListPaginatedFineTuningJobs200 = ListPaginatedFineTuningJobsResponse +export const ListPaginatedFineTuningJobs200 = ListPaginatedFineTuningJobsResponse +export type ListPaginatedFineTuningJobs400 = ErrorResponse +export const ListPaginatedFineTuningJobs400 = ErrorResponse +export type ListPaginatedFineTuningJobs429 = ErrorResponse +export const ListPaginatedFineTuningJobs429 = ErrorResponse +export type CreateFineTuningJobRequestJson = CreateFineTuningJobRequest +export const CreateFineTuningJobRequestJson = CreateFineTuningJobRequest +export type CreateFineTuningJob200 = FineTuningJob +export const CreateFineTuningJob200 = FineTuningJob +export type CreateFineTuningJob400 = ErrorResponse +export const CreateFineTuningJob400 = ErrorResponse +export type CreateFineTuningJob429 = ErrorResponse +export const CreateFineTuningJob429 = ErrorResponse +export type RetrieveFineTuningJob200 = FineTuningJob +export const RetrieveFineTuningJob200 = FineTuningJob +export type RetrieveFineTuningJob404 = ErrorResponse +export const RetrieveFineTuningJob404 = ErrorResponse +export type RetrieveFineTuningJob429 = ErrorResponse +export const RetrieveFineTuningJob429 = ErrorResponse +export type CancelFineTuningJob200 = FineTuningJob +export const CancelFineTuningJob200 = FineTuningJob +export type CancelFineTuningJob400 = ErrorResponse +export const CancelFineTuningJob400 = ErrorResponse +export type CancelFineTuningJob404 = ErrorResponse +export const CancelFineTuningJob404 = ErrorResponse +export type CancelFineTuningJob429 = ErrorResponse +export const CancelFineTuningJob429 = ErrorResponse +export type ListFineTuningJobCheckpointsParams = { readonly "after"?: string; readonly "limit"?: number } +export const ListFineTuningJobCheckpointsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) +}) +export type ListFineTuningJobCheckpoints200 = ListFineTuningJobCheckpointsResponse +export const ListFineTuningJobCheckpoints200 = ListFineTuningJobCheckpointsResponse +export type ListFineTuningJobCheckpoints400 = ErrorResponse +export const ListFineTuningJobCheckpoints400 = ErrorResponse +export type ListFineTuningJobCheckpoints404 = ErrorResponse +export const ListFineTuningJobCheckpoints404 = ErrorResponse +export type ListFineTuningJobCheckpoints429 = ErrorResponse +export const ListFineTuningJobCheckpoints429 = ErrorResponse +export type ListFineTuningEventsParams = { readonly "after"?: string; readonly "limit"?: number } +export const ListFineTuningEventsParams = Schema.Struct({ + "after": Schema.optionalKey(Schema.String), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))) +}) +export type ListFineTuningEvents200 = ListFineTuningJobEventsResponse +export const ListFineTuningEvents200 = ListFineTuningJobEventsResponse +export type ListFineTuningEvents400 = ErrorResponse +export const ListFineTuningEvents400 = ErrorResponse +export type ListFineTuningEvents404 = ErrorResponse +export const ListFineTuningEvents404 = ErrorResponse +export type ListFineTuningEvents429 = ErrorResponse +export const ListFineTuningEvents429 = ErrorResponse +export type PauseFineTuningJob200 = FineTuningJob +export const PauseFineTuningJob200 = FineTuningJob +export type PauseFineTuningJob400 = ErrorResponse +export const PauseFineTuningJob400 = ErrorResponse +export type PauseFineTuningJob404 = ErrorResponse +export const PauseFineTuningJob404 = ErrorResponse +export type PauseFineTuningJob429 = ErrorResponse +export const PauseFineTuningJob429 = ErrorResponse +export type ResumeFineTuningJob200 = FineTuningJob +export const ResumeFineTuningJob200 = FineTuningJob +export type ResumeFineTuningJob400 = ErrorResponse +export const ResumeFineTuningJob400 = ErrorResponse +export type ResumeFineTuningJob404 = ErrorResponse +export const ResumeFineTuningJob404 = ErrorResponse +export type ResumeFineTuningJob429 = ErrorResponse +export const ResumeFineTuningJob429 = ErrorResponse +export type CreateImageEditRequestJson = EditImageBodyJsonParam +export const CreateImageEditRequestJson = EditImageBodyJsonParam +export type CreateImageEditRequestFormData = CreateImageEditRequestMultipart +export const CreateImageEditRequestFormData = CreateImageEditRequestMultipart +export type CreateImageEdit200 = ImagesResponse +export const CreateImageEdit200 = ImagesResponse +export type CreateImageEdit200Sse = ImageEditStreamEvent +export const CreateImageEdit200Sse = ImageEditStreamEvent +export type CreateImageEdit400 = ErrorResponse +export const CreateImageEdit400 = ErrorResponse +export type CreateImageEdit403 = ErrorResponse +export const CreateImageEdit403 = ErrorResponse +export type CreateImageEdit410 = ErrorResponse +export const CreateImageEdit410 = ErrorResponse +export type CreateImageEdit429 = ErrorResponse +export const CreateImageEdit429 = ErrorResponse +export type CreateImageEdit500 = ErrorResponse +export const CreateImageEdit500 = ErrorResponse +export type CreateImageEdit503 = ErrorResponse +export const CreateImageEdit503 = ErrorResponse +export type CreateImageRequestJson = CreateImageRequest +export const CreateImageRequestJson = CreateImageRequest +export type CreateImage200 = ImagesResponse +export const CreateImage200 = ImagesResponse +export type CreateImage200Sse = ImageGenStreamEvent +export const CreateImage200Sse = ImageGenStreamEvent +export type CreateImage400 = ErrorResponse +export const CreateImage400 = ErrorResponse +export type CreateImage403 = ErrorResponse +export const CreateImage403 = ErrorResponse +export type CreateImage410 = ErrorResponse +export const CreateImage410 = ErrorResponse +export type CreateImage429 = ErrorResponse +export const CreateImage429 = ErrorResponse +export type CreateImage500 = ErrorResponse +export const CreateImage500 = ErrorResponse +export type CreateImage503 = ErrorResponse +export const CreateImage503 = ErrorResponse +export type CreateImageVariationRequestFormData = CreateImageVariationRequestMultipart +export const CreateImageVariationRequestFormData = CreateImageVariationRequestMultipart +export type CreateImageVariation200 = ImagesResponse +export const CreateImageVariation200 = ImagesResponse +export type CreateImageVariation400 = ErrorResponse +export const CreateImageVariation400 = ErrorResponse +export type CreateImageVariation403 = ErrorResponse +export const CreateImageVariation403 = ErrorResponse +export type CreateImageVariation410 = ErrorResponse +export const CreateImageVariation410 = ErrorResponse +export type CreateImageVariation429 = ErrorResponse +export const CreateImageVariation429 = ErrorResponse +export type CreateImageVariation500 = ErrorResponse +export const CreateImageVariation500 = ErrorResponse +export type CreateLiveRequestJson = LiveSessionCreateRequest +export const CreateLiveRequestJson = LiveSessionCreateRequest +export type CreateLive201 = LiveSessionCreateResponse +export const CreateLive201 = LiveSessionCreateResponse +export type CreateLive400 = ErrorResponse +export const CreateLive400 = ErrorResponse +export type CreateLive413 = ErrorResponse +export const CreateLive413 = ErrorResponse +export type CreateLive429 = ErrorResponse +export const CreateLive429 = ErrorResponse +export type CreateLive503 = ErrorResponse +export const CreateLive503 = ErrorResponse +export type CreateLive504 = ErrorResponse +export const CreateLive504 = ErrorResponse +export type AcceptLiveSessionRequestJson = LiveCallAcceptRequest +export const AcceptLiveSessionRequestJson = LiveCallAcceptRequest +export type AcceptLiveSession400 = ErrorResponse +export const AcceptLiveSession400 = ErrorResponse +export type AcceptLiveSession404 = ErrorResponse +export const AcceptLiveSession404 = ErrorResponse +export type AcceptLiveSession409 = ErrorResponse +export const AcceptLiveSession409 = ErrorResponse +export type AcceptLiveSession429 = ErrorResponse +export const AcceptLiveSession429 = ErrorResponse +export type AcceptLiveSession503 = ErrorResponse +export const AcceptLiveSession503 = ErrorResponse +export type ForkLiveSessionRequestJson = LiveForkRequest +export const ForkLiveSessionRequestJson = LiveForkRequest +export type ForkLiveSession201 = LiveCreateResponse +export const ForkLiveSession201 = LiveCreateResponse +export type ForkLiveSession400 = ErrorResponse +export const ForkLiveSession400 = ErrorResponse +export type ForkLiveSession413 = ErrorResponse +export const ForkLiveSession413 = ErrorResponse +export type ForkLiveSession429 = ErrorResponse +export const ForkLiveSession429 = ErrorResponse +export type ForkLiveSession503 = ErrorResponse +export const ForkLiveSession503 = ErrorResponse +export type HangupLiveSession404 = ErrorResponse +export const HangupLiveSession404 = ErrorResponse +export type ReferLiveSessionRequestJson = LiveCallReferRequest +export const ReferLiveSessionRequestJson = LiveCallReferRequest +export type ReferLiveSession400 = ErrorResponse +export const ReferLiveSession400 = ErrorResponse +export type ReferLiveSession404 = ErrorResponse +export const ReferLiveSession404 = ErrorResponse +export type RejectLiveSessionRequestJson = LiveCallRejectRequest +export const RejectLiveSessionRequestJson = LiveCallRejectRequest +export type RejectLiveSession400 = ErrorResponse +export const RejectLiveSession400 = ErrorResponse +export type RejectLiveSession404 = ErrorResponse +export const RejectLiveSession404 = ErrorResponse +export type RejectLiveSession409 = ErrorResponse +export const RejectLiveSession409 = ErrorResponse +export type ListModels200 = ListModelsResponse +export const ListModels200 = ListModelsResponse +export type ListModels403 = PermissionErrorResponse +export const ListModels403 = PermissionErrorResponse +export type RetrieveModel200 = Model +export const RetrieveModel200 = Model +export type RetrieveModel403 = PermissionErrorResponse +export const RetrieveModel403 = PermissionErrorResponse +export type RetrieveModel404 = ErrorResponse +export const RetrieveModel404 = ErrorResponse +export type DeleteModel200 = DeleteModelResponse +export const DeleteModel200 = DeleteModelResponse +export type DeleteModel403 = PermissionErrorResponse +export const DeleteModel403 = PermissionErrorResponse +export type DeleteModel404 = ErrorResponse +export const DeleteModel404 = ErrorResponse +export type CreateModerationRequestJson = CreateModerationRequest +export const CreateModerationRequestJson = CreateModerationRequest +export type CreateModeration200 = CreateModerationResponse +export const CreateModeration200 = CreateModerationResponse +export type CreateModeration400 = ErrorResponse +export const CreateModeration400 = ErrorResponse +export type CreateModeration403 = ErrorResponse +export const CreateModeration403 = ErrorResponse +export type CreateModeration429 = ErrorResponse +export const CreateModeration429 = ErrorResponse +export type CreateModeration500 = ErrorResponse +export const CreateModeration500 = ErrorResponse +export type CreateModeration503 = ErrorResponse +export const CreateModeration503 = ErrorResponse +export type AdminApiKeysListParams = { + readonly "after"?: string | null + readonly "order"?: "asc" | "desc" + readonly "limit"?: number +} +export const AdminApiKeysListParams = Schema.Struct({ + "after": Schema.optionalKey( + Schema.Union([Schema.String, Schema.Null]).annotate({ + "description": "Return keys with IDs that come after this ID in the pagination order." + }) + ), + "order": Schema.optionalKey( + Schema.Literals(["asc", "desc"]).annotate({ + "description": "Order results by creation time, ascending or descending." + }) + ), + "limit": Schema.optionalKey( + Schema.Number.annotate({ "description": "Maximum number of keys to return." }).check( + Schema.isInt().annotate({ "expected": "an integer" }) + ) + ) +}) +export type AdminApiKeysList200 = ApiKeyList +export const AdminApiKeysList200 = ApiKeyList +export type AdminApiKeysList400 = ErrorResponse +export const AdminApiKeysList400 = ErrorResponse +export type AdminApiKeysList403 = PermissionErrorResponse +export const AdminApiKeysList403 = PermissionErrorResponse +export type AdminApiKeysCreateRequestJson = { readonly "name": string; readonly "expires_in_seconds"?: number } +export const AdminApiKeysCreateRequestJson = Schema.Struct({ + "name": Schema.String, + "expires_in_seconds": Schema.optionalKey( + Schema.Number.annotate({ + "description": "The number of seconds until the API key expires. Omit this field for a key that does not expire." + }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(31536000).annotate({ "expected": "a value less than or equal to 31536000" })) + ) +}) +export type AdminApiKeysCreate200 = AdminApiKeyCreateResponse +export const AdminApiKeysCreate200 = AdminApiKeyCreateResponse +export type AdminApiKeysCreate400 = ErrorResponse +export const AdminApiKeysCreate400 = ErrorResponse +export type AdminApiKeysCreate403 = PermissionErrorResponse +export const AdminApiKeysCreate403 = PermissionErrorResponse +export type AdminApiKeysCreate404 = ErrorResponse +export const AdminApiKeysCreate404 = ErrorResponse +export type AdminApiKeysGet200 = AdminApiKey +export const AdminApiKeysGet200 = AdminApiKey +export type AdminApiKeysGet403 = PermissionErrorResponse +export const AdminApiKeysGet403 = PermissionErrorResponse +export type AdminApiKeysGet404 = ErrorResponse +export const AdminApiKeysGet404 = ErrorResponse +export type AdminApiKeysDelete200 = { + readonly "id": string + readonly "object": "organization.admin_api_key.deleted" + readonly "deleted": boolean +} +export const AdminApiKeysDelete200 = Schema.Struct({ + "id": Schema.String, + "object": Schema.Literal("organization.admin_api_key.deleted"), + "deleted": Schema.Boolean +}) +export type AdminApiKeysDelete403 = PermissionErrorResponse +export const AdminApiKeysDelete403 = PermissionErrorResponse +export type AdminApiKeysDelete404 = ErrorResponse +export const AdminApiKeysDelete404 = ErrorResponse +export type ListAuditLogsParams = { + readonly "effective_at[gt]"?: number + readonly "effective_at[gte]"?: number + readonly "effective_at[lt]"?: number + readonly "effective_at[lte]"?: number + readonly "project_ids[]"?: ReadonlyArray + readonly "event_types[]"?: ReadonlyArray + readonly "actor_ids[]"?: ReadonlyArray + readonly "actor_emails[]"?: ReadonlyArray + readonly "resource_ids[]"?: ReadonlyArray + readonly "tenant_only"?: boolean + readonly "limit"?: number + readonly "after"?: string + readonly "before"?: string +} +export const ListAuditLogsParams = Schema.Struct({ + "effective_at[gt]": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Return only events whose `effective_at` (Unix seconds) is greater than this value." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "effective_at[gte]": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Return only events whose `effective_at` (Unix seconds) is greater than or equal to this value." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "effective_at[lt]": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Return only events whose `effective_at` (Unix seconds) is less than this value." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "effective_at[lte]": Schema.optionalKey( + Schema.Number.annotate({ + "description": "Return only events whose `effective_at` (Unix seconds) is less than or equal to this value." + }).check(Schema.isInt().annotate({ "expected": "an integer" })) + ), + "project_ids[]": Schema.optionalKey(Schema.Array(Schema.String)), + "event_types[]": Schema.optionalKey(Schema.Array(AuditLogEventType)), + "actor_ids[]": Schema.optionalKey(Schema.Array(Schema.String)), + "actor_emails[]": Schema.optionalKey(Schema.Array(Schema.String)), + "resource_ids[]": Schema.optionalKey(Schema.Array(Schema.String)), + "tenant_only": Schema.optionalKey(Schema.Boolean), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListAuditLogs200 = ListAuditLogsResponse +export const ListAuditLogs200 = ListAuditLogsResponse +export type ListAuditLogs400 = ErrorResponse +export const ListAuditLogs400 = ErrorResponse +export type ListAuditLogs404 = ErrorResponse +export const ListAuditLogs404 = ErrorResponse +export type ListAuditLogs429 = ErrorResponse +export const ListAuditLogs429 = ErrorResponse +export type ListOrganizationCertificatesParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListOrganizationCertificatesParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListOrganizationCertificates200 = ListCertificatesResponse +export const ListOrganizationCertificates200 = ListCertificatesResponse +export type ListOrganizationCertificates400 = ErrorResponse +export const ListOrganizationCertificates400 = ErrorResponse +export type ListOrganizationCertificates403 = PermissionErrorResponse +export const ListOrganizationCertificates403 = PermissionErrorResponse +export type ListOrganizationCertificates429 = ErrorResponse +export const ListOrganizationCertificates429 = ErrorResponse +export type UploadCertificateRequestJson = UploadCertificateRequest +export const UploadCertificateRequestJson = UploadCertificateRequest +export type UploadCertificate200 = Certificate +export const UploadCertificate200 = Certificate +export type UploadCertificate400 = ErrorResponse +export const UploadCertificate400 = ErrorResponse +export type UploadCertificate403 = PermissionErrorResponse +export const UploadCertificate403 = PermissionErrorResponse +export type UploadCertificate429 = ErrorResponse +export const UploadCertificate429 = ErrorResponse +export type ActivateOrganizationCertificatesRequestJson = ToggleCertificatesRequest +export const ActivateOrganizationCertificatesRequestJson = ToggleCertificatesRequest +export type ActivateOrganizationCertificates200 = OrganizationCertificateActivationResponse +export const ActivateOrganizationCertificates200 = OrganizationCertificateActivationResponse +export type ActivateOrganizationCertificates400 = ErrorResponse +export const ActivateOrganizationCertificates400 = ErrorResponse +export type ActivateOrganizationCertificates403 = PermissionErrorResponse +export const ActivateOrganizationCertificates403 = PermissionErrorResponse +export type ActivateOrganizationCertificates404 = ErrorResponse +export const ActivateOrganizationCertificates404 = ErrorResponse +export type ActivateOrganizationCertificates429 = ErrorResponse +export const ActivateOrganizationCertificates429 = ErrorResponse +export type ActivateOrganizationCertificates500 = ErrorResponse +export const ActivateOrganizationCertificates500 = ErrorResponse +export type DeactivateOrganizationCertificatesRequestJson = ToggleCertificatesRequest +export const DeactivateOrganizationCertificatesRequestJson = ToggleCertificatesRequest +export type DeactivateOrganizationCertificates200 = OrganizationCertificateDeactivationResponse +export const DeactivateOrganizationCertificates200 = OrganizationCertificateDeactivationResponse +export type DeactivateOrganizationCertificates400 = ErrorResponse +export const DeactivateOrganizationCertificates400 = ErrorResponse +export type DeactivateOrganizationCertificates403 = PermissionErrorResponse +export const DeactivateOrganizationCertificates403 = PermissionErrorResponse +export type DeactivateOrganizationCertificates404 = ErrorResponse +export const DeactivateOrganizationCertificates404 = ErrorResponse +export type DeactivateOrganizationCertificates429 = ErrorResponse +export const DeactivateOrganizationCertificates429 = ErrorResponse +export type DeactivateOrganizationCertificates500 = ErrorResponse +export const DeactivateOrganizationCertificates500 = ErrorResponse +export type GetCertificateParams = { readonly "include"?: ReadonlyArray<"content"> } +export const GetCertificateParams = Schema.Struct({ + "include": Schema.optionalKey(Schema.Array(Schema.Literal("content"))) +}) +export type GetCertificate200 = Certificate +export const GetCertificate200 = Certificate +export type GetCertificate400 = ErrorResponse +export const GetCertificate400 = ErrorResponse +export type GetCertificate403 = PermissionErrorResponse +export const GetCertificate403 = PermissionErrorResponse +export type GetCertificate404 = ErrorResponse +export const GetCertificate404 = ErrorResponse +export type GetCertificate429 = ErrorResponse +export const GetCertificate429 = ErrorResponse +export type ModifyCertificateRequestJson = ModifyCertificateRequest +export const ModifyCertificateRequestJson = ModifyCertificateRequest +export type ModifyCertificate200 = Certificate +export const ModifyCertificate200 = Certificate +export type ModifyCertificate400 = ErrorResponse +export const ModifyCertificate400 = ErrorResponse +export type ModifyCertificate403 = PermissionErrorResponse +export const ModifyCertificate403 = PermissionErrorResponse +export type ModifyCertificate404 = ErrorResponse +export const ModifyCertificate404 = ErrorResponse +export type ModifyCertificate429 = ErrorResponse +export const ModifyCertificate429 = ErrorResponse +export type ModifyCertificate500 = ErrorResponse +export const ModifyCertificate500 = ErrorResponse +export type DeleteCertificate200 = DeleteCertificateResponse +export const DeleteCertificate200 = DeleteCertificateResponse +export type DeleteCertificate400 = ErrorResponse +export const DeleteCertificate400 = ErrorResponse +export type DeleteCertificate403 = PermissionErrorResponse +export const DeleteCertificate403 = PermissionErrorResponse +export type DeleteCertificate404 = ErrorResponse +export const DeleteCertificate404 = ErrorResponse +export type DeleteCertificate429 = ErrorResponse +export const DeleteCertificate429 = ErrorResponse +export type UsageCostsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1d" + readonly "project_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "line_items"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "line_item" | "api_key_id" | "api_source"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageCostsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literal("1d")), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "line_items": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey( + Schema.Array(Schema.Literals(["project_id", "user_id", "line_item", "api_key_id", "api_source"])) + ), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageCosts200 = UsageResponse +export const UsageCosts200 = UsageResponse +export type UsageCosts400 = ErrorResponse +export const UsageCosts400 = ErrorResponse +export type UsageCosts403 = PermissionErrorResponse +export const UsageCosts403 = PermissionErrorResponse +export type UsageCosts429 = ErrorResponse +export const UsageCosts429 = ErrorResponse +export type RetrieveOrganizationDataRetention200 = OrganizationDataRetention +export const RetrieveOrganizationDataRetention200 = OrganizationDataRetention +export type RetrieveOrganizationDataRetention403 = PermissionErrorResponse +export const RetrieveOrganizationDataRetention403 = PermissionErrorResponse +export type RetrieveOrganizationDataRetention404 = ErrorResponse +export const RetrieveOrganizationDataRetention404 = ErrorResponse +export type UpdateOrganizationDataRetentionRequestJson = UpdateOrganizationDataRetentionBody +export const UpdateOrganizationDataRetentionRequestJson = UpdateOrganizationDataRetentionBody +export type UpdateOrganizationDataRetention200 = OrganizationDataRetention +export const UpdateOrganizationDataRetention200 = OrganizationDataRetention +export type UpdateOrganizationDataRetention400 = ErrorResponse +export const UpdateOrganizationDataRetention400 = ErrorResponse +export type UpdateOrganizationDataRetention403 = PermissionErrorResponse +export const UpdateOrganizationDataRetention403 = PermissionErrorResponse +export type UpdateOrganizationDataRetention404 = ErrorResponse +export const UpdateOrganizationDataRetention404 = ErrorResponse +export type ListGroupsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListGroupsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListGroups200 = GroupListResource +export const ListGroups200 = GroupListResource +export type ListGroups400 = ErrorResponse +export const ListGroups400 = ErrorResponse +export type ListGroups403 = PermissionErrorResponse +export const ListGroups403 = PermissionErrorResponse +export type ListGroups404 = ErrorResponse +export const ListGroups404 = ErrorResponse +export type ListGroups429 = ErrorResponse +export const ListGroups429 = ErrorResponse +export type CreateGroupRequestJson = CreateGroupBody +export const CreateGroupRequestJson = CreateGroupBody +export type CreateGroup200 = GroupResponse +export const CreateGroup200 = GroupResponse +export type CreateGroup400 = ErrorResponse +export const CreateGroup400 = ErrorResponse +export type CreateGroup403 = PermissionErrorResponse +export const CreateGroup403 = PermissionErrorResponse +export type CreateGroup404 = ErrorResponse +export const CreateGroup404 = ErrorResponse +export type CreateGroup422 = ErrorResponse +export const CreateGroup422 = ErrorResponse +export type CreateGroup429 = ErrorResponse +export const CreateGroup429 = ErrorResponse +export type CreateGroup500 = ErrorResponse +export const CreateGroup500 = ErrorResponse +export type RetrieveGroup200 = GroupResponse +export const RetrieveGroup200 = GroupResponse +export type RetrieveGroup400 = ErrorResponse +export const RetrieveGroup400 = ErrorResponse +export type RetrieveGroup403 = PermissionErrorResponse +export const RetrieveGroup403 = PermissionErrorResponse +export type RetrieveGroup404 = ErrorResponse +export const RetrieveGroup404 = ErrorResponse +export type RetrieveGroup429 = ErrorResponse +export const RetrieveGroup429 = ErrorResponse +export type RetrieveGroup500 = ErrorResponse +export const RetrieveGroup500 = ErrorResponse +export type UpdateGroupRequestJson = UpdateGroupBody +export const UpdateGroupRequestJson = UpdateGroupBody +export type UpdateGroup200 = GroupResourceWithSuccess +export const UpdateGroup200 = GroupResourceWithSuccess +export type UpdateGroup400 = ErrorResponse +export const UpdateGroup400 = ErrorResponse +export type UpdateGroup403 = PermissionErrorResponse +export const UpdateGroup403 = PermissionErrorResponse +export type UpdateGroup404 = ErrorResponse +export const UpdateGroup404 = ErrorResponse +export type UpdateGroup429 = ErrorResponse +export const UpdateGroup429 = ErrorResponse +export type UpdateGroup500 = ErrorResponse +export const UpdateGroup500 = ErrorResponse +export type DeleteGroup200 = GroupDeletedResource +export const DeleteGroup200 = GroupDeletedResource +export type DeleteGroup400 = ErrorResponse +export const DeleteGroup400 = ErrorResponse +export type DeleteGroup403 = PermissionErrorResponse +export const DeleteGroup403 = PermissionErrorResponse +export type DeleteGroup404 = ErrorResponse +export const DeleteGroup404 = ErrorResponse +export type DeleteGroup429 = ErrorResponse +export const DeleteGroup429 = ErrorResponse +export type ListGroupRoleAssignmentsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListGroupRoleAssignmentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListGroupRoleAssignments200 = RoleListResource +export const ListGroupRoleAssignments200 = RoleListResource +export type ListGroupRoleAssignments400 = ErrorResponse +export const ListGroupRoleAssignments400 = ErrorResponse +export type ListGroupRoleAssignments403 = PermissionErrorResponse +export const ListGroupRoleAssignments403 = PermissionErrorResponse +export type ListGroupRoleAssignments404 = ErrorResponse +export const ListGroupRoleAssignments404 = ErrorResponse +export type ListGroupRoleAssignments429 = ErrorResponse +export const ListGroupRoleAssignments429 = ErrorResponse +export type AssignGroupRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export const AssignGroupRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export type AssignGroupRole200 = GroupRoleAssignment +export const AssignGroupRole200 = GroupRoleAssignment +export type AssignGroupRole400 = ErrorResponse +export const AssignGroupRole400 = ErrorResponse +export type AssignGroupRole403 = PermissionErrorResponse +export const AssignGroupRole403 = PermissionErrorResponse +export type AssignGroupRole404 = ErrorResponse +export const AssignGroupRole404 = ErrorResponse +export type AssignGroupRole429 = ErrorResponse +export const AssignGroupRole429 = ErrorResponse +export type RetrieveGroupRole200 = AssignedRoleDetails +export const RetrieveGroupRole200 = AssignedRoleDetails +export type RetrieveGroupRole400 = ErrorResponse +export const RetrieveGroupRole400 = ErrorResponse +export type RetrieveGroupRole403 = PermissionErrorResponse +export const RetrieveGroupRole403 = PermissionErrorResponse +export type RetrieveGroupRole404 = ErrorResponse +export const RetrieveGroupRole404 = ErrorResponse +export type RetrieveGroupRole429 = ErrorResponse +export const RetrieveGroupRole429 = ErrorResponse +export type UnassignGroupRole200 = DeletedRoleAssignmentResource +export const UnassignGroupRole200 = DeletedRoleAssignmentResource +export type UnassignGroupRole400 = ErrorResponse +export const UnassignGroupRole400 = ErrorResponse +export type UnassignGroupRole403 = PermissionErrorResponse +export const UnassignGroupRole403 = PermissionErrorResponse +export type UnassignGroupRole404 = ErrorResponse +export const UnassignGroupRole404 = ErrorResponse +export type UnassignGroupRole429 = ErrorResponse +export const UnassignGroupRole429 = ErrorResponse +export type ListGroupUsersParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListGroupUsersParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListGroupUsers200 = UserListResource +export const ListGroupUsers200 = UserListResource +export type ListGroupUsers400 = ErrorResponse +export const ListGroupUsers400 = ErrorResponse +export type ListGroupUsers403 = PermissionErrorResponse +export const ListGroupUsers403 = PermissionErrorResponse +export type ListGroupUsers404 = ErrorResponse +export const ListGroupUsers404 = ErrorResponse +export type ListGroupUsers429 = ErrorResponse +export const ListGroupUsers429 = ErrorResponse +export type ListGroupUsers502 = ErrorResponse +export const ListGroupUsers502 = ErrorResponse +export type AddGroupUserRequestJson = CreateGroupUserBody +export const AddGroupUserRequestJson = CreateGroupUserBody +export type AddGroupUser200 = GroupUserAssignment +export const AddGroupUser200 = GroupUserAssignment +export type AddGroupUser400 = ErrorResponse +export const AddGroupUser400 = ErrorResponse +export type AddGroupUser403 = PermissionErrorResponse +export const AddGroupUser403 = PermissionErrorResponse +export type AddGroupUser404 = ErrorResponse +export const AddGroupUser404 = ErrorResponse +export type AddGroupUser409 = ErrorResponse +export const AddGroupUser409 = ErrorResponse +export type AddGroupUser429 = ErrorResponse +export const AddGroupUser429 = ErrorResponse +export type RetrieveGroupUser200 = GroupMemberUser +export const RetrieveGroupUser200 = GroupMemberUser +export type RetrieveGroupUser400 = ErrorResponse +export const RetrieveGroupUser400 = ErrorResponse +export type RetrieveGroupUser403 = PermissionErrorResponse +export const RetrieveGroupUser403 = PermissionErrorResponse +export type RetrieveGroupUser404 = ErrorResponse +export const RetrieveGroupUser404 = ErrorResponse +export type RetrieveGroupUser429 = ErrorResponse +export const RetrieveGroupUser429 = ErrorResponse +export type RemoveGroupUser200 = GroupUserDeletedResource +export const RemoveGroupUser200 = GroupUserDeletedResource +export type RemoveGroupUser400 = ErrorResponse +export const RemoveGroupUser400 = ErrorResponse +export type RemoveGroupUser403 = PermissionErrorResponse +export const RemoveGroupUser403 = PermissionErrorResponse +export type RemoveGroupUser404 = ErrorResponse +export const RemoveGroupUser404 = ErrorResponse +export type RemoveGroupUser409 = ErrorResponse +export const RemoveGroupUser409 = ErrorResponse +export type RemoveGroupUser429 = ErrorResponse +export const RemoveGroupUser429 = ErrorResponse +export type ListInvitesParams = { readonly "limit"?: number; readonly "after"?: string } +export const ListInvitesParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String) +}) +export type ListInvites200 = InviteListResponse +export const ListInvites200 = InviteListResponse +export type ListInvites400 = ErrorResponse +export const ListInvites400 = ErrorResponse +export type ListInvites403 = PermissionErrorResponse +export const ListInvites403 = PermissionErrorResponse +export type ListInvites404 = ErrorResponse +export const ListInvites404 = ErrorResponse +export type ListInvites429 = ErrorResponse +export const ListInvites429 = ErrorResponse +export type InviteUserRequestJson = InviteRequest +export const InviteUserRequestJson = InviteRequest +export type InviteUser200 = Invite +export const InviteUser200 = Invite +export type InviteUser201 = Invite +export const InviteUser201 = Invite +export type InviteUser400 = ErrorResponse +export const InviteUser400 = ErrorResponse +export type InviteUser403 = PermissionErrorResponse +export const InviteUser403 = PermissionErrorResponse +export type InviteUser429 = ErrorResponse +export const InviteUser429 = ErrorResponse +export type RetrieveInvite200 = Invite +export const RetrieveInvite200 = Invite +export type RetrieveInvite400 = ErrorResponse +export const RetrieveInvite400 = ErrorResponse +export type RetrieveInvite403 = PermissionErrorResponse +export const RetrieveInvite403 = PermissionErrorResponse +export type RetrieveInvite404 = ErrorResponse +export const RetrieveInvite404 = ErrorResponse +export type RetrieveInvite429 = ErrorResponse +export const RetrieveInvite429 = ErrorResponse +export type RetrieveInvite500 = ErrorResponse +export const RetrieveInvite500 = ErrorResponse +export type DeleteInvite200 = InviteDeleteResponse +export const DeleteInvite200 = InviteDeleteResponse +export type DeleteInvite400 = ErrorResponse +export const DeleteInvite400 = ErrorResponse +export type DeleteInvite403 = PermissionErrorResponse +export const DeleteInvite403 = PermissionErrorResponse +export type DeleteInvite404 = ErrorResponse +export const DeleteInvite404 = ErrorResponse +export type DeleteInvite429 = ErrorResponse +export const DeleteInvite429 = ErrorResponse +export type ListProjectsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "include_archived"?: boolean +} +export const ListProjectsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "include_archived": Schema.optionalKey(Schema.Boolean) +}) +export type ListProjects200 = ProjectListResponse +export const ListProjects200 = ProjectListResponse +export type ListProjects400 = ErrorResponse +export const ListProjects400 = ErrorResponse +export type ListProjects403 = PermissionErrorResponse +export const ListProjects403 = PermissionErrorResponse +export type ListProjects404 = ErrorResponse +export const ListProjects404 = ErrorResponse +export type ListProjects429 = ErrorResponse +export const ListProjects429 = ErrorResponse +export type CreateProjectRequestJson = ProjectCreateRequest +export const CreateProjectRequestJson = ProjectCreateRequest +export type CreateProject200 = Project +export const CreateProject200 = Project +export type CreateProject201 = Project +export const CreateProject201 = Project +export type CreateProject400 = ErrorResponse +export const CreateProject400 = ErrorResponse +export type CreateProject403 = PermissionErrorResponse +export const CreateProject403 = PermissionErrorResponse +export type CreateProject429 = ErrorResponse +export const CreateProject429 = ErrorResponse +export type RetrieveProject200 = Project +export const RetrieveProject200 = Project +export type RetrieveProject403 = PermissionErrorResponse +export const RetrieveProject403 = PermissionErrorResponse +export type RetrieveProject404 = ErrorResponse +export const RetrieveProject404 = ErrorResponse +export type RetrieveProject429 = ErrorResponse +export const RetrieveProject429 = ErrorResponse +export type ModifyProjectRequestJson = ProjectUpdateRequest +export const ModifyProjectRequestJson = ProjectUpdateRequest +export type ModifyProject200 = Project +export const ModifyProject200 = Project +export type ModifyProject400 = ErrorResponse +export const ModifyProject400 = ErrorResponse +export type ModifyProject403 = PermissionErrorResponse +export const ModifyProject403 = PermissionErrorResponse +export type ModifyProject404 = ErrorResponse +export const ModifyProject404 = ErrorResponse +export type ModifyProject429 = ErrorResponse +export const ModifyProject429 = ErrorResponse +export type ListProjectApiKeysParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "owner_project_access"?: "active" | "inactive" | "any" +} +export const ListProjectApiKeysParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "owner_project_access": Schema.optionalKey(Schema.Literals(["active", "inactive", "any"])) +}) +export type ListProjectApiKeys200 = ProjectApiKeyListResponse +export const ListProjectApiKeys200 = ProjectApiKeyListResponse +export type ListProjectApiKeys400 = ErrorResponse +export const ListProjectApiKeys400 = ErrorResponse +export type ListProjectApiKeys403 = PermissionErrorResponse +export const ListProjectApiKeys403 = PermissionErrorResponse +export type ListProjectApiKeys404 = ErrorResponse +export const ListProjectApiKeys404 = ErrorResponse +export type ListProjectApiKeys429 = ErrorResponse +export const ListProjectApiKeys429 = ErrorResponse +export type RetrieveProjectApiKey200 = ProjectApiKey +export const RetrieveProjectApiKey200 = ProjectApiKey +export type RetrieveProjectApiKey400 = ErrorResponse +export const RetrieveProjectApiKey400 = ErrorResponse +export type RetrieveProjectApiKey403 = PermissionErrorResponse +export const RetrieveProjectApiKey403 = PermissionErrorResponse +export type RetrieveProjectApiKey404 = ErrorResponse +export const RetrieveProjectApiKey404 = ErrorResponse +export type RetrieveProjectApiKey429 = ErrorResponse +export const RetrieveProjectApiKey429 = ErrorResponse +export type DeleteProjectApiKey200 = ProjectApiKeyDeleteResponse +export const DeleteProjectApiKey200 = ProjectApiKeyDeleteResponse +export type DeleteProjectApiKey400 = ErrorResponse +export const DeleteProjectApiKey400 = ErrorResponse +export type DeleteProjectApiKey403 = PermissionErrorResponse +export const DeleteProjectApiKey403 = PermissionErrorResponse +export type DeleteProjectApiKey404 = ErrorResponse +export const DeleteProjectApiKey404 = ErrorResponse +export type DeleteProjectApiKey429 = ErrorResponse +export const DeleteProjectApiKey429 = ErrorResponse +export type ArchiveProject200 = Project +export const ArchiveProject200 = Project +export type ArchiveProject400 = ErrorResponse +export const ArchiveProject400 = ErrorResponse +export type ArchiveProject403 = PermissionErrorResponse +export const ArchiveProject403 = PermissionErrorResponse +export type ArchiveProject404 = ErrorResponse +export const ArchiveProject404 = ErrorResponse +export type ArchiveProject429 = ErrorResponse +export const ArchiveProject429 = ErrorResponse +export type ListProjectCertificatesParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListProjectCertificatesParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListProjectCertificates200 = ListProjectCertificatesResponse +export const ListProjectCertificates200 = ListProjectCertificatesResponse +export type ListProjectCertificates400 = ErrorResponse +export const ListProjectCertificates400 = ErrorResponse +export type ListProjectCertificates403 = PermissionErrorResponse +export const ListProjectCertificates403 = PermissionErrorResponse +export type ListProjectCertificates429 = ErrorResponse +export const ListProjectCertificates429 = ErrorResponse +export type ActivateProjectCertificatesRequestJson = ToggleCertificatesRequest +export const ActivateProjectCertificatesRequestJson = ToggleCertificatesRequest +export type ActivateProjectCertificates200 = OrganizationProjectCertificateActivationResponse +export const ActivateProjectCertificates200 = OrganizationProjectCertificateActivationResponse +export type ActivateProjectCertificates400 = ErrorResponse +export const ActivateProjectCertificates400 = ErrorResponse +export type ActivateProjectCertificates403 = PermissionErrorResponse +export const ActivateProjectCertificates403 = PermissionErrorResponse +export type ActivateProjectCertificates404 = ErrorResponse +export const ActivateProjectCertificates404 = ErrorResponse +export type ActivateProjectCertificates429 = ErrorResponse +export const ActivateProjectCertificates429 = ErrorResponse +export type ActivateProjectCertificates500 = ErrorResponse +export const ActivateProjectCertificates500 = ErrorResponse +export type DeactivateProjectCertificatesRequestJson = ToggleCertificatesRequest +export const DeactivateProjectCertificatesRequestJson = ToggleCertificatesRequest +export type DeactivateProjectCertificates200 = OrganizationProjectCertificateDeactivationResponse +export const DeactivateProjectCertificates200 = OrganizationProjectCertificateDeactivationResponse +export type DeactivateProjectCertificates400 = ErrorResponse +export const DeactivateProjectCertificates400 = ErrorResponse +export type DeactivateProjectCertificates403 = PermissionErrorResponse +export const DeactivateProjectCertificates403 = PermissionErrorResponse +export type DeactivateProjectCertificates404 = ErrorResponse +export const DeactivateProjectCertificates404 = ErrorResponse +export type DeactivateProjectCertificates429 = ErrorResponse +export const DeactivateProjectCertificates429 = ErrorResponse +export type DeactivateProjectCertificates500 = ErrorResponse +export const DeactivateProjectCertificates500 = ErrorResponse +export type RetrieveProjectDataRetention200 = ProjectDataRetention +export const RetrieveProjectDataRetention200 = ProjectDataRetention +export type RetrieveProjectDataRetention400 = ErrorResponse +export const RetrieveProjectDataRetention400 = ErrorResponse +export type RetrieveProjectDataRetention403 = PermissionErrorResponse +export const RetrieveProjectDataRetention403 = PermissionErrorResponse +export type RetrieveProjectDataRetention404 = ErrorResponse +export const RetrieveProjectDataRetention404 = ErrorResponse +export type UpdateProjectDataRetentionRequestJson = UpdateProjectDataRetentionBody +export const UpdateProjectDataRetentionRequestJson = UpdateProjectDataRetentionBody +export type UpdateProjectDataRetention200 = ProjectDataRetention +export const UpdateProjectDataRetention200 = ProjectDataRetention +export type UpdateProjectDataRetention400 = ErrorResponse +export const UpdateProjectDataRetention400 = ErrorResponse +export type UpdateProjectDataRetention403 = PermissionErrorResponse +export const UpdateProjectDataRetention403 = PermissionErrorResponse +export type UpdateProjectDataRetention404 = ErrorResponse +export const UpdateProjectDataRetention404 = ErrorResponse +export type ListProjectGroupsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListProjectGroupsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListProjectGroups200 = ProjectGroupListResource +export const ListProjectGroups200 = ProjectGroupListResource +export type ListProjectGroups400 = ErrorResponse +export const ListProjectGroups400 = ErrorResponse +export type ListProjectGroups403 = PermissionErrorResponse +export const ListProjectGroups403 = PermissionErrorResponse +export type ListProjectGroups404 = ErrorResponse +export const ListProjectGroups404 = ErrorResponse +export type ListProjectGroups429 = ErrorResponse +export const ListProjectGroups429 = ErrorResponse +export type AddProjectGroupRequestJson = InviteProjectGroupBody +export const AddProjectGroupRequestJson = InviteProjectGroupBody +export type AddProjectGroup200 = ProjectGroup +export const AddProjectGroup200 = ProjectGroup +export type AddProjectGroup400 = ErrorResponse +export const AddProjectGroup400 = ErrorResponse +export type AddProjectGroup403 = PermissionErrorResponse +export const AddProjectGroup403 = PermissionErrorResponse +export type AddProjectGroup404 = ErrorResponse +export const AddProjectGroup404 = ErrorResponse +export type AddProjectGroup429 = ErrorResponse +export const AddProjectGroup429 = ErrorResponse +export type AddProjectGroup500 = ErrorResponse +export const AddProjectGroup500 = ErrorResponse +export type RetrieveProjectGroupParams = { readonly "group_type"?: "group" | "tenant_group" } +export const RetrieveProjectGroupParams = Schema.Struct({ + "group_type": Schema.optionalKey(Schema.Literals(["group", "tenant_group"])) +}) +export type RetrieveProjectGroup200 = ProjectGroup +export const RetrieveProjectGroup200 = ProjectGroup +export type RetrieveProjectGroup400 = ErrorResponse +export const RetrieveProjectGroup400 = ErrorResponse +export type RetrieveProjectGroup403 = PermissionErrorResponse +export const RetrieveProjectGroup403 = PermissionErrorResponse +export type RetrieveProjectGroup404 = ErrorResponse +export const RetrieveProjectGroup404 = ErrorResponse +export type RetrieveProjectGroup429 = ErrorResponse +export const RetrieveProjectGroup429 = ErrorResponse +export type RemoveProjectGroup200 = ProjectGroupDeletedResource +export const RemoveProjectGroup200 = ProjectGroupDeletedResource +export type RemoveProjectGroup400 = ErrorResponse +export const RemoveProjectGroup400 = ErrorResponse +export type RemoveProjectGroup403 = PermissionErrorResponse +export const RemoveProjectGroup403 = PermissionErrorResponse +export type RemoveProjectGroup404 = ErrorResponse +export const RemoveProjectGroup404 = ErrorResponse +export type RemoveProjectGroup429 = ErrorResponse +export const RemoveProjectGroup429 = ErrorResponse +export type RetrieveProjectHostedToolPermissions200 = ProjectHostedToolPermissions +export const RetrieveProjectHostedToolPermissions200 = ProjectHostedToolPermissions +export type RetrieveProjectHostedToolPermissions400 = ErrorResponse +export const RetrieveProjectHostedToolPermissions400 = ErrorResponse +export type RetrieveProjectHostedToolPermissions403 = PermissionErrorResponse +export const RetrieveProjectHostedToolPermissions403 = PermissionErrorResponse +export type RetrieveProjectHostedToolPermissions404 = ErrorResponse +export const RetrieveProjectHostedToolPermissions404 = ErrorResponse +export type RetrieveProjectHostedToolPermissions429 = ErrorResponse +export const RetrieveProjectHostedToolPermissions429 = ErrorResponse +export type UpdateProjectHostedToolPermissionsRequestJson = ProjectHostedToolPermissionsUpdateRequest +export const UpdateProjectHostedToolPermissionsRequestJson = ProjectHostedToolPermissionsUpdateRequest +export type UpdateProjectHostedToolPermissions200 = ProjectHostedToolPermissions +export const UpdateProjectHostedToolPermissions200 = ProjectHostedToolPermissions +export type UpdateProjectHostedToolPermissions400 = ErrorResponse +export const UpdateProjectHostedToolPermissions400 = ErrorResponse +export type UpdateProjectHostedToolPermissions403 = PermissionErrorResponse +export const UpdateProjectHostedToolPermissions403 = PermissionErrorResponse +export type UpdateProjectHostedToolPermissions404 = ErrorResponse +export const UpdateProjectHostedToolPermissions404 = ErrorResponse +export type UpdateProjectHostedToolPermissions409 = ErrorResponse +export const UpdateProjectHostedToolPermissions409 = ErrorResponse +export type UpdateProjectHostedToolPermissions429 = ErrorResponse +export const UpdateProjectHostedToolPermissions429 = ErrorResponse +export type RetrieveProjectModelPermissions200 = ProjectModelPermissions +export const RetrieveProjectModelPermissions200 = ProjectModelPermissions +export type RetrieveProjectModelPermissions400 = ErrorResponse +export const RetrieveProjectModelPermissions400 = ErrorResponse +export type RetrieveProjectModelPermissions403 = PermissionErrorResponse +export const RetrieveProjectModelPermissions403 = PermissionErrorResponse +export type RetrieveProjectModelPermissions404 = ErrorResponse +export const RetrieveProjectModelPermissions404 = ErrorResponse +export type RetrieveProjectModelPermissions429 = ErrorResponse +export const RetrieveProjectModelPermissions429 = ErrorResponse +export type UpdateProjectModelPermissionsRequestJson = ProjectModelPermissionsUpdateRequest +export const UpdateProjectModelPermissionsRequestJson = ProjectModelPermissionsUpdateRequest +export type UpdateProjectModelPermissions200 = ProjectModelPermissions +export const UpdateProjectModelPermissions200 = ProjectModelPermissions +export type UpdateProjectModelPermissions400 = ErrorResponse +export const UpdateProjectModelPermissions400 = ErrorResponse +export type UpdateProjectModelPermissions403 = PermissionErrorResponse +export const UpdateProjectModelPermissions403 = PermissionErrorResponse +export type UpdateProjectModelPermissions404 = ErrorResponse +export const UpdateProjectModelPermissions404 = ErrorResponse +export type UpdateProjectModelPermissions429 = ErrorResponse +export const UpdateProjectModelPermissions429 = ErrorResponse +export type DeleteProjectModelPermissions200 = ProjectModelPermissionsDeleteResponse +export const DeleteProjectModelPermissions200 = ProjectModelPermissionsDeleteResponse +export type DeleteProjectModelPermissions400 = ErrorResponse +export const DeleteProjectModelPermissions400 = ErrorResponse +export type DeleteProjectModelPermissions403 = PermissionErrorResponse +export const DeleteProjectModelPermissions403 = PermissionErrorResponse +export type DeleteProjectModelPermissions404 = ErrorResponse +export const DeleteProjectModelPermissions404 = ErrorResponse +export type DeleteProjectModelPermissions429 = ErrorResponse +export const DeleteProjectModelPermissions429 = ErrorResponse +export type ListProjectRateLimitsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "before"?: string +} +export const ListProjectRateLimitsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListProjectRateLimits200 = ProjectRateLimitListResponse +export const ListProjectRateLimits200 = ProjectRateLimitListResponse +export type ListProjectRateLimits400 = ErrorResponse +export const ListProjectRateLimits400 = ErrorResponse +export type ListProjectRateLimits403 = PermissionErrorResponse +export const ListProjectRateLimits403 = PermissionErrorResponse +export type ListProjectRateLimits404 = ErrorResponse +export const ListProjectRateLimits404 = ErrorResponse +export type UpdateProjectRateLimitsRequestJson = ProjectRateLimitUpdateRequest +export const UpdateProjectRateLimitsRequestJson = ProjectRateLimitUpdateRequest +export type UpdateProjectRateLimits200 = ProjectRateLimit +export const UpdateProjectRateLimits200 = ProjectRateLimit +export type UpdateProjectRateLimits400 = ErrorResponse +export const UpdateProjectRateLimits400 = ErrorResponse +export type UpdateProjectRateLimits403 = PermissionErrorResponse +export const UpdateProjectRateLimits403 = PermissionErrorResponse +export type UpdateProjectRateLimits404 = ErrorResponse +export const UpdateProjectRateLimits404 = ErrorResponse +export type ListProjectServiceAccountsParams = { readonly "limit"?: number; readonly "after"?: string } +export const ListProjectServiceAccountsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String) +}) +export type ListProjectServiceAccounts200 = ProjectServiceAccountListResponse +export const ListProjectServiceAccounts200 = ProjectServiceAccountListResponse +export type ListProjectServiceAccounts400 = ErrorResponse +export const ListProjectServiceAccounts400 = ErrorResponse +export type ListProjectServiceAccounts403 = PermissionErrorResponse +export const ListProjectServiceAccounts403 = PermissionErrorResponse +export type ListProjectServiceAccounts404 = ErrorResponse +export const ListProjectServiceAccounts404 = ErrorResponse +export type ListProjectServiceAccounts429 = ErrorResponse +export const ListProjectServiceAccounts429 = ErrorResponse +export type CreateProjectServiceAccountRequestJson = ProjectServiceAccountCreateRequest +export const CreateProjectServiceAccountRequestJson = ProjectServiceAccountCreateRequest +export type CreateProjectServiceAccount200 = ProjectServiceAccountCreateResponse +export const CreateProjectServiceAccount200 = ProjectServiceAccountCreateResponse +export type CreateProjectServiceAccount201 = ProjectServiceAccountCreateResponse +export const CreateProjectServiceAccount201 = ProjectServiceAccountCreateResponse +export type CreateProjectServiceAccount400 = ErrorResponse +export const CreateProjectServiceAccount400 = ErrorResponse +export type CreateProjectServiceAccount403 = PermissionErrorResponse +export const CreateProjectServiceAccount403 = PermissionErrorResponse +export type CreateProjectServiceAccount404 = ErrorResponse +export const CreateProjectServiceAccount404 = ErrorResponse +export type CreateProjectServiceAccount429 = ErrorResponse +export const CreateProjectServiceAccount429 = ErrorResponse +export type RetrieveProjectServiceAccount200 = ProjectServiceAccount +export const RetrieveProjectServiceAccount200 = ProjectServiceAccount +export type RetrieveProjectServiceAccount400 = ErrorResponse +export const RetrieveProjectServiceAccount400 = ErrorResponse +export type RetrieveProjectServiceAccount403 = PermissionErrorResponse +export const RetrieveProjectServiceAccount403 = PermissionErrorResponse +export type RetrieveProjectServiceAccount404 = ErrorResponse +export const RetrieveProjectServiceAccount404 = ErrorResponse +export type RetrieveProjectServiceAccount429 = ErrorResponse +export const RetrieveProjectServiceAccount429 = ErrorResponse +export type UpdateProjectServiceAccountRequestJson = UpdateProjectServiceAccountBody +export const UpdateProjectServiceAccountRequestJson = UpdateProjectServiceAccountBody +export type UpdateProjectServiceAccount200 = ProjectServiceAccount +export const UpdateProjectServiceAccount200 = ProjectServiceAccount +export type UpdateProjectServiceAccount400 = ErrorResponse +export const UpdateProjectServiceAccount400 = ErrorResponse +export type UpdateProjectServiceAccount403 = PermissionErrorResponse +export const UpdateProjectServiceAccount403 = PermissionErrorResponse +export type UpdateProjectServiceAccount404 = ErrorResponse +export const UpdateProjectServiceAccount404 = ErrorResponse +export type UpdateProjectServiceAccount429 = ErrorResponse +export const UpdateProjectServiceAccount429 = ErrorResponse +export type DeleteProjectServiceAccount200 = ProjectServiceAccountDeleteResponse +export const DeleteProjectServiceAccount200 = ProjectServiceAccountDeleteResponse +export type DeleteProjectServiceAccount400 = ErrorResponse +export const DeleteProjectServiceAccount400 = ErrorResponse +export type DeleteProjectServiceAccount403 = PermissionErrorResponse +export const DeleteProjectServiceAccount403 = PermissionErrorResponse +export type DeleteProjectServiceAccount404 = ErrorResponse +export const DeleteProjectServiceAccount404 = ErrorResponse +export type DeleteProjectServiceAccount429 = ErrorResponse +export const DeleteProjectServiceAccount429 = ErrorResponse +export type ListProjectSpendAlertsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string +} +export const ListProjectSpendAlertsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListProjectSpendAlerts200 = ProjectSpendAlertListResource +export const ListProjectSpendAlerts200 = ProjectSpendAlertListResource +export type ListProjectSpendAlerts400 = ErrorResponse +export const ListProjectSpendAlerts400 = ErrorResponse +export type ListProjectSpendAlerts403 = PermissionErrorResponse +export const ListProjectSpendAlerts403 = PermissionErrorResponse +export type ListProjectSpendAlerts404 = ErrorResponse +export const ListProjectSpendAlerts404 = ErrorResponse +export type CreateProjectSpendAlertRequestJson = CreateSpendAlertBody +export const CreateProjectSpendAlertRequestJson = CreateSpendAlertBody +export type CreateProjectSpendAlert200 = ProjectSpendAlert +export const CreateProjectSpendAlert200 = ProjectSpendAlert +export type CreateProjectSpendAlert400 = ErrorResponse +export const CreateProjectSpendAlert400 = ErrorResponse +export type CreateProjectSpendAlert403 = PermissionErrorResponse +export const CreateProjectSpendAlert403 = PermissionErrorResponse +export type CreateProjectSpendAlert404 = ErrorResponse +export const CreateProjectSpendAlert404 = ErrorResponse +export type RetrieveProjectSpendAlert200 = ProjectSpendAlert +export const RetrieveProjectSpendAlert200 = ProjectSpendAlert +export type RetrieveProjectSpendAlert400 = ErrorResponse +export const RetrieveProjectSpendAlert400 = ErrorResponse +export type RetrieveProjectSpendAlert403 = PermissionErrorResponse +export const RetrieveProjectSpendAlert403 = PermissionErrorResponse +export type RetrieveProjectSpendAlert404 = ErrorResponse +export const RetrieveProjectSpendAlert404 = ErrorResponse +export type UpdateProjectSpendAlertRequestJson = CreateSpendAlertBody +export const UpdateProjectSpendAlertRequestJson = CreateSpendAlertBody +export type UpdateProjectSpendAlert200 = ProjectSpendAlert +export const UpdateProjectSpendAlert200 = ProjectSpendAlert +export type UpdateProjectSpendAlert400 = ErrorResponse +export const UpdateProjectSpendAlert400 = ErrorResponse +export type UpdateProjectSpendAlert403 = PermissionErrorResponse +export const UpdateProjectSpendAlert403 = PermissionErrorResponse +export type UpdateProjectSpendAlert404 = ErrorResponse +export const UpdateProjectSpendAlert404 = ErrorResponse +export type DeleteProjectSpendAlert200 = ProjectSpendAlertDeletedResource +export const DeleteProjectSpendAlert200 = ProjectSpendAlertDeletedResource +export type DeleteProjectSpendAlert400 = ErrorResponse +export const DeleteProjectSpendAlert400 = ErrorResponse +export type DeleteProjectSpendAlert403 = PermissionErrorResponse +export const DeleteProjectSpendAlert403 = PermissionErrorResponse +export type DeleteProjectSpendAlert404 = ErrorResponse +export const DeleteProjectSpendAlert404 = ErrorResponse +export type ListProjectUsersParams = { readonly "limit"?: number; readonly "after"?: string } +export const ListProjectUsersParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String) +}) +export type ListProjectUsers200 = ProjectUserListResponse +export const ListProjectUsers200 = ProjectUserListResponse +export type ListProjectUsers400 = ErrorResponse +export const ListProjectUsers400 = ErrorResponse +export type ListProjectUsers403 = PermissionErrorResponse +export const ListProjectUsers403 = PermissionErrorResponse +export type ListProjectUsers404 = ErrorResponse +export const ListProjectUsers404 = ErrorResponse +export type ListProjectUsers429 = ErrorResponse +export const ListProjectUsers429 = ErrorResponse +export type CreateProjectUserRequestJson = ProjectUserCreateRequest +export const CreateProjectUserRequestJson = ProjectUserCreateRequest +export type CreateProjectUser200 = ProjectUser +export const CreateProjectUser200 = ProjectUser +export type CreateProjectUser201 = ProjectUser +export const CreateProjectUser201 = ProjectUser +export type CreateProjectUser400 = ErrorResponse +export const CreateProjectUser400 = ErrorResponse +export type CreateProjectUser403 = PermissionErrorResponse +export const CreateProjectUser403 = PermissionErrorResponse +export type CreateProjectUser404 = ErrorResponse +export const CreateProjectUser404 = ErrorResponse +export type CreateProjectUser409 = ErrorResponse +export const CreateProjectUser409 = ErrorResponse +export type CreateProjectUser429 = ErrorResponse +export const CreateProjectUser429 = ErrorResponse +export type RetrieveProjectUser200 = ProjectUser +export const RetrieveProjectUser200 = ProjectUser +export type RetrieveProjectUser400 = ErrorResponse +export const RetrieveProjectUser400 = ErrorResponse +export type RetrieveProjectUser403 = PermissionErrorResponse +export const RetrieveProjectUser403 = PermissionErrorResponse +export type RetrieveProjectUser404 = ErrorResponse +export const RetrieveProjectUser404 = ErrorResponse +export type RetrieveProjectUser429 = ErrorResponse +export const RetrieveProjectUser429 = ErrorResponse +export type ModifyProjectUserRequestJson = ProjectUserUpdateRequest +export const ModifyProjectUserRequestJson = ProjectUserUpdateRequest +export type ModifyProjectUser200 = ProjectUser +export const ModifyProjectUser200 = ProjectUser +export type ModifyProjectUser400 = ErrorResponse +export const ModifyProjectUser400 = ErrorResponse +export type ModifyProjectUser403 = PermissionErrorResponse +export const ModifyProjectUser403 = PermissionErrorResponse +export type ModifyProjectUser404 = ErrorResponse +export const ModifyProjectUser404 = ErrorResponse +export type ModifyProjectUser429 = ErrorResponse +export const ModifyProjectUser429 = ErrorResponse +export type DeleteProjectUser200 = ProjectUserDeleteResponse +export const DeleteProjectUser200 = ProjectUserDeleteResponse +export type DeleteProjectUser400 = ErrorResponse +export const DeleteProjectUser400 = ErrorResponse +export type DeleteProjectUser403 = PermissionErrorResponse +export const DeleteProjectUser403 = PermissionErrorResponse +export type DeleteProjectUser404 = ErrorResponse +export const DeleteProjectUser404 = ErrorResponse +export type DeleteProjectUser429 = ErrorResponse +export const DeleteProjectUser429 = ErrorResponse +export type ListRolesParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListRolesParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListRoles200 = PublicRoleListResource +export const ListRoles200 = PublicRoleListResource +export type ListRoles400 = ErrorResponse +export const ListRoles400 = ErrorResponse +export type ListRoles403 = PermissionErrorResponse +export const ListRoles403 = PermissionErrorResponse +export type ListRoles404 = ErrorResponse +export const ListRoles404 = ErrorResponse +export type ListRoles429 = ErrorResponse +export const ListRoles429 = ErrorResponse +export type CreateRoleRequestJson = PublicCreateOrganizationRoleBody +export const CreateRoleRequestJson = PublicCreateOrganizationRoleBody +export type CreateRole200 = Role +export const CreateRole200 = Role +export type CreateRole400 = ErrorResponse +export const CreateRole400 = ErrorResponse +export type CreateRole403 = PermissionErrorResponse +export const CreateRole403 = PermissionErrorResponse +export type CreateRole404 = ErrorResponse +export const CreateRole404 = ErrorResponse +export type CreateRole429 = ErrorResponse +export const CreateRole429 = ErrorResponse +export type RetrieveRole200 = Role +export const RetrieveRole200 = Role +export type RetrieveRole403 = PermissionErrorResponse +export const RetrieveRole403 = PermissionErrorResponse +export type RetrieveRole404 = ErrorResponse +export const RetrieveRole404 = ErrorResponse +export type RetrieveRole429 = ErrorResponse +export const RetrieveRole429 = ErrorResponse +export type UpdateRoleRequestJson = PublicUpdateOrganizationRoleBody +export const UpdateRoleRequestJson = PublicUpdateOrganizationRoleBody +export type UpdateRole200 = Role +export const UpdateRole200 = Role +export type UpdateRole400 = ErrorResponse +export const UpdateRole400 = ErrorResponse +export type UpdateRole403 = PermissionErrorResponse +export const UpdateRole403 = PermissionErrorResponse +export type UpdateRole404 = ErrorResponse +export const UpdateRole404 = ErrorResponse +export type UpdateRole429 = ErrorResponse +export const UpdateRole429 = ErrorResponse +export type DeleteRole200 = RoleDeletedResource +export const DeleteRole200 = RoleDeletedResource +export type DeleteRole400 = ErrorResponse +export const DeleteRole400 = ErrorResponse +export type DeleteRole403 = PermissionErrorResponse +export const DeleteRole403 = PermissionErrorResponse +export type DeleteRole404 = ErrorResponse +export const DeleteRole404 = ErrorResponse +export type DeleteRole429 = ErrorResponse +export const DeleteRole429 = ErrorResponse +export type ListOrganizationSpendAlertsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string +} +export const ListOrganizationSpendAlertsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListOrganizationSpendAlerts200 = OrganizationSpendAlertListResource +export const ListOrganizationSpendAlerts200 = OrganizationSpendAlertListResource +export type ListOrganizationSpendAlerts400 = ErrorResponse +export const ListOrganizationSpendAlerts400 = ErrorResponse +export type ListOrganizationSpendAlerts403 = PermissionErrorResponse +export const ListOrganizationSpendAlerts403 = PermissionErrorResponse +export type ListOrganizationSpendAlerts404 = ErrorResponse +export const ListOrganizationSpendAlerts404 = ErrorResponse +export type CreateOrganizationSpendAlertRequestJson = CreateSpendAlertBody +export const CreateOrganizationSpendAlertRequestJson = CreateSpendAlertBody +export type CreateOrganizationSpendAlert200 = OrganizationSpendAlert +export const CreateOrganizationSpendAlert200 = OrganizationSpendAlert +export type CreateOrganizationSpendAlert400 = ErrorResponse +export const CreateOrganizationSpendAlert400 = ErrorResponse +export type CreateOrganizationSpendAlert403 = PermissionErrorResponse +export const CreateOrganizationSpendAlert403 = PermissionErrorResponse +export type RetrieveOrganizationSpendAlert200 = OrganizationSpendAlert +export const RetrieveOrganizationSpendAlert200 = OrganizationSpendAlert +export type RetrieveOrganizationSpendAlert403 = PermissionErrorResponse +export const RetrieveOrganizationSpendAlert403 = PermissionErrorResponse +export type RetrieveOrganizationSpendAlert404 = ErrorResponse +export const RetrieveOrganizationSpendAlert404 = ErrorResponse +export type UpdateOrganizationSpendAlertRequestJson = CreateSpendAlertBody +export const UpdateOrganizationSpendAlertRequestJson = CreateSpendAlertBody +export type UpdateOrganizationSpendAlert200 = OrganizationSpendAlert +export const UpdateOrganizationSpendAlert200 = OrganizationSpendAlert +export type UpdateOrganizationSpendAlert400 = ErrorResponse +export const UpdateOrganizationSpendAlert400 = ErrorResponse +export type UpdateOrganizationSpendAlert403 = PermissionErrorResponse +export const UpdateOrganizationSpendAlert403 = PermissionErrorResponse +export type UpdateOrganizationSpendAlert404 = ErrorResponse +export const UpdateOrganizationSpendAlert404 = ErrorResponse +export type DeleteOrganizationSpendAlert200 = OrganizationSpendAlertDeletedResource +export const DeleteOrganizationSpendAlert200 = OrganizationSpendAlertDeletedResource +export type DeleteOrganizationSpendAlert403 = PermissionErrorResponse +export const DeleteOrganizationSpendAlert403 = PermissionErrorResponse +export type DeleteOrganizationSpendAlert404 = ErrorResponse +export const DeleteOrganizationSpendAlert404 = ErrorResponse +export type UsageAudioSpeechesParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "api_key_id" | "model"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageAudioSpeechesParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey(Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "model"]))), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageAudioSpeeches200 = UsageResponse +export const UsageAudioSpeeches200 = UsageResponse +export type UsageAudioSpeeches400 = ErrorResponse +export const UsageAudioSpeeches400 = ErrorResponse +export type UsageAudioSpeeches403 = PermissionErrorResponse +export const UsageAudioSpeeches403 = PermissionErrorResponse +export type UsageAudioSpeeches429 = ErrorResponse +export const UsageAudioSpeeches429 = ErrorResponse +export type UsageAudioTranscriptionsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "api_key_id" | "model"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageAudioTranscriptionsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey(Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "model"]))), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageAudioTranscriptions200 = UsageResponse +export const UsageAudioTranscriptions200 = UsageResponse +export type UsageAudioTranscriptions400 = ErrorResponse +export const UsageAudioTranscriptions400 = ErrorResponse +export type UsageAudioTranscriptions403 = PermissionErrorResponse +export const UsageAudioTranscriptions403 = PermissionErrorResponse +export type UsageAudioTranscriptions429 = ErrorResponse +export const UsageAudioTranscriptions429 = ErrorResponse +export type UsageCodeInterpreterSessionsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageCodeInterpreterSessionsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey(Schema.Array(Schema.Literal("project_id"))), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageCodeInterpreterSessions200 = UsageResponse +export const UsageCodeInterpreterSessions200 = UsageResponse +export type UsageCodeInterpreterSessions400 = ErrorResponse +export const UsageCodeInterpreterSessions400 = ErrorResponse +export type UsageCodeInterpreterSessions403 = PermissionErrorResponse +export const UsageCodeInterpreterSessions403 = PermissionErrorResponse +export type UsageCodeInterpreterSessions429 = ErrorResponse +export const UsageCodeInterpreterSessions429 = ErrorResponse +export type UsageCompletionsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "batch"?: boolean + readonly "group_by"?: ReadonlyArray< + "project_id" | "user_id" | "api_key_id" | "model" | "batch" | "service_tier" | "api_source" + > + readonly "limit"?: number + readonly "page"?: string +} +export const UsageCompletionsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "batch": Schema.optionalKey(Schema.Boolean), + "group_by": Schema.optionalKey( + Schema.Array( + Schema.Literals(["project_id", "user_id", "api_key_id", "model", "batch", "service_tier", "api_source"]) + ) + ), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageCompletions200 = UsageResponse +export const UsageCompletions200 = UsageResponse +export type UsageCompletions400 = ErrorResponse +export const UsageCompletions400 = ErrorResponse +export type UsageCompletions403 = PermissionErrorResponse +export const UsageCompletions403 = PermissionErrorResponse +export type UsageCompletions429 = ErrorResponse +export const UsageCompletions429 = ErrorResponse +export type UsageEmbeddingsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "api_key_id" | "model"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageEmbeddingsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey(Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "model"]))), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageEmbeddings200 = UsageResponse +export const UsageEmbeddings200 = UsageResponse +export type UsageEmbeddings400 = ErrorResponse +export const UsageEmbeddings400 = ErrorResponse +export type UsageEmbeddings403 = PermissionErrorResponse +export const UsageEmbeddings403 = PermissionErrorResponse +export type UsageEmbeddings429 = ErrorResponse +export const UsageEmbeddings429 = ErrorResponse +export type UsageFileSearchCallsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "vector_store_ids"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "api_key_id" | "vector_store_id"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageFileSearchCallsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "vector_store_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey( + Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "vector_store_id"])) + ), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageFileSearchCalls200 = UsageResponse +export const UsageFileSearchCalls200 = UsageResponse +export type UsageFileSearchCalls400 = ErrorResponse +export const UsageFileSearchCalls400 = ErrorResponse +export type UsageFileSearchCalls403 = PermissionErrorResponse +export const UsageFileSearchCalls403 = PermissionErrorResponse +export type UsageFileSearchCalls429 = ErrorResponse +export const UsageFileSearchCalls429 = ErrorResponse +export type UsageImagesParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "sources"?: ReadonlyArray<"image.generation" | "image.edit" | "image.variation"> + readonly "sizes"?: ReadonlyArray<"256x256" | "512x512" | "1024x1024" | "1792x1792" | "1024x1792"> + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "api_key_id" | "model" | "size" | "source"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageImagesParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "sources": Schema.optionalKey(Schema.Array(Schema.Literals(["image.generation", "image.edit", "image.variation"]))), + "sizes": Schema.optionalKey( + Schema.Array(Schema.Literals(["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"])) + ), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey( + Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "model", "size", "source"])) + ), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageImages200 = UsageResponse +export const UsageImages200 = UsageResponse +export type UsageImages400 = ErrorResponse +export const UsageImages400 = ErrorResponse +export type UsageImages403 = PermissionErrorResponse +export const UsageImages403 = PermissionErrorResponse +export type UsageImages429 = ErrorResponse +export const UsageImages429 = ErrorResponse +export type UsageModerationsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id" | "user_id" | "api_key_id" | "model"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageModerationsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey(Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "model"]))), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageModerations200 = UsageResponse +export const UsageModerations200 = UsageResponse +export type UsageModerations400 = ErrorResponse +export const UsageModerations400 = ErrorResponse +export type UsageModerations403 = PermissionErrorResponse +export const UsageModerations403 = PermissionErrorResponse +export type UsageModerations429 = ErrorResponse +export const UsageModerations429 = ErrorResponse +export type UsageVectorStoresParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "group_by"?: ReadonlyArray<"project_id"> + readonly "limit"?: number + readonly "page"?: string +} +export const UsageVectorStoresParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "group_by": Schema.optionalKey(Schema.Array(Schema.Literal("project_id"))), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageVectorStores200 = UsageResponse +export const UsageVectorStores200 = UsageResponse +export type UsageVectorStores400 = ErrorResponse +export const UsageVectorStores400 = ErrorResponse +export type UsageVectorStores403 = PermissionErrorResponse +export const UsageVectorStores403 = PermissionErrorResponse +export type UsageVectorStores429 = ErrorResponse +export const UsageVectorStores429 = ErrorResponse +export type UsageWebSearchCallsParams = { + readonly "start_time": number + readonly "end_time"?: number + readonly "bucket_width"?: "1m" | "1h" | "1d" + readonly "project_ids"?: ReadonlyArray + readonly "user_ids"?: ReadonlyArray + readonly "api_key_ids"?: ReadonlyArray + readonly "models"?: ReadonlyArray + readonly "context_levels"?: ReadonlyArray<"low" | "medium" | "high"> + readonly "group_by"?: ReadonlyArray< + "project_id" | "user_id" | "api_key_id" | "model" | "context_level" | "api_source" + > + readonly "limit"?: number + readonly "page"?: string +} +export const UsageWebSearchCallsParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "bucket_width": Schema.optionalKey(Schema.Literals(["1m", "1h", "1d"])), + "project_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "user_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "api_key_ids": Schema.optionalKey(Schema.Array(Schema.String)), + "models": Schema.optionalKey(Schema.Array(Schema.String)), + "context_levels": Schema.optionalKey(Schema.Array(Schema.Literals(["low", "medium", "high"]))), + "group_by": Schema.optionalKey( + Schema.Array(Schema.Literals(["project_id", "user_id", "api_key_id", "model", "context_level", "api_source"])) + ), + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "page": Schema.optionalKey(Schema.String) +}) +export type UsageWebSearchCalls200 = UsageResponse +export const UsageWebSearchCalls200 = UsageResponse +export type UsageWebSearchCalls400 = ErrorResponse +export const UsageWebSearchCalls400 = ErrorResponse +export type UsageWebSearchCalls403 = PermissionErrorResponse +export const UsageWebSearchCalls403 = PermissionErrorResponse +export type UsageWebSearchCalls429 = ErrorResponse +export const UsageWebSearchCalls429 = ErrorResponse +export type ListUsersParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "emails"?: ReadonlyArray +} +export const ListUsersParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "after": Schema.optionalKey(Schema.String), + "emails": Schema.optionalKey(Schema.Array(Schema.String)) +}) +export type ListUsers200 = UserListResponse +export const ListUsers200 = UserListResponse +export type ListUsers400 = ErrorResponse +export const ListUsers400 = ErrorResponse +export type ListUsers403 = PermissionErrorResponse +export const ListUsers403 = PermissionErrorResponse +export type ListUsers429 = ErrorResponse +export const ListUsers429 = ErrorResponse +export type RetrieveUser200 = User +export const RetrieveUser200 = User +export type RetrieveUser403 = PermissionErrorResponse +export const RetrieveUser403 = PermissionErrorResponse +export type RetrieveUser404 = ErrorResponse +export const RetrieveUser404 = ErrorResponse +export type RetrieveUser429 = ErrorResponse +export const RetrieveUser429 = ErrorResponse +export type ModifyUserRequestJson = UserRoleUpdateRequest +export const ModifyUserRequestJson = UserRoleUpdateRequest +export type ModifyUser200 = User +export const ModifyUser200 = User +export type ModifyUser400 = ErrorResponse +export const ModifyUser400 = ErrorResponse +export type ModifyUser403 = PermissionErrorResponse +export const ModifyUser403 = PermissionErrorResponse +export type ModifyUser404 = ErrorResponse +export const ModifyUser404 = ErrorResponse +export type ModifyUser429 = ErrorResponse +export const ModifyUser429 = ErrorResponse +export type DeleteUser200 = UserDeleteResponse +export const DeleteUser200 = UserDeleteResponse +export type DeleteUser400 = ErrorResponse +export const DeleteUser400 = ErrorResponse +export type DeleteUser403 = PermissionErrorResponse +export const DeleteUser403 = PermissionErrorResponse +export type DeleteUser404 = ErrorResponse +export const DeleteUser404 = ErrorResponse +export type DeleteUser429 = ErrorResponse +export const DeleteUser429 = ErrorResponse +export type ListUserRoleAssignmentsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListUserRoleAssignmentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListUserRoleAssignments200 = RoleListResource +export const ListUserRoleAssignments200 = RoleListResource +export type ListUserRoleAssignments400 = ErrorResponse +export const ListUserRoleAssignments400 = ErrorResponse +export type ListUserRoleAssignments403 = PermissionErrorResponse +export const ListUserRoleAssignments403 = PermissionErrorResponse +export type ListUserRoleAssignments404 = ErrorResponse +export const ListUserRoleAssignments404 = ErrorResponse +export type ListUserRoleAssignments429 = ErrorResponse +export const ListUserRoleAssignments429 = ErrorResponse +export type AssignUserRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export const AssignUserRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export type AssignUserRole200 = UserRoleAssignment +export const AssignUserRole200 = UserRoleAssignment +export type AssignUserRole400 = ErrorResponse +export const AssignUserRole400 = ErrorResponse +export type AssignUserRole403 = PermissionErrorResponse +export const AssignUserRole403 = PermissionErrorResponse +export type AssignUserRole404 = ErrorResponse +export const AssignUserRole404 = ErrorResponse +export type AssignUserRole429 = ErrorResponse +export const AssignUserRole429 = ErrorResponse +export type RetrieveUserRole200 = AssignedRoleDetails +export const RetrieveUserRole200 = AssignedRoleDetails +export type RetrieveUserRole400 = ErrorResponse +export const RetrieveUserRole400 = ErrorResponse +export type RetrieveUserRole403 = PermissionErrorResponse +export const RetrieveUserRole403 = PermissionErrorResponse +export type RetrieveUserRole404 = ErrorResponse +export const RetrieveUserRole404 = ErrorResponse +export type RetrieveUserRole429 = ErrorResponse +export const RetrieveUserRole429 = ErrorResponse +export type UnassignUserRole200 = DeletedRoleAssignmentResource +export const UnassignUserRole200 = DeletedRoleAssignmentResource +export type UnassignUserRole400 = ErrorResponse +export const UnassignUserRole400 = ErrorResponse +export type UnassignUserRole403 = PermissionErrorResponse +export const UnassignUserRole403 = PermissionErrorResponse +export type UnassignUserRole404 = ErrorResponse +export const UnassignUserRole404 = ErrorResponse +export type UnassignUserRole429 = ErrorResponse +export const UnassignUserRole429 = ErrorResponse +export type ListProjectGroupRoleAssignmentsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListProjectGroupRoleAssignmentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListProjectGroupRoleAssignments200 = RoleListResource +export const ListProjectGroupRoleAssignments200 = RoleListResource +export type ListProjectGroupRoleAssignments400 = ErrorResponse +export const ListProjectGroupRoleAssignments400 = ErrorResponse +export type ListProjectGroupRoleAssignments403 = PermissionErrorResponse +export const ListProjectGroupRoleAssignments403 = PermissionErrorResponse +export type ListProjectGroupRoleAssignments404 = ErrorResponse +export const ListProjectGroupRoleAssignments404 = ErrorResponse +export type ListProjectGroupRoleAssignments429 = ErrorResponse +export const ListProjectGroupRoleAssignments429 = ErrorResponse +export type AssignProjectGroupRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export const AssignProjectGroupRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export type AssignProjectGroupRole200 = GroupRoleAssignment +export const AssignProjectGroupRole200 = GroupRoleAssignment +export type AssignProjectGroupRole400 = ErrorResponse +export const AssignProjectGroupRole400 = ErrorResponse +export type AssignProjectGroupRole403 = PermissionErrorResponse +export const AssignProjectGroupRole403 = PermissionErrorResponse +export type AssignProjectGroupRole404 = ErrorResponse +export const AssignProjectGroupRole404 = ErrorResponse +export type AssignProjectGroupRole429 = ErrorResponse +export const AssignProjectGroupRole429 = ErrorResponse +export type RetrieveProjectGroupRole200 = AssignedRoleDetails +export const RetrieveProjectGroupRole200 = AssignedRoleDetails +export type RetrieveProjectGroupRole400 = ErrorResponse +export const RetrieveProjectGroupRole400 = ErrorResponse +export type RetrieveProjectGroupRole403 = PermissionErrorResponse +export const RetrieveProjectGroupRole403 = PermissionErrorResponse +export type RetrieveProjectGroupRole404 = ErrorResponse +export const RetrieveProjectGroupRole404 = ErrorResponse +export type RetrieveProjectGroupRole429 = ErrorResponse +export const RetrieveProjectGroupRole429 = ErrorResponse +export type UnassignProjectGroupRole200 = DeletedRoleAssignmentResource +export const UnassignProjectGroupRole200 = DeletedRoleAssignmentResource +export type UnassignProjectGroupRole400 = ErrorResponse +export const UnassignProjectGroupRole400 = ErrorResponse +export type UnassignProjectGroupRole403 = PermissionErrorResponse +export const UnassignProjectGroupRole403 = PermissionErrorResponse +export type UnassignProjectGroupRole404 = ErrorResponse +export const UnassignProjectGroupRole404 = ErrorResponse +export type UnassignProjectGroupRole429 = ErrorResponse +export const UnassignProjectGroupRole429 = ErrorResponse +export type ListProjectRolesParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListProjectRolesParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListProjectRoles200 = PublicRoleListResource +export const ListProjectRoles200 = PublicRoleListResource +export type ListProjectRoles400 = ErrorResponse +export const ListProjectRoles400 = ErrorResponse +export type ListProjectRoles403 = PermissionErrorResponse +export const ListProjectRoles403 = PermissionErrorResponse +export type ListProjectRoles404 = ErrorResponse +export const ListProjectRoles404 = ErrorResponse +export type ListProjectRoles429 = ErrorResponse +export const ListProjectRoles429 = ErrorResponse +export type CreateProjectRoleRequestJson = PublicCreateOrganizationRoleBody +export const CreateProjectRoleRequestJson = PublicCreateOrganizationRoleBody +export type CreateProjectRole200 = Role +export const CreateProjectRole200 = Role +export type CreateProjectRole400 = ErrorResponse +export const CreateProjectRole400 = ErrorResponse +export type CreateProjectRole403 = PermissionErrorResponse +export const CreateProjectRole403 = PermissionErrorResponse +export type CreateProjectRole404 = ErrorResponse +export const CreateProjectRole404 = ErrorResponse +export type CreateProjectRole429 = ErrorResponse +export const CreateProjectRole429 = ErrorResponse +export type RetrieveProjectRole200 = Role +export const RetrieveProjectRole200 = Role +export type RetrieveProjectRole403 = PermissionErrorResponse +export const RetrieveProjectRole403 = PermissionErrorResponse +export type RetrieveProjectRole404 = ErrorResponse +export const RetrieveProjectRole404 = ErrorResponse +export type RetrieveProjectRole429 = ErrorResponse +export const RetrieveProjectRole429 = ErrorResponse +export type UpdateProjectRoleRequestJson = PublicUpdateOrganizationRoleBody +export const UpdateProjectRoleRequestJson = PublicUpdateOrganizationRoleBody +export type UpdateProjectRole200 = Role +export const UpdateProjectRole200 = Role +export type UpdateProjectRole400 = ErrorResponse +export const UpdateProjectRole400 = ErrorResponse +export type UpdateProjectRole403 = PermissionErrorResponse +export const UpdateProjectRole403 = PermissionErrorResponse +export type UpdateProjectRole404 = ErrorResponse +export const UpdateProjectRole404 = ErrorResponse +export type UpdateProjectRole429 = ErrorResponse +export const UpdateProjectRole429 = ErrorResponse +export type DeleteProjectRole200 = RoleDeletedResource +export const DeleteProjectRole200 = RoleDeletedResource +export type DeleteProjectRole400 = ErrorResponse +export const DeleteProjectRole400 = ErrorResponse +export type DeleteProjectRole403 = PermissionErrorResponse +export const DeleteProjectRole403 = PermissionErrorResponse +export type DeleteProjectRole404 = ErrorResponse +export const DeleteProjectRole404 = ErrorResponse +export type DeleteProjectRole429 = ErrorResponse +export const DeleteProjectRole429 = ErrorResponse +export type ListProjectUserRoleAssignmentsParams = { + readonly "limit"?: number + readonly "after"?: string + readonly "order"?: "asc" | "desc" +} +export const ListProjectUserRoleAssignmentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(1000).annotate({ "expected": "a value less than or equal to 1000" })) + ), + "after": Schema.optionalKey(Schema.String), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])) +}) +export type ListProjectUserRoleAssignments200 = RoleListResource +export const ListProjectUserRoleAssignments200 = RoleListResource +export type ListProjectUserRoleAssignments400 = ErrorResponse +export const ListProjectUserRoleAssignments400 = ErrorResponse +export type ListProjectUserRoleAssignments403 = PermissionErrorResponse +export const ListProjectUserRoleAssignments403 = PermissionErrorResponse +export type ListProjectUserRoleAssignments404 = ErrorResponse +export const ListProjectUserRoleAssignments404 = ErrorResponse +export type ListProjectUserRoleAssignments429 = ErrorResponse +export const ListProjectUserRoleAssignments429 = ErrorResponse +export type AssignProjectUserRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export const AssignProjectUserRoleRequestJson = PublicAssignOrganizationGroupRoleBody +export type AssignProjectUserRole200 = UserRoleAssignment +export const AssignProjectUserRole200 = UserRoleAssignment +export type AssignProjectUserRole400 = ErrorResponse +export const AssignProjectUserRole400 = ErrorResponse +export type AssignProjectUserRole403 = PermissionErrorResponse +export const AssignProjectUserRole403 = PermissionErrorResponse +export type AssignProjectUserRole404 = ErrorResponse +export const AssignProjectUserRole404 = ErrorResponse +export type AssignProjectUserRole429 = ErrorResponse +export const AssignProjectUserRole429 = ErrorResponse +export type RetrieveProjectUserRole200 = AssignedRoleDetails +export const RetrieveProjectUserRole200 = AssignedRoleDetails +export type RetrieveProjectUserRole400 = ErrorResponse +export const RetrieveProjectUserRole400 = ErrorResponse +export type RetrieveProjectUserRole403 = PermissionErrorResponse +export const RetrieveProjectUserRole403 = PermissionErrorResponse +export type RetrieveProjectUserRole404 = ErrorResponse +export const RetrieveProjectUserRole404 = ErrorResponse +export type RetrieveProjectUserRole429 = ErrorResponse +export const RetrieveProjectUserRole429 = ErrorResponse +export type UnassignProjectUserRole200 = DeletedRoleAssignmentResource +export const UnassignProjectUserRole200 = DeletedRoleAssignmentResource +export type UnassignProjectUserRole400 = ErrorResponse +export const UnassignProjectUserRole400 = ErrorResponse +export type UnassignProjectUserRole403 = PermissionErrorResponse +export const UnassignProjectUserRole403 = PermissionErrorResponse +export type UnassignProjectUserRole404 = ErrorResponse +export const UnassignProjectUserRole404 = ErrorResponse +export type UnassignProjectUserRole429 = ErrorResponse +export const UnassignProjectUserRole429 = ErrorResponse +export type CreateRealtimeCallRequestFormData = RealtimeCallCreateRequest +export const CreateRealtimeCallRequestFormData = RealtimeCallCreateRequest +export type AcceptRealtimeCallRequestJson = RealtimeSessionCreateRequestGA +export const AcceptRealtimeCallRequestJson = RealtimeSessionCreateRequestGA +export type AcceptRealtimeCall404 = ErrorResponse +export const AcceptRealtimeCall404 = ErrorResponse +export type AcceptRealtimeCall409 = ErrorResponse +export const AcceptRealtimeCall409 = ErrorResponse +export type HangupRealtimeCall404 = ErrorResponse +export const HangupRealtimeCall404 = ErrorResponse +export type ReferRealtimeCallRequestJson = RealtimeCallReferRequest +export const ReferRealtimeCallRequestJson = RealtimeCallReferRequest +export type ReferRealtimeCall404 = ErrorResponse +export const ReferRealtimeCall404 = ErrorResponse +export type RejectRealtimeCallRequestJson = RealtimeCallRejectRequest +export const RejectRealtimeCallRequestJson = RealtimeCallRejectRequest +export type RejectRealtimeCall404 = ErrorResponse +export const RejectRealtimeCall404 = ErrorResponse +export type RejectRealtimeCall409 = ErrorResponse +export const RejectRealtimeCall409 = ErrorResponse +export type CreateRealtimeClientSecretRequestJson = RealtimeCreateClientSecretRequest +export const CreateRealtimeClientSecretRequestJson = RealtimeCreateClientSecretRequest +export type CreateRealtimeClientSecret200 = RealtimeCreateClientSecretResponse +export const CreateRealtimeClientSecret200 = RealtimeCreateClientSecretResponse +export type CreateRealtimeClientSecret400 = ErrorResponse +export const CreateRealtimeClientSecret400 = ErrorResponse +export type CreateRealtimeClientSecret404 = ErrorResponse +export const CreateRealtimeClientSecret404 = ErrorResponse +export type CreateRealtimeClientSecret500 = ErrorResponse +export const CreateRealtimeClientSecret500 = ErrorResponse +export type CreateRealtimeSessionRequestJson = RealtimeSessionCreateRequest +export const CreateRealtimeSessionRequestJson = RealtimeSessionCreateRequest +export type CreateRealtimeSession200 = RealtimeSessionCreateResponse +export const CreateRealtimeSession200 = RealtimeSessionCreateResponse +export type CreateRealtimeTranscriptionSessionRequestJson = RealtimeTranscriptionSessionCreateRequest +export const CreateRealtimeTranscriptionSessionRequestJson = RealtimeTranscriptionSessionCreateRequest +export type CreateRealtimeTranscriptionSession200 = RealtimeTranscriptionSessionCreateResponse +export const CreateRealtimeTranscriptionSession200 = RealtimeTranscriptionSessionCreateResponse +export type CreateRealtimeTranslationClientSecretRequestJson = RealtimeTranslationClientSecretCreateRequest +export const CreateRealtimeTranslationClientSecretRequestJson = RealtimeTranslationClientSecretCreateRequest +export type CreateRealtimeTranslationClientSecret200 = RealtimeTranslationClientSecretCreateResponse +export const CreateRealtimeTranslationClientSecret200 = RealtimeTranslationClientSecretCreateResponse +export type CreateRealtimeTranslationClientSecret400 = ErrorResponse +export const CreateRealtimeTranslationClientSecret400 = ErrorResponse +export type CreateRealtimeTranslationClientSecret404 = ErrorResponse +export const CreateRealtimeTranslationClientSecret404 = ErrorResponse +export type CreateRealtimeTranslationClientSecret500 = ErrorResponse +export const CreateRealtimeTranslationClientSecret500 = ErrorResponse +export type CreateResponseRequestJson = CreateResponse +export const CreateResponseRequestJson = CreateResponse +export type CreateResponse200 = Response +export const CreateResponse200 = Response +export type CreateResponse200Sse = ResponseStreamEvent +export const CreateResponse200Sse = ResponseStreamEvent +export type CreateResponse400 = ErrorResponse +export const CreateResponse400 = ErrorResponse +export type CreateResponse404 = ErrorResponse +export const CreateResponse404 = ErrorResponse +export type CreateResponse429 = ErrorResponse +export const CreateResponse429 = ErrorResponse +export type CreateResponse503 = ErrorResponse +export const CreateResponse503 = ErrorResponse +export type GetResponseParams = { + readonly "include"?: ReadonlyArray + readonly "stream"?: boolean + readonly "starting_after"?: number + readonly "include_obfuscation"?: boolean +} +export const GetResponseParams = Schema.Struct({ + "include": Schema.optionalKey(Schema.Array(IncludeEnum)), + "stream": Schema.optionalKey(Schema.Boolean), + "starting_after": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "include_obfuscation": Schema.optionalKey(Schema.Boolean) +}) +export type GetResponse200 = Response +export const GetResponse200 = Response +export type GetResponse404 = ErrorResponse +export const GetResponse404 = ErrorResponse +export type GetResponse429 = ErrorResponse +export const GetResponse429 = ErrorResponse +export type DeleteResponse200 = { + readonly "object": "response.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const DeleteResponse200 = Schema.Struct({ + "object": Schema.Literal("response.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String +}) +export type DeleteResponse404 = ErrorResponse +export const DeleteResponse404 = ErrorResponse +export type DeleteResponse429 = ErrorResponse +export const DeleteResponse429 = ErrorResponse +export type CancelResponse200 = Response +export const CancelResponse200 = Response +export type CancelResponse404 = ErrorResponse +export const CancelResponse404 = ErrorResponse +export type CancelResponse429 = ErrorResponse +export const CancelResponse429 = ErrorResponse +export type ListInputItemsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "include"?: ReadonlyArray +} +export const ListInputItemsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "include": Schema.optionalKey(Schema.Array(IncludeEnum)) +}) +export type ListInputItems200 = ResponseItemList +export const ListInputItems200 = ResponseItemList +export type ListInputItems404 = ErrorResponse +export const ListInputItems404 = ErrorResponse +export type ListInputItems429 = ErrorResponse +export const ListInputItems429 = ErrorResponse +export type CreateThreadRequestJson = CreateThreadRequest +export const CreateThreadRequestJson = CreateThreadRequest +export type CreateThread200 = ThreadObject +export const CreateThread200 = ThreadObject +export type CreateThread429 = ErrorResponse +export const CreateThread429 = ErrorResponse +export type CreateThreadAndRunRequestJson = CreateThreadAndRunRequest +export const CreateThreadAndRunRequestJson = CreateThreadAndRunRequest +export type CreateThreadAndRun200 = RunObject +export const CreateThreadAndRun200 = RunObject +export type CreateThreadAndRun200Sse = AssistantStreamEvent +export const CreateThreadAndRun200Sse = AssistantStreamEvent +export type CreateThreadAndRun429 = ErrorResponse +export const CreateThreadAndRun429 = ErrorResponse +export type GetThread200 = ThreadObject +export const GetThread200 = ThreadObject +export type GetThread429 = ErrorResponse +export const GetThread429 = ErrorResponse +export type ModifyThreadRequestJson = ModifyThreadRequest +export const ModifyThreadRequestJson = ModifyThreadRequest +export type ModifyThread200 = ThreadObject +export const ModifyThread200 = ThreadObject +export type ModifyThread429 = ErrorResponse +export const ModifyThread429 = ErrorResponse +export type DeleteThread200 = DeleteThreadResponse +export const DeleteThread200 = DeleteThreadResponse +export type DeleteThread429 = ErrorResponse +export const DeleteThread429 = ErrorResponse +export type ListMessagesParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string + readonly "run_id"?: string +} +export const ListMessagesParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String), + "run_id": Schema.optionalKey(Schema.String) +}) +export type ListMessages200 = ListMessagesResponse +export const ListMessages200 = ListMessagesResponse +export type ListMessages429 = ErrorResponse +export const ListMessages429 = ErrorResponse +export type CreateMessageRequestJson = CreateMessageRequest +export const CreateMessageRequestJson = CreateMessageRequest +export type CreateMessage200 = MessageObject +export const CreateMessage200 = MessageObject +export type CreateMessage429 = ErrorResponse +export const CreateMessage429 = ErrorResponse +export type GetMessage200 = MessageObject +export const GetMessage200 = MessageObject +export type GetMessage429 = ErrorResponse +export const GetMessage429 = ErrorResponse +export type ModifyMessageRequestJson = ModifyMessageRequest +export const ModifyMessageRequestJson = ModifyMessageRequest +export type ModifyMessage200 = MessageObject +export const ModifyMessage200 = MessageObject +export type ModifyMessage429 = ErrorResponse +export const ModifyMessage429 = ErrorResponse +export type DeleteMessage200 = DeleteMessageResponse +export const DeleteMessage200 = DeleteMessageResponse +export type DeleteMessage429 = ErrorResponse +export const DeleteMessage429 = ErrorResponse +export type ListRunsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string +} +export const ListRunsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListRuns200 = ListRunsResponse +export const ListRuns200 = ListRunsResponse +export type ListRuns429 = ErrorResponse +export const ListRuns429 = ErrorResponse +export type CreateRunParams = { + readonly "include[]"?: ReadonlyArray<"step_details.tool_calls[*].file_search.results[*].content"> +} +export const CreateRunParams = Schema.Struct({ + "include[]": Schema.optionalKey( + Schema.Array(Schema.Literal("step_details.tool_calls[*].file_search.results[*].content")) + ) +}) +export type CreateRunRequestJson = CreateRunRequest +export const CreateRunRequestJson = CreateRunRequest +export type CreateRun200 = RunObject +export const CreateRun200 = RunObject +export type CreateRun200Sse = AssistantStreamEvent +export const CreateRun200Sse = AssistantStreamEvent +export type CreateRun429 = ErrorResponse +export const CreateRun429 = ErrorResponse +export type GetRun200 = RunObject +export const GetRun200 = RunObject +export type GetRun429 = ErrorResponse +export const GetRun429 = ErrorResponse +export type ModifyRunRequestJson = ModifyRunRequest +export const ModifyRunRequestJson = ModifyRunRequest +export type ModifyRun200 = RunObject +export const ModifyRun200 = RunObject +export type ModifyRun429 = ErrorResponse +export const ModifyRun429 = ErrorResponse +export type CancelRun200 = RunObject +export const CancelRun200 = RunObject +export type CancelRun429 = ErrorResponse +export const CancelRun429 = ErrorResponse +export type ListRunStepsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string + readonly "include[]"?: ReadonlyArray<"step_details.tool_calls[*].file_search.results[*].content"> +} +export const ListRunStepsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String), + "include[]": Schema.optionalKey( + Schema.Array(Schema.Literal("step_details.tool_calls[*].file_search.results[*].content")) + ) +}) +export type ListRunSteps200 = ListRunStepsResponse +export const ListRunSteps200 = ListRunStepsResponse +export type ListRunSteps429 = ErrorResponse +export const ListRunSteps429 = ErrorResponse +export type GetRunStepParams = { + readonly "include[]"?: ReadonlyArray<"step_details.tool_calls[*].file_search.results[*].content"> +} +export const GetRunStepParams = Schema.Struct({ + "include[]": Schema.optionalKey( + Schema.Array(Schema.Literal("step_details.tool_calls[*].file_search.results[*].content")) + ) +}) +export type GetRunStep200 = RunStepObject +export const GetRunStep200 = RunStepObject +export type GetRunStep429 = ErrorResponse +export const GetRunStep429 = ErrorResponse +export type SubmitToolOuputsToRunRequestJson = SubmitToolOutputsRunRequest +export const SubmitToolOuputsToRunRequestJson = SubmitToolOutputsRunRequest +export type SubmitToolOuputsToRun200 = RunObject +export const SubmitToolOuputsToRun200 = RunObject +export type SubmitToolOuputsToRun200Sse = AssistantStreamEvent +export const SubmitToolOuputsToRun200Sse = AssistantStreamEvent +export type SubmitToolOuputsToRun429 = ErrorResponse +export const SubmitToolOuputsToRun429 = ErrorResponse +export type CreateUploadRequestJson = CreateUploadRequest +export const CreateUploadRequestJson = CreateUploadRequest +export type CreateUpload200 = Upload +export const CreateUpload200 = Upload +export type CreateUpload400 = ErrorResponse +export const CreateUpload400 = ErrorResponse +export type CreateUpload429 = ErrorResponse +export const CreateUpload429 = ErrorResponse +export type CancelUpload200 = Upload +export const CancelUpload200 = Upload +export type CancelUpload400 = ErrorResponse +export const CancelUpload400 = ErrorResponse +export type CancelUpload404 = ErrorResponse +export const CancelUpload404 = ErrorResponse +export type CancelUpload410 = ErrorResponse +export const CancelUpload410 = ErrorResponse +export type CancelUpload429 = ErrorResponse +export const CancelUpload429 = ErrorResponse +export type CompleteUploadRequestJson = CompleteUploadRequest +export const CompleteUploadRequestJson = CompleteUploadRequest +export type CompleteUpload200 = Upload +export const CompleteUpload200 = Upload +export type CompleteUpload400 = ErrorResponse +export const CompleteUpload400 = ErrorResponse +export type CompleteUpload404 = ErrorResponse +export const CompleteUpload404 = ErrorResponse +export type CompleteUpload410 = ErrorResponse +export const CompleteUpload410 = ErrorResponse +export type CompleteUpload429 = ErrorResponse +export const CompleteUpload429 = ErrorResponse +export type CompleteUpload500 = ErrorResponse +export const CompleteUpload500 = ErrorResponse +export type AddUploadPartRequestFormData = AddUploadPartRequestMultipart +export const AddUploadPartRequestFormData = AddUploadPartRequestMultipart +export type AddUploadPart200 = UploadPart +export const AddUploadPart200 = UploadPart +export type AddUploadPart400 = ErrorResponse +export const AddUploadPart400 = ErrorResponse +export type AddUploadPart404 = ErrorResponse +export const AddUploadPart404 = ErrorResponse +export type AddUploadPart410 = ErrorResponse +export const AddUploadPart410 = ErrorResponse +export type AddUploadPart429 = ErrorResponse +export const AddUploadPart429 = ErrorResponse +export type AddUploadPart500 = ErrorResponse +export const AddUploadPart500 = ErrorResponse +export type ListVectorStoresParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string +} +export const ListVectorStoresParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String) +}) +export type ListVectorStores200 = ListVectorStoresResponse +export const ListVectorStores200 = ListVectorStoresResponse +export type ListVectorStores429 = ErrorResponse +export const ListVectorStores429 = ErrorResponse +export type ListVectorStores503 = ErrorResponse +export const ListVectorStores503 = ErrorResponse +export type CreateVectorStoreRequestJson = CreateVectorStoreRequest +export const CreateVectorStoreRequestJson = CreateVectorStoreRequest +export type CreateVectorStore200 = VectorStoreObject +export const CreateVectorStore200 = VectorStoreObject +export type CreateVectorStore400 = ErrorResponse +export const CreateVectorStore400 = ErrorResponse +export type CreateVectorStore404 = ErrorResponse +export const CreateVectorStore404 = ErrorResponse +export type CreateVectorStore429 = ErrorResponse +export const CreateVectorStore429 = ErrorResponse +export type GetVectorStore200 = VectorStoreObject +export const GetVectorStore200 = VectorStoreObject +export type GetVectorStore404 = ErrorResponse +export const GetVectorStore404 = ErrorResponse +export type GetVectorStore429 = ErrorResponse +export const GetVectorStore429 = ErrorResponse +export type ModifyVectorStoreRequestJson = UpdateVectorStoreRequest +export const ModifyVectorStoreRequestJson = UpdateVectorStoreRequest +export type ModifyVectorStore200 = VectorStoreObject +export const ModifyVectorStore200 = VectorStoreObject +export type ModifyVectorStore400 = ErrorResponse +export const ModifyVectorStore400 = ErrorResponse +export type ModifyVectorStore403 = ErrorResponse +export const ModifyVectorStore403 = ErrorResponse +export type ModifyVectorStore404 = ErrorResponse +export const ModifyVectorStore404 = ErrorResponse +export type ModifyVectorStore429 = ErrorResponse +export const ModifyVectorStore429 = ErrorResponse +export type DeleteVectorStore200 = DeleteVectorStoreResponse +export const DeleteVectorStore200 = DeleteVectorStoreResponse +export type DeleteVectorStore404 = ErrorResponse +export const DeleteVectorStore404 = ErrorResponse +export type DeleteVectorStore409 = ErrorResponse +export const DeleteVectorStore409 = ErrorResponse +export type DeleteVectorStore429 = ErrorResponse +export const DeleteVectorStore429 = ErrorResponse +export type CreateVectorStoreFileBatchRequestJson = CreateVectorStoreFileBatchRequest +export const CreateVectorStoreFileBatchRequestJson = CreateVectorStoreFileBatchRequest +export type CreateVectorStoreFileBatch200 = VectorStoreFileBatchObject +export const CreateVectorStoreFileBatch200 = VectorStoreFileBatchObject +export type CreateVectorStoreFileBatch400 = ErrorResponse +export const CreateVectorStoreFileBatch400 = ErrorResponse +export type CreateVectorStoreFileBatch404 = ErrorResponse +export const CreateVectorStoreFileBatch404 = ErrorResponse +export type CreateVectorStoreFileBatch409 = ErrorResponse +export const CreateVectorStoreFileBatch409 = ErrorResponse +export type CreateVectorStoreFileBatch429 = ErrorResponse +export const CreateVectorStoreFileBatch429 = ErrorResponse +export type CreateVectorStoreFileBatch500 = ErrorResponse +export const CreateVectorStoreFileBatch500 = ErrorResponse +export type GetVectorStoreFileBatch200 = VectorStoreFileBatchObject +export const GetVectorStoreFileBatch200 = VectorStoreFileBatchObject +export type GetVectorStoreFileBatch400 = ErrorResponse +export const GetVectorStoreFileBatch400 = ErrorResponse +export type GetVectorStoreFileBatch404 = ErrorResponse +export const GetVectorStoreFileBatch404 = ErrorResponse +export type GetVectorStoreFileBatch429 = ErrorResponse +export const GetVectorStoreFileBatch429 = ErrorResponse +export type CancelVectorStoreFileBatch200 = VectorStoreFileBatchObject +export const CancelVectorStoreFileBatch200 = VectorStoreFileBatchObject +export type CancelVectorStoreFileBatch400 = ErrorResponse +export const CancelVectorStoreFileBatch400 = ErrorResponse +export type CancelVectorStoreFileBatch404 = ErrorResponse +export const CancelVectorStoreFileBatch404 = ErrorResponse +export type CancelVectorStoreFileBatch409 = ErrorResponse +export const CancelVectorStoreFileBatch409 = ErrorResponse +export type CancelVectorStoreFileBatch429 = ErrorResponse +export const CancelVectorStoreFileBatch429 = ErrorResponse +export type ListFilesInVectorStoreBatchParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string + readonly "filter"?: "in_progress" | "completed" | "failed" | "cancelled" +} +export const ListFilesInVectorStoreBatchParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String), + "filter": Schema.optionalKey(Schema.Literals(["in_progress", "completed", "failed", "cancelled"])) +}) +export type ListFilesInVectorStoreBatch200 = ListVectorStoreFilesResponse +export const ListFilesInVectorStoreBatch200 = ListVectorStoreFilesResponse +export type ListFilesInVectorStoreBatch400 = ErrorResponse +export const ListFilesInVectorStoreBatch400 = ErrorResponse +export type ListFilesInVectorStoreBatch404 = ErrorResponse +export const ListFilesInVectorStoreBatch404 = ErrorResponse +export type ListFilesInVectorStoreBatch429 = ErrorResponse +export const ListFilesInVectorStoreBatch429 = ErrorResponse +export type ListVectorStoreFilesParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "before"?: string + readonly "filter"?: "in_progress" | "completed" | "failed" | "cancelled" +} +export const ListVectorStoreFilesParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "before": Schema.optionalKey(Schema.String), + "filter": Schema.optionalKey(Schema.Literals(["in_progress", "completed", "failed", "cancelled"])) +}) +export type ListVectorStoreFiles200 = ListVectorStoreFilesResponse +export const ListVectorStoreFiles200 = ListVectorStoreFilesResponse +export type ListVectorStoreFiles400 = ErrorResponse +export const ListVectorStoreFiles400 = ErrorResponse +export type ListVectorStoreFiles404 = ErrorResponse +export const ListVectorStoreFiles404 = ErrorResponse +export type ListVectorStoreFiles429 = ErrorResponse +export const ListVectorStoreFiles429 = ErrorResponse +export type CreateVectorStoreFileRequestJson = CreateVectorStoreFileRequest +export const CreateVectorStoreFileRequestJson = CreateVectorStoreFileRequest +export type CreateVectorStoreFile200 = VectorStoreFileObject +export const CreateVectorStoreFile200 = VectorStoreFileObject +export type CreateVectorStoreFile400 = ErrorResponse +export const CreateVectorStoreFile400 = ErrorResponse +export type CreateVectorStoreFile404 = ErrorResponse +export const CreateVectorStoreFile404 = ErrorResponse +export type CreateVectorStoreFile409 = ErrorResponse +export const CreateVectorStoreFile409 = ErrorResponse +export type CreateVectorStoreFile429 = ErrorResponse +export const CreateVectorStoreFile429 = ErrorResponse +export type CreateVectorStoreFile500 = ErrorResponse +export const CreateVectorStoreFile500 = ErrorResponse +export type GetVectorStoreFile200 = VectorStoreFileObject +export const GetVectorStoreFile200 = VectorStoreFileObject +export type GetVectorStoreFile404 = ErrorResponse +export const GetVectorStoreFile404 = ErrorResponse +export type GetVectorStoreFile429 = ErrorResponse +export const GetVectorStoreFile429 = ErrorResponse +export type GetVectorStoreFile500 = ErrorResponse +export const GetVectorStoreFile500 = ErrorResponse +export type UpdateVectorStoreFileAttributesRequestJson = UpdateVectorStoreFileAttributesRequest +export const UpdateVectorStoreFileAttributesRequestJson = UpdateVectorStoreFileAttributesRequest +export type UpdateVectorStoreFileAttributes200 = VectorStoreFileObject +export const UpdateVectorStoreFileAttributes200 = VectorStoreFileObject +export type UpdateVectorStoreFileAttributes400 = ErrorResponse +export const UpdateVectorStoreFileAttributes400 = ErrorResponse +export type UpdateVectorStoreFileAttributes404 = ErrorResponse +export const UpdateVectorStoreFileAttributes404 = ErrorResponse +export type UpdateVectorStoreFileAttributes409 = ErrorResponse +export const UpdateVectorStoreFileAttributes409 = ErrorResponse +export type UpdateVectorStoreFileAttributes429 = ErrorResponse +export const UpdateVectorStoreFileAttributes429 = ErrorResponse +export type UpdateVectorStoreFileAttributes500 = ErrorResponse +export const UpdateVectorStoreFileAttributes500 = ErrorResponse +export type DeleteVectorStoreFile200 = DeleteVectorStoreFileResponse +export const DeleteVectorStoreFile200 = DeleteVectorStoreFileResponse +export type DeleteVectorStoreFile400 = ErrorResponse +export const DeleteVectorStoreFile400 = ErrorResponse +export type DeleteVectorStoreFile404 = ErrorResponse +export const DeleteVectorStoreFile404 = ErrorResponse +export type DeleteVectorStoreFile409 = ErrorResponse +export const DeleteVectorStoreFile409 = ErrorResponse +export type DeleteVectorStoreFile429 = ErrorResponse +export const DeleteVectorStoreFile429 = ErrorResponse +export type DeleteVectorStoreFile500 = ErrorResponse +export const DeleteVectorStoreFile500 = ErrorResponse +export type RetrieveVectorStoreFileContent200 = VectorStoreFileContentResponse +export const RetrieveVectorStoreFileContent200 = VectorStoreFileContentResponse +export type RetrieveVectorStoreFileContent400 = ErrorResponse +export const RetrieveVectorStoreFileContent400 = ErrorResponse +export type RetrieveVectorStoreFileContent404 = ErrorResponse +export const RetrieveVectorStoreFileContent404 = ErrorResponse +export type RetrieveVectorStoreFileContent429 = ErrorResponse +export const RetrieveVectorStoreFileContent429 = ErrorResponse +export type RetrieveVectorStoreFileContent500 = ErrorResponse +export const RetrieveVectorStoreFileContent500 = ErrorResponse +export type SearchVectorStoreRequestJson = VectorStoreSearchRequest +export const SearchVectorStoreRequestJson = VectorStoreSearchRequest +export type SearchVectorStore200 = VectorStoreSearchResultsPage +export const SearchVectorStore200 = VectorStoreSearchResultsPage +export type SearchVectorStore400 = ErrorResponse +export const SearchVectorStore400 = ErrorResponse +export type SearchVectorStore404 = ErrorResponse +export const SearchVectorStore404 = ErrorResponse +export type SearchVectorStore429 = ErrorResponse +export const SearchVectorStore429 = ErrorResponse +export type CreateConversationRequestJson = CreateConversationBody +export const CreateConversationRequestJson = CreateConversationBody +export type CreateConversation200 = ConversationResource +export const CreateConversation200 = ConversationResource +export type CreateConversation400 = ErrorResponse +export const CreateConversation400 = ErrorResponse +export type CreateConversation429 = ErrorResponse +export const CreateConversation429 = ErrorResponse +export type CreateConversation500 = ErrorResponse +export const CreateConversation500 = ErrorResponse +export type GetConversation200 = ConversationResource +export const GetConversation200 = ConversationResource +export type GetConversation404 = ErrorResponse +export const GetConversation404 = ErrorResponse +export type GetConversation429 = ErrorResponse +export const GetConversation429 = ErrorResponse +export type UpdateConversationRequestJson = UpdateConversationBody +export const UpdateConversationRequestJson = UpdateConversationBody +export type UpdateConversation200 = ConversationResource +export const UpdateConversation200 = ConversationResource +export type UpdateConversation400 = ErrorResponse +export const UpdateConversation400 = ErrorResponse +export type UpdateConversation404 = ErrorResponse +export const UpdateConversation404 = ErrorResponse +export type UpdateConversation429 = ErrorResponse +export const UpdateConversation429 = ErrorResponse +export type UpdateConversation500 = ErrorResponse +export const UpdateConversation500 = ErrorResponse +export type DeleteConversation200 = DeletedConversationResource +export const DeleteConversation200 = DeletedConversationResource +export type DeleteConversation404 = ErrorResponse +export const DeleteConversation404 = ErrorResponse +export type DeleteConversation429 = ErrorResponse +export const DeleteConversation429 = ErrorResponse +export type Getsafetycase200 = SafetyCaseResource +export const Getsafetycase200 = SafetyCaseResource +export type Getsafetycase400 = ErrorResponse +export const Getsafetycase400 = ErrorResponse +export type Getsafetycase404 = ErrorResponse +export const Getsafetycase404 = ErrorResponse +export type Getsafetycase429 = ErrorResponse +export const Getsafetycase429 = ErrorResponse +export type Getsafetycase500 = ErrorResponse +export const Getsafetycase500 = ErrorResponse +export type Getsafetycase503 = ErrorResponse +export const Getsafetycase503 = ErrorResponse +export type Getprojectsafetyalert200 = SafetyAlertResource +export const Getprojectsafetyalert200 = SafetyAlertResource +export type Getprojectsafetyalert400 = ErrorResponse +export const Getprojectsafetyalert400 = ErrorResponse +export type Getprojectsafetyalert404 = ErrorResponse +export const Getprojectsafetyalert404 = ErrorResponse +export type Getprojectsafetyalert429 = ErrorResponse +export const Getprojectsafetyalert429 = ErrorResponse +export type Getprojectsafetyalert503 = ErrorResponse +export const Getprojectsafetyalert503 = ErrorResponse +export type DownloadLiveRecording400 = ErrorResponse +export const DownloadLiveRecording400 = ErrorResponse +export type DownloadLiveRecording404 = ErrorResponse +export const DownloadLiveRecording404 = ErrorResponse +export type DownloadLiveRecording429 = ErrorResponse +export const DownloadLiveRecording429 = ErrorResponse +export type DownloadLiveRecording500 = ErrorResponse +export const DownloadLiveRecording500 = ErrorResponse +export type DownloadLiveRecording503 = ErrorResponse +export const DownloadLiveRecording503 = ErrorResponse +export type OrganizationDecisionsusageParams = { + readonly "start_time": number + readonly "end_time"?: number | null + readonly "bucket_width"?: DecisionsUsageBucketWidth | null + readonly "limit"?: number | null + readonly "page"?: string | null + readonly "project_ids"?: ReadonlyArray | null + readonly "group_by"?: ReadonlyArray | null + readonly "user_ids"?: ReadonlyArray | null + readonly "api_key_ids"?: ReadonlyArray | null + readonly "models"?: ReadonlyArray | null + readonly "batch"?: boolean | null +} +export const OrganizationDecisionsusageParams = Schema.Struct({ + "start_time": Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), + "end_time": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + ), + "bucket_width": Schema.optionalKey( + Schema.Union([ + Schema.Union([DecisionsUsageBucketWidth], { mode: "oneOf" }).annotate({ + "description": + "Width of each time bucket in response. Currently `1m`, `1h` and `1d` are supported, default to `1d`." + }), + Schema.Null + ]) + ), + "limit": Schema.optionalKey( + Schema.Union([Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })), Schema.Null]) + ), + "page": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ + "description": + "A cursor for use in pagination. Corresponding to the `next_page` field from the previous response." + }), + Schema.Null + ]) + ), + "project_ids": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.Array(Schema.String)], { mode: "oneOf" }).annotate({ + "description": "Return only usage for these projects." + }), + Schema.Null + ]) + ), + "group_by": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.Array(DecisionsUsageGroupBy)], { mode: "oneOf" }).annotate({ + "description": + "Group the usage data by the specified fields. Support fields include `project_id`, `user_id`, `api_key_id`, `model`, `batch`, `service_tier`, `api_source` or any combination of them. When grouped by `api_source`, results use `agents_api` for attributed Agents API activity and `unlabeled` for all other activity. Without source grouping, `api_source` is null." + }), + Schema.Null + ]) + ), + "user_ids": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.Array(Schema.String)], { mode: "oneOf" }).annotate({ + "description": "Return only usage for these users." + }), + Schema.Null + ]) + ), + "api_key_ids": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.Array(Schema.String)], { mode: "oneOf" }).annotate({ + "description": "Return only usage for these API keys." + }), + Schema.Null + ]) + ), + "models": Schema.optionalKey( + Schema.Union([ + Schema.Union([Schema.Array(Schema.String)], { mode: "oneOf" }).annotate({ + "description": "Return only usage for these models." + }), + Schema.Null + ]) + ), + "batch": Schema.optionalKey(Schema.Union([Schema.Boolean, Schema.Null])) +}) +export type OrganizationDecisionsusage200 = PublicDecisionsUsageResponseResource +export const OrganizationDecisionsusage200 = PublicDecisionsUsageResponseResource +export type OrganizationDecisionsusage400 = DecisionsUsageErrorResponse +export const OrganizationDecisionsusage400 = DecisionsUsageErrorResponse +export type OrganizationDecisionsusage403 = DecisionsUsagePermissionErrorResponse +export const OrganizationDecisionsusage403 = DecisionsUsagePermissionErrorResponse +export type OrganizationDecisionsusage429 = ErrorResponse +export const OrganizationDecisionsusage429 = ErrorResponse +export type ListexternalstorageconfigurationsParams = { + readonly "project_id"?: string | null + readonly "after"?: string | null + readonly "order"?: ExternalStorageOrder + readonly "limit"?: number +} +export const ListexternalstorageconfigurationsParams = Schema.Struct({ + "project_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), + "after": Schema.optionalKey( + Schema.Union([ + Schema.String.annotate({ "description": "Return external storage configurations after this ID." }), + Schema.Null + ]) + ), + "order": Schema.optionalKey(ExternalStorageOrder), + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ) +}) +export type Listexternalstorageconfigurations200 = ExternalStorageListResource +export const Listexternalstorageconfigurations200 = ExternalStorageListResource +export type Listexternalstorageconfigurations400 = ErrorResponse +export const Listexternalstorageconfigurations400 = ErrorResponse +export type Listexternalstorageconfigurations403 = ErrorResponse +export const Listexternalstorageconfigurations403 = ErrorResponse +export type Listexternalstorageconfigurations404 = ErrorResponse +export const Listexternalstorageconfigurations404 = ErrorResponse +export type CreateanexternalstorageconfigurationRequestJson = CreateExternalStorageBody +export const CreateanexternalstorageconfigurationRequestJson = CreateExternalStorageBody +export type Createanexternalstorageconfiguration200 = ExternalStorageResponse +export const Createanexternalstorageconfiguration200 = ExternalStorageResponse +export type Createanexternalstorageconfiguration400 = ErrorResponse +export const Createanexternalstorageconfiguration400 = ErrorResponse +export type Createanexternalstorageconfiguration403 = ErrorResponse +export const Createanexternalstorageconfiguration403 = ErrorResponse +export type Createanexternalstorageconfiguration404 = ErrorResponse +export const Createanexternalstorageconfiguration404 = ErrorResponse +export type Createanexternalstorageconfiguration503 = ErrorResponse +export const Createanexternalstorageconfiguration503 = ErrorResponse +export type Getanexternalstorageconfiguration200 = ExternalStorageResponse +export const Getanexternalstorageconfiguration200 = ExternalStorageResponse +export type Getanexternalstorageconfiguration400 = ErrorResponse +export const Getanexternalstorageconfiguration400 = ErrorResponse +export type Getanexternalstorageconfiguration403 = ErrorResponse +export const Getanexternalstorageconfiguration403 = ErrorResponse +export type Getanexternalstorageconfiguration404 = ErrorResponse +export const Getanexternalstorageconfiguration404 = ErrorResponse +export type Deleteanexternalstorageconfiguration200 = ExternalStorageDeletedResource +export const Deleteanexternalstorageconfiguration200 = ExternalStorageDeletedResource +export type Deleteanexternalstorageconfiguration400 = ErrorResponse +export const Deleteanexternalstorageconfiguration400 = ErrorResponse +export type Deleteanexternalstorageconfiguration403 = ErrorResponse +export const Deleteanexternalstorageconfiguration403 = ErrorResponse +export type Deleteanexternalstorageconfiguration404 = ErrorResponse +export const Deleteanexternalstorageconfiguration404 = ErrorResponse +export type Validateanexternalstorageconfiguration200 = ExternalStorageResponse +export const Validateanexternalstorageconfiguration200 = ExternalStorageResponse +export type Validateanexternalstorageconfiguration400 = ErrorResponse +export const Validateanexternalstorageconfiguration400 = ErrorResponse +export type Validateanexternalstorageconfiguration403 = ErrorResponse +export const Validateanexternalstorageconfiguration403 = ErrorResponse +export type Validateanexternalstorageconfiguration404 = ErrorResponse +export const Validateanexternalstorageconfiguration404 = ErrorResponse +export type Validateanexternalstorageconfiguration503 = ErrorResponse +export const Validateanexternalstorageconfiguration503 = ErrorResponse +export type Getorganizationspendlimit200 = OrganizationSpendLimitResource +export const Getorganizationspendlimit200 = OrganizationSpendLimitResource +export type Getorganizationspendlimit403 = PermissionErrorResponse +export const Getorganizationspendlimit403 = PermissionErrorResponse +export type Getorganizationspendlimit404 = ErrorResponse +export const Getorganizationspendlimit404 = ErrorResponse +export type UpdateorganizationspendlimitRequestJson = UpdateOrganizationSpendLimitBody +export const UpdateorganizationspendlimitRequestJson = UpdateOrganizationSpendLimitBody +export type Updateorganizationspendlimit200 = OrganizationSpendLimitResource +export const Updateorganizationspendlimit200 = OrganizationSpendLimitResource +export type Updateorganizationspendlimit400 = ErrorResponse +export const Updateorganizationspendlimit400 = ErrorResponse +export type Updateorganizationspendlimit403 = PermissionErrorResponse +export const Updateorganizationspendlimit403 = PermissionErrorResponse +export type Deleteorganizationspendlimit200 = OrganizationSpendLimitDeletedResource +export const Deleteorganizationspendlimit200 = OrganizationSpendLimitDeletedResource +export type Deleteorganizationspendlimit403 = PermissionErrorResponse +export const Deleteorganizationspendlimit403 = PermissionErrorResponse +export type Deleteorganizationspendlimit404 = ErrorResponse +export const Deleteorganizationspendlimit404 = ErrorResponse +export type Getprojectspendlimit200 = ProjectSpendLimitResource +export const Getprojectspendlimit200 = ProjectSpendLimitResource +export type Getprojectspendlimit400 = ErrorResponse +export const Getprojectspendlimit400 = ErrorResponse +export type Getprojectspendlimit403 = PermissionErrorResponse +export const Getprojectspendlimit403 = PermissionErrorResponse +export type Getprojectspendlimit404 = ErrorResponse +export const Getprojectspendlimit404 = ErrorResponse +export type UpdateprojectspendlimitRequestJson = UpdateProjectSpendLimitBody +export const UpdateprojectspendlimitRequestJson = UpdateProjectSpendLimitBody +export type Updateprojectspendlimit200 = ProjectSpendLimitResource +export const Updateprojectspendlimit200 = ProjectSpendLimitResource +export type Updateprojectspendlimit400 = ErrorResponse +export const Updateprojectspendlimit400 = ErrorResponse +export type Updateprojectspendlimit403 = PermissionErrorResponse +export const Updateprojectspendlimit403 = PermissionErrorResponse +export type Updateprojectspendlimit404 = ErrorResponse +export const Updateprojectspendlimit404 = ErrorResponse +export type Deleteprojectspendlimit200 = ProjectSpendLimitDeletedResource +export const Deleteprojectspendlimit200 = ProjectSpendLimitDeletedResource +export type Deleteprojectspendlimit400 = ErrorResponse +export const Deleteprojectspendlimit400 = ErrorResponse +export type Deleteprojectspendlimit403 = PermissionErrorResponse +export const Deleteprojectspendlimit403 = PermissionErrorResponse +export type Deleteprojectspendlimit404 = ErrorResponse +export const Deleteprojectspendlimit404 = ErrorResponse +export type CreateanAPIkeyforaserviceaccountRequestJson = CreateProjectServiceAccountApiKeyBody +export const CreateanAPIkeyforaserviceaccountRequestJson = CreateProjectServiceAccountApiKeyBody +export type CreateanAPIkeyforaserviceaccount200 = ServiceAccountApiKeyBody +export const CreateanAPIkeyforaserviceaccount200 = ServiceAccountApiKeyBody +export type CreateanAPIkeyforaserviceaccount400 = ErrorResponse +export const CreateanAPIkeyforaserviceaccount400 = ErrorResponse +export type CreateanAPIkeyforaserviceaccount403 = PermissionErrorResponse +export const CreateanAPIkeyforaserviceaccount403 = PermissionErrorResponse +export type CreateanAPIkeyforaserviceaccount404 = ErrorResponse +export const CreateanAPIkeyforaserviceaccount404 = ErrorResponse +export type CreateanAPIkeyforaserviceaccount409 = ErrorResponse +export const CreateanAPIkeyforaserviceaccount409 = ErrorResponse +export type CreateanAPIkeyforaserviceaccount429 = ErrorResponse +export const CreateanAPIkeyforaserviceaccount429 = ErrorResponse +export type CreatecontentprovenancecheckRequestFormData = CreateContentProvenanceBodyMultipart +export const CreatecontentprovenancecheckRequestFormData = CreateContentProvenanceBodyMultipart +export type Createcontentprovenancecheck200 = ProvenanceResource +export const Createcontentprovenancecheck200 = ProvenanceResource +export type Createcontentprovenancecheck400 = ErrorResponse +export const Createcontentprovenancecheck400 = ErrorResponse +export type Createcontentprovenancecheck404 = ErrorResponse +export const Createcontentprovenancecheck404 = ErrorResponse +export type Createcontentprovenancecheck429 = ErrorResponse +export const Createcontentprovenancecheck429 = ErrorResponse +export type Createcontentprovenancecheck500 = ErrorResponse +export const Createcontentprovenancecheck500 = ErrorResponse +export type Createcontentprovenancecheck503 = ErrorResponse +export const Createcontentprovenancecheck503 = ErrorResponse +export type ListVideosParams = { readonly "limit"?: number; readonly "order"?: OrderEnum; readonly "after"?: string } +export const ListVideosParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(OrderEnum), + "after": Schema.optionalKey( + Schema.String.annotate({ "description": "Identifier for the last item from the previous pagination request" }) + ) +}) +export type ListVideos200 = VideoListResource +export const ListVideos200 = VideoListResource +export type ListVideos400 = ErrorResponse +export const ListVideos400 = ErrorResponse +export type ListVideos404 = ErrorResponse +export const ListVideos404 = ErrorResponse +export type CreateVideoRequestJson = CreateVideoJsonBody +export const CreateVideoRequestJson = CreateVideoJsonBody +export type CreateVideoRequestFormData = CreateVideoMultipartBodyMultipart +export const CreateVideoRequestFormData = CreateVideoMultipartBodyMultipart +export type CreateVideo200 = VideoResource +export const CreateVideo200 = VideoResource +export type CreateVideo400 = ErrorResponse +export const CreateVideo400 = ErrorResponse +export type CreateVideo404 = ErrorResponse +export const CreateVideo404 = ErrorResponse +export type CreateVideo429 = ErrorResponse +export const CreateVideo429 = ErrorResponse +export type CreateVideo500 = ErrorResponse +export const CreateVideo500 = ErrorResponse +export type CreateVideo503 = ErrorResponse +export const CreateVideo503 = ErrorResponse +export type CreateVideoCharacterRequestFormData = CreateVideoCharacterBodyMultipart +export const CreateVideoCharacterRequestFormData = CreateVideoCharacterBodyMultipart +export type CreateVideoCharacter200 = VideoCharacterResource +export const CreateVideoCharacter200 = VideoCharacterResource +export type CreateVideoCharacter400 = ErrorResponse +export const CreateVideoCharacter400 = ErrorResponse +export type CreateVideoCharacter404 = ErrorResponse +export const CreateVideoCharacter404 = ErrorResponse +export type CreateVideoCharacter500 = ErrorResponse +export const CreateVideoCharacter500 = ErrorResponse +export type CreateVideoCharacter504 = ErrorResponse +export const CreateVideoCharacter504 = ErrorResponse +export type GetVideoCharacter200 = VideoCharacterResource +export const GetVideoCharacter200 = VideoCharacterResource +export type GetVideoCharacter400 = ErrorResponse +export const GetVideoCharacter400 = ErrorResponse +export type GetVideoCharacter404 = ErrorResponse +export const GetVideoCharacter404 = ErrorResponse +export type CreateVideoEditRequestJson = CreateVideoEditJsonBody +export const CreateVideoEditRequestJson = CreateVideoEditJsonBody +export type CreateVideoEditRequestFormData = CreateVideoEditMultipartBodyMultipart +export const CreateVideoEditRequestFormData = CreateVideoEditMultipartBodyMultipart +export type CreateVideoEdit200 = VideoResource +export const CreateVideoEdit200 = VideoResource +export type CreateVideoEdit400 = ErrorResponse +export const CreateVideoEdit400 = ErrorResponse +export type CreateVideoEdit404 = ErrorResponse +export const CreateVideoEdit404 = ErrorResponse +export type CreateVideoEdit429 = ErrorResponse +export const CreateVideoEdit429 = ErrorResponse +export type CreateVideoEdit500 = ErrorResponse +export const CreateVideoEdit500 = ErrorResponse +export type CreateVideoEdit503 = ErrorResponse +export const CreateVideoEdit503 = ErrorResponse +export type CreateVideoExtendRequestJson = CreateVideoExtendJsonBody +export const CreateVideoExtendRequestJson = CreateVideoExtendJsonBody +export type CreateVideoExtendRequestFormData = CreateVideoExtendMultipartBodyMultipart +export const CreateVideoExtendRequestFormData = CreateVideoExtendMultipartBodyMultipart +export type CreateVideoExtend200 = VideoResource +export const CreateVideoExtend200 = VideoResource +export type CreateVideoExtend400 = ErrorResponse +export const CreateVideoExtend400 = ErrorResponse +export type CreateVideoExtend404 = ErrorResponse +export const CreateVideoExtend404 = ErrorResponse +export type CreateVideoExtend429 = ErrorResponse +export const CreateVideoExtend429 = ErrorResponse +export type CreateVideoExtend500 = ErrorResponse +export const CreateVideoExtend500 = ErrorResponse +export type CreateVideoExtend503 = ErrorResponse +export const CreateVideoExtend503 = ErrorResponse +export type GetVideo200 = VideoResource +export const GetVideo200 = VideoResource +export type GetVideo400 = ErrorResponse +export const GetVideo400 = ErrorResponse +export type GetVideo404 = ErrorResponse +export const GetVideo404 = ErrorResponse +export type DeleteVideo200 = DeletedVideoResource +export const DeleteVideo200 = DeletedVideoResource +export type DeleteVideo400 = ErrorResponse +export const DeleteVideo400 = ErrorResponse +export type DeleteVideo404 = ErrorResponse +export const DeleteVideo404 = ErrorResponse +export type DeleteVideo500 = ErrorResponse +export const DeleteVideo500 = ErrorResponse +export type RetrieveVideoContentParams = { readonly "variant"?: VideoContentVariant } +export const RetrieveVideoContentParams = Schema.Struct({ "variant": Schema.optionalKey(VideoContentVariant) }) +export type RetrieveVideoContent200 = string +export const RetrieveVideoContent200 = Schema.String +export type RetrieveVideoContent400 = ErrorResponse +export const RetrieveVideoContent400 = ErrorResponse +export type RetrieveVideoContent404 = ErrorResponse +export const RetrieveVideoContent404 = ErrorResponse +export type RetrieveVideoContent500 = ErrorResponse +export const RetrieveVideoContent500 = ErrorResponse +export type CreateVideoRemixRequestJson = CreateVideoRemixBody +export const CreateVideoRemixRequestJson = CreateVideoRemixBody +export type CreateVideoRemixRequestFormData = CreateVideoRemixBody +export const CreateVideoRemixRequestFormData = CreateVideoRemixBody +export type CreateVideoRemix200 = VideoResource +export const CreateVideoRemix200 = VideoResource +export type CreateVideoRemix400 = ErrorResponse +export const CreateVideoRemix400 = ErrorResponse +export type CreateVideoRemix404 = ErrorResponse +export const CreateVideoRemix404 = ErrorResponse +export type CreateVideoRemix429 = ErrorResponse +export const CreateVideoRemix429 = ErrorResponse +export type CreateVideoRemix500 = ErrorResponse +export const CreateVideoRemix500 = ErrorResponse +export type CreateVideoRemix503 = ErrorResponse +export const CreateVideoRemix503 = ErrorResponse +export type GetinputtokencountsRequestJson = TokenCountsBody +export const GetinputtokencountsRequestJson = TokenCountsBody +export type Getinputtokencounts200 = TokenCountsResource +export const Getinputtokencounts200 = TokenCountsResource +export type Getinputtokencounts400 = ErrorResponse +export const Getinputtokencounts400 = ErrorResponse +export type Getinputtokencounts429 = ErrorResponse +export const Getinputtokencounts429 = ErrorResponse +export type CompactconversationRequestJson = CompactResponseMethodPublicBody +export const CompactconversationRequestJson = CompactResponseMethodPublicBody +export type Compactconversation200 = CompactResource +export const Compactconversation200 = CompactResource +export type Compactconversation400 = ErrorResponse +export const Compactconversation400 = ErrorResponse +export type Compactconversation429 = ErrorResponse +export const Compactconversation429 = ErrorResponse +export type Compactconversation503 = ErrorResponse +export const Compactconversation503 = ErrorResponse +export type ListSkillsParams = { readonly "limit"?: number; readonly "order"?: OrderEnum; readonly "after"?: string } +export const ListSkillsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(OrderEnum), + "after": Schema.optionalKey( + Schema.String.annotate({ "description": "Identifier for the last item from the previous pagination request" }) + ) +}) +export type ListSkills200 = SkillListResource +export const ListSkills200 = SkillListResource +export type ListSkills404 = ErrorResponse +export const ListSkills404 = ErrorResponse +export type ListSkills429 = ErrorResponse +export const ListSkills429 = ErrorResponse +export type CreateSkillRequestJson = CreateSkillBody +export const CreateSkillRequestJson = CreateSkillBody +export type CreateSkillRequestFormData = CreateSkillBodyMultipart +export const CreateSkillRequestFormData = CreateSkillBodyMultipart +export type CreateSkill200 = SkillResource +export const CreateSkill200 = SkillResource +export type CreateSkill400 = ErrorResponse +export const CreateSkill400 = ErrorResponse +export type CreateSkill429 = ErrorResponse +export const CreateSkill429 = ErrorResponse +export type CreateSkill500 = ErrorResponse +export const CreateSkill500 = ErrorResponse +export type GetSkill200 = SkillResource +export const GetSkill200 = SkillResource +export type GetSkill404 = ErrorResponse +export const GetSkill404 = ErrorResponse +export type GetSkill429 = ErrorResponse +export const GetSkill429 = ErrorResponse +export type UpdateSkillDefaultVersionRequestJson = SetDefaultSkillVersionBody +export const UpdateSkillDefaultVersionRequestJson = SetDefaultSkillVersionBody +export type UpdateSkillDefaultVersion200 = SkillResource +export const UpdateSkillDefaultVersion200 = SkillResource +export type UpdateSkillDefaultVersion400 = ErrorResponse +export const UpdateSkillDefaultVersion400 = ErrorResponse +export type UpdateSkillDefaultVersion404 = ErrorResponse +export const UpdateSkillDefaultVersion404 = ErrorResponse +export type UpdateSkillDefaultVersion429 = ErrorResponse +export const UpdateSkillDefaultVersion429 = ErrorResponse +export type DeleteSkill200 = DeletedSkillResource +export const DeleteSkill200 = DeletedSkillResource +export type DeleteSkill404 = ErrorResponse +export const DeleteSkill404 = ErrorResponse +export type DeleteSkill429 = ErrorResponse +export const DeleteSkill429 = ErrorResponse +export type GetSkillContent200 = string +export const GetSkillContent200 = Schema.String +export type GetSkillContent404 = ErrorResponse +export const GetSkillContent404 = ErrorResponse +export type GetSkillContent429 = ErrorResponse +export const GetSkillContent429 = ErrorResponse +export type ListSkillVersionsParams = { + readonly "limit"?: number + readonly "order"?: OrderEnum + readonly "after"?: string +} +export const ListSkillVersionsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(OrderEnum), + "after": Schema.optionalKey(Schema.String) +}) +export type ListSkillVersions200 = SkillVersionListResource +export const ListSkillVersions200 = SkillVersionListResource +export type ListSkillVersions400 = ErrorResponse +export const ListSkillVersions400 = ErrorResponse +export type ListSkillVersions404 = ErrorResponse +export const ListSkillVersions404 = ErrorResponse +export type ListSkillVersions429 = ErrorResponse +export const ListSkillVersions429 = ErrorResponse +export type CreateSkillVersionRequestJson = CreateSkillVersionBody +export const CreateSkillVersionRequestJson = CreateSkillVersionBody +export type CreateSkillVersionRequestFormData = CreateSkillVersionBodyMultipart +export const CreateSkillVersionRequestFormData = CreateSkillVersionBodyMultipart +export type CreateSkillVersion200 = SkillVersionResource +export const CreateSkillVersion200 = SkillVersionResource +export type CreateSkillVersion400 = ErrorResponse +export const CreateSkillVersion400 = ErrorResponse +export type CreateSkillVersion404 = ErrorResponse +export const CreateSkillVersion404 = ErrorResponse +export type CreateSkillVersion429 = ErrorResponse +export const CreateSkillVersion429 = ErrorResponse +export type CreateSkillVersion500 = ErrorResponse +export const CreateSkillVersion500 = ErrorResponse +export type GetSkillVersion200 = SkillVersionResource +export const GetSkillVersion200 = SkillVersionResource +export type GetSkillVersion400 = ErrorResponse +export const GetSkillVersion400 = ErrorResponse +export type GetSkillVersion404 = ErrorResponse +export const GetSkillVersion404 = ErrorResponse +export type GetSkillVersion429 = ErrorResponse +export const GetSkillVersion429 = ErrorResponse +export type DeleteSkillVersion200 = DeletedSkillVersionResource +export const DeleteSkillVersion200 = DeletedSkillVersionResource +export type DeleteSkillVersion400 = ErrorResponse +export const DeleteSkillVersion400 = ErrorResponse +export type DeleteSkillVersion404 = ErrorResponse +export const DeleteSkillVersion404 = ErrorResponse +export type DeleteSkillVersion429 = ErrorResponse +export const DeleteSkillVersion429 = ErrorResponse +export type GetSkillVersionContent200 = string +export const GetSkillVersionContent200 = Schema.String +export type GetSkillVersionContent400 = ErrorResponse +export const GetSkillVersionContent400 = ErrorResponse +export type GetSkillVersionContent404 = ErrorResponse +export const GetSkillVersionContent404 = ErrorResponse +export type GetSkillVersionContent429 = ErrorResponse +export const GetSkillVersionContent429 = ErrorResponse +export type CancelChatSessionMethod200 = ChatSessionResource +export const CancelChatSessionMethod200 = ChatSessionResource +export type CancelChatSessionMethod400 = ErrorResponse +export const CancelChatSessionMethod400 = ErrorResponse +export type CancelChatSessionMethod404 = ErrorResponse +export const CancelChatSessionMethod404 = ErrorResponse +export type CancelChatSessionMethod429 = ErrorResponse +export const CancelChatSessionMethod429 = ErrorResponse +export type CreateChatSessionMethodRequestJson = CreateChatSessionBody +export const CreateChatSessionMethodRequestJson = CreateChatSessionBody +export type CreateChatSessionMethod200 = ChatSessionResource +export const CreateChatSessionMethod200 = ChatSessionResource +export type CreateChatSessionMethod400 = ErrorResponse +export const CreateChatSessionMethod400 = ErrorResponse +export type CreateChatSessionMethod404 = ErrorResponse +export const CreateChatSessionMethod404 = ErrorResponse +export type CreateChatSessionMethod429 = ErrorResponse +export const CreateChatSessionMethod429 = ErrorResponse +export type ListThreadItemsMethodParams = { + readonly "limit"?: number + readonly "order"?: OrderEnum + readonly "after"?: string + readonly "before"?: string +} +export const ListThreadItemsMethodParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(OrderEnum), + "after": Schema.optionalKey( + Schema.String.annotate({ + "description": "List items created after this thread item ID. Defaults to null for the first page." + }) + ), + "before": Schema.optionalKey( + Schema.String.annotate({ + "description": "List items created before this thread item ID. Defaults to null for the newest results." + }) + ) +}) +export type ListThreadItemsMethod200 = ThreadItemListResource +export const ListThreadItemsMethod200 = ThreadItemListResource +export type ListThreadItemsMethod400 = ErrorResponse +export const ListThreadItemsMethod400 = ErrorResponse +export type ListThreadItemsMethod404 = ErrorResponse +export const ListThreadItemsMethod404 = ErrorResponse +export type ListThreadItemsMethod429 = ErrorResponse +export const ListThreadItemsMethod429 = ErrorResponse +export type GetThreadMethod200 = ThreadResource +export const GetThreadMethod200 = ThreadResource +export type GetThreadMethod400 = ErrorResponse +export const GetThreadMethod400 = ErrorResponse +export type GetThreadMethod404 = ErrorResponse +export const GetThreadMethod404 = ErrorResponse +export type GetThreadMethod429 = ErrorResponse +export const GetThreadMethod429 = ErrorResponse +export type DeleteThreadMethod200 = DeletedThreadResource +export const DeleteThreadMethod200 = DeletedThreadResource +export type DeleteThreadMethod400 = ErrorResponse +export const DeleteThreadMethod400 = ErrorResponse +export type DeleteThreadMethod404 = ErrorResponse +export const DeleteThreadMethod404 = ErrorResponse +export type DeleteThreadMethod429 = ErrorResponse +export const DeleteThreadMethod429 = ErrorResponse +export type ListThreadsMethodParams = { + readonly "limit"?: number + readonly "order"?: OrderEnum + readonly "after"?: string + readonly "before"?: string + readonly "user"?: string +} +export const ListThreadsMethodParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(0).annotate({ "expected": "a value greater than or equal to 0" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(OrderEnum), + "after": Schema.optionalKey( + Schema.String.annotate({ + "description": "List items created after this thread item ID. Defaults to null for the first page." + }) + ), + "before": Schema.optionalKey( + Schema.String.annotate({ + "description": "List items created before this thread item ID. Defaults to null for the newest results." + }) + ), + "user": Schema.optionalKey( + Schema.String.annotate({ + "description": "Filter threads that belong to this user identifier. Defaults to null to return all users." + }).check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" }) + ) + ) +}) +export type ListThreadsMethod200 = ThreadListResource +export const ListThreadsMethod200 = ThreadListResource +export type ListThreadsMethod400 = ErrorResponse +export const ListThreadsMethod400 = ErrorResponse +export type ListThreadsMethod404 = ErrorResponse +export const ListThreadsMethod404 = ErrorResponse +export type ListThreadsMethod429 = ErrorResponse +export const ListThreadsMethod429 = ErrorResponse +export type CreateDecisionRequestJson = DecisionRequest +export const CreateDecisionRequestJson = DecisionRequest +export type CreateDecision200 = DecisionResponse +export const CreateDecision200 = DecisionResponse +export type ListAgentEnvironmentsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string + readonly "type"?: EnvironmentTypeParam +} +export const ListAgentEnvironmentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "type": Schema.optionalKey(EnvironmentTypeParam) +}) +export type ListAgentEnvironments200 = AgentEnvironmentListResource +export const ListAgentEnvironments200 = Schema.suspend((): Schema.Codec => + AgentEnvironmentListResource +).annotate({ "description": "An ID-paginated list of owned environments." }) +export type ListAgentEnvironments400 = ErrorResponse_2 +export const ListAgentEnvironments400 = ErrorResponse_2 +export type ListAgentEnvironments401 = ErrorResponse_2 +export const ListAgentEnvironments401 = ErrorResponse_2 +export type ListAgentEnvironments403 = ErrorResponse_2 +export const ListAgentEnvironments403 = ErrorResponse_2 +export type ListAgentEnvironments404 = ErrorResponse_2 +export const ListAgentEnvironments404 = ErrorResponse_2 +export type ListAgentEnvironments409 = ErrorResponse_2 +export const ListAgentEnvironments409 = ErrorResponse_2 +export type ListAgentEnvironments500 = ErrorResponse_2 +export const ListAgentEnvironments500 = ErrorResponse_2 +export type ListAgentEnvironments503 = ErrorResponse_2 +export const ListAgentEnvironments503 = ErrorResponse_2 +export type CreateAgentEnvironmentParams2 = { readonly "Idempotency-Key"?: string } +export const CreateAgentEnvironmentParams2 = Schema.Struct({ + "Idempotency-Key": Schema.optionalKey( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ) +}) +export type CreateAgentEnvironmentRequestJson = CreateAgentEnvironmentParams +export const CreateAgentEnvironmentRequestJson = CreateAgentEnvironmentParams +export type CreateAgentEnvironment201 = PublicEnvironmentResource +export const CreateAgentEnvironment201 = PublicEnvironmentResource +export type CreateAgentEnvironment400 = ErrorResponse_2 +export const CreateAgentEnvironment400 = ErrorResponse_2 +export type CreateAgentEnvironment401 = ErrorResponse_2 +export const CreateAgentEnvironment401 = ErrorResponse_2 +export type CreateAgentEnvironment403 = ErrorResponse_2 +export const CreateAgentEnvironment403 = ErrorResponse_2 +export type CreateAgentEnvironment404 = ErrorResponse_2 +export const CreateAgentEnvironment404 = ErrorResponse_2 +export type CreateAgentEnvironment409 = ErrorResponse_2 +export const CreateAgentEnvironment409 = ErrorResponse_2 +export type CreateAgentEnvironment429 = ErrorResponse_2 +export const CreateAgentEnvironment429 = ErrorResponse_2 +export type CreateAgentEnvironment500 = ErrorResponse_2 +export const CreateAgentEnvironment500 = ErrorResponse_2 +export type CreateAgentEnvironment503 = ErrorResponse_2 +export const CreateAgentEnvironment503 = ErrorResponse_2 +export type RetrieveAgentEnvironment200 = PublicEnvironmentResource +export const RetrieveAgentEnvironment200 = PublicEnvironmentResource +export type RetrieveAgentEnvironment400 = ErrorResponse_2 +export const RetrieveAgentEnvironment400 = ErrorResponse_2 +export type RetrieveAgentEnvironment401 = ErrorResponse_2 +export const RetrieveAgentEnvironment401 = ErrorResponse_2 +export type RetrieveAgentEnvironment403 = ErrorResponse_2 +export const RetrieveAgentEnvironment403 = ErrorResponse_2 +export type RetrieveAgentEnvironment404 = ErrorResponse_2 +export const RetrieveAgentEnvironment404 = ErrorResponse_2 +export type RetrieveAgentEnvironment409 = ErrorResponse_2 +export const RetrieveAgentEnvironment409 = ErrorResponse_2 +export type RetrieveAgentEnvironment500 = ErrorResponse_2 +export const RetrieveAgentEnvironment500 = ErrorResponse_2 +export type RetrieveAgentEnvironment503 = ErrorResponse_2 +export const RetrieveAgentEnvironment503 = ErrorResponse_2 +export type ListAgentEnvironmentFilesParams = { + readonly "path"?: string | null + readonly "limit"?: number | null + readonly "order"?: ListOrderParam + readonly "page"?: string +} +export const ListAgentEnvironmentFilesParams = Schema.Struct({ + "path": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]) + ), + "limit": Schema.optionalKey( + Schema.Union([ + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })), + Schema.Null + ]).annotate({ "format": "int64" }) + ), + "order": Schema.optionalKey(ListOrderParam), + "page": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentEnvironmentFiles200 = EnvironmentFileListResource +export const ListAgentEnvironmentFiles200 = EnvironmentFileListResource +export type ListAgentEnvironmentFiles400 = ErrorResponse_2 +export const ListAgentEnvironmentFiles400 = ErrorResponse_2 +export type ListAgentEnvironmentFiles401 = ErrorResponse_2 +export const ListAgentEnvironmentFiles401 = ErrorResponse_2 +export type ListAgentEnvironmentFiles403 = ErrorResponse_2 +export const ListAgentEnvironmentFiles403 = ErrorResponse_2 +export type ListAgentEnvironmentFiles404 = ErrorResponse_2 +export const ListAgentEnvironmentFiles404 = ErrorResponse_2 +export type ListAgentEnvironmentFiles409 = ErrorResponse_2 +export const ListAgentEnvironmentFiles409 = ErrorResponse_2 +export type ListAgentEnvironmentFiles500 = ErrorResponse_2 +export const ListAgentEnvironmentFiles500 = ErrorResponse_2 +export type ListAgentEnvironmentFiles503 = ErrorResponse_2 +export const ListAgentEnvironmentFiles503 = ErrorResponse_2 +export type CreateAgentEnvironmentFileRequestJson = HostedEnvironmentFileParam +export const CreateAgentEnvironmentFileRequestJson = HostedEnvironmentFileParam +export type CreateAgentEnvironmentFile201 = EnvironmentFileResource +export const CreateAgentEnvironmentFile201 = EnvironmentFileResource +export type CreateAgentEnvironmentFile400 = ErrorResponse_2 +export const CreateAgentEnvironmentFile400 = ErrorResponse_2 +export type CreateAgentEnvironmentFile401 = ErrorResponse_2 +export const CreateAgentEnvironmentFile401 = ErrorResponse_2 +export type CreateAgentEnvironmentFile403 = ErrorResponse_2 +export const CreateAgentEnvironmentFile403 = ErrorResponse_2 +export type CreateAgentEnvironmentFile404 = ErrorResponse_2 +export const CreateAgentEnvironmentFile404 = ErrorResponse_2 +export type CreateAgentEnvironmentFile409 = ErrorResponse_2 +export const CreateAgentEnvironmentFile409 = ErrorResponse_2 +export type CreateAgentEnvironmentFile429 = ErrorResponse_2 +export const CreateAgentEnvironmentFile429 = ErrorResponse_2 +export type CreateAgentEnvironmentFile500 = ErrorResponse_2 +export const CreateAgentEnvironmentFile500 = ErrorResponse_2 +export type CreateAgentEnvironmentFile503 = ErrorResponse_2 +export const CreateAgentEnvironmentFile503 = ErrorResponse_2 +export type ListAgentSessionSubagentsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionSubagentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionSubagents200 = { + readonly "object": "list" + readonly "data": ReadonlyArray + readonly "first_id": string | null + readonly "last_id": string | null + readonly "has_more": boolean +} +export const ListAgentSessionSubagents200 = Schema.Struct({ + "object": Schema.Literal("list").annotate({ "description": "The object type, which is always `list`." }), + "data": Schema.Array(SubagentResource).annotate({ + "description": "The resources returned in this page, in the requested sort order." + }).check(Schema.isMinLength(0).annotate({ "expected": "a value with a length of at least 0" })).check( + Schema.isMaxLength(2000).annotate({ "expected": "a value with a length of at most 2000" }) + ), + "first_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ "description": "The ID of the first resource in `data`, or `null` if the page is empty." }), + "last_id": Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })), + Schema.Null + ]).annotate({ + "description": + "The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters." + }), + "has_more": Schema.Boolean.annotate({ + "description": "Whether there are more resources to retrieve after this page." + }) +}).annotate({ "description": "A page of Agents API resources, with IDs for retrieving additional pages." }) +export type ListAgentSessionSubagents400 = ErrorResponse_2 +export const ListAgentSessionSubagents400 = ErrorResponse_2 +export type ListAgentSessionSubagents401 = ErrorResponse_2 +export const ListAgentSessionSubagents401 = ErrorResponse_2 +export type ListAgentSessionSubagents403 = ErrorResponse_2 +export const ListAgentSessionSubagents403 = ErrorResponse_2 +export type ListAgentSessionSubagents404 = ErrorResponse_2 +export const ListAgentSessionSubagents404 = ErrorResponse_2 +export type ListAgentSessionSubagents409 = ErrorResponse_2 +export const ListAgentSessionSubagents409 = ErrorResponse_2 +export type ListAgentSessionSubagents500 = ErrorResponse_2 +export const ListAgentSessionSubagents500 = ErrorResponse_2 +export type ListAgentSessionSubagents503 = ErrorResponse_2 +export const ListAgentSessionSubagents503 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent200 = SubagentResource +export const RetrieveAgentSessionSubagent200 = SubagentResource +export type RetrieveAgentSessionSubagent400 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent400 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent401 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent401 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent403 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent403 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent404 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent404 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent409 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent409 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent500 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent500 = ErrorResponse_2 +export type RetrieveAgentSessionSubagent503 = ErrorResponse_2 +export const RetrieveAgentSessionSubagent503 = ErrorResponse_2 +export type ListAgentSessionSubagentItemsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionSubagentItemsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionSubagentItems200 = SessionItemListResource +export const ListAgentSessionSubagentItems200 = SessionItemListResource +export type ListAgentSessionSubagentItems400 = ErrorResponse_2 +export const ListAgentSessionSubagentItems400 = ErrorResponse_2 +export type ListAgentSessionSubagentItems401 = ErrorResponse_2 +export const ListAgentSessionSubagentItems401 = ErrorResponse_2 +export type ListAgentSessionSubagentItems403 = ErrorResponse_2 +export const ListAgentSessionSubagentItems403 = ErrorResponse_2 +export type ListAgentSessionSubagentItems404 = ErrorResponse_2 +export const ListAgentSessionSubagentItems404 = ErrorResponse_2 +export type ListAgentSessionSubagentItems409 = ErrorResponse_2 +export const ListAgentSessionSubagentItems409 = ErrorResponse_2 +export type ListAgentSessionSubagentItems500 = ErrorResponse_2 +export const ListAgentSessionSubagentItems500 = ErrorResponse_2 +export type ListAgentSessionSubagentItems503 = ErrorResponse_2 +export const ListAgentSessionSubagentItems503 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionSubagentTurnsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionSubagentTurns200 = SessionTurnListResource +export const ListAgentSessionSubagentTurns200 = Schema.suspend((): Schema.Codec => + SessionTurnListResource +).annotate({ "description": "A page of turns from an agent session or subagent." }) +export type ListAgentSessionSubagentTurns400 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns400 = ErrorResponse_2 +export type ListAgentSessionSubagentTurns401 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns401 = ErrorResponse_2 +export type ListAgentSessionSubagentTurns403 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns403 = ErrorResponse_2 +export type ListAgentSessionSubagentTurns404 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns404 = ErrorResponse_2 +export type ListAgentSessionSubagentTurns409 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns409 = ErrorResponse_2 +export type ListAgentSessionSubagentTurns500 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns500 = ErrorResponse_2 +export type ListAgentSessionSubagentTurns503 = ErrorResponse_2 +export const ListAgentSessionSubagentTurns503 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn200 = TurnResource +export const RetrieveAgentSessionSubagentTurn200 = TurnResource +export type RetrieveAgentSessionSubagentTurn400 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn400 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn401 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn401 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn403 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn403 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn404 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn404 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn409 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn409 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn500 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn500 = ErrorResponse_2 +export type RetrieveAgentSessionSubagentTurn503 = ErrorResponse_2 +export const RetrieveAgentSessionSubagentTurn503 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItemsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionSubagentTurnItemsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionSubagentTurnItems200 = SessionItemListResource +export const ListAgentSessionSubagentTurnItems200 = SessionItemListResource +export type ListAgentSessionSubagentTurnItems400 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems400 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItems401 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems401 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItems403 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems403 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItems404 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems404 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItems409 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems409 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItems500 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems500 = ErrorResponse_2 +export type ListAgentSessionSubagentTurnItems503 = ErrorResponse_2 +export const ListAgentSessionSubagentTurnItems503 = ErrorResponse_2 +export type ListAgentsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgents200 = AgentListResource +export const ListAgents200 = Schema.suspend((): Schema.Codec => AgentListResource).annotate({ + "description": "A page of reusable agents." +}) +export type ListAgents400 = ErrorResponse_2 +export const ListAgents400 = ErrorResponse_2 +export type ListAgents401 = ErrorResponse_2 +export const ListAgents401 = ErrorResponse_2 +export type ListAgents403 = ErrorResponse_2 +export const ListAgents403 = ErrorResponse_2 +export type ListAgents404 = ErrorResponse_2 +export const ListAgents404 = ErrorResponse_2 +export type ListAgents409 = ErrorResponse_2 +export const ListAgents409 = ErrorResponse_2 +export type ListAgents500 = ErrorResponse_2 +export const ListAgents500 = ErrorResponse_2 +export type ListAgents503 = ErrorResponse_2 +export const ListAgents503 = ErrorResponse_2 +export type CreateAgentRequestJson = CreateAgentParams +export const CreateAgentRequestJson = CreateAgentParams +export type CreateAgent201 = AgentResource +export const CreateAgent201 = AgentResource +export type CreateAgent400 = ErrorResponse_2 +export const CreateAgent400 = ErrorResponse_2 +export type CreateAgent401 = ErrorResponse_2 +export const CreateAgent401 = ErrorResponse_2 +export type CreateAgent403 = ErrorResponse_2 +export const CreateAgent403 = ErrorResponse_2 +export type CreateAgent404 = ErrorResponse_2 +export const CreateAgent404 = ErrorResponse_2 +export type CreateAgent409 = ErrorResponse_2 +export const CreateAgent409 = ErrorResponse_2 +export type CreateAgent500 = ErrorResponse_2 +export const CreateAgent500 = ErrorResponse_2 +export type CreateAgent503 = ErrorResponse_2 +export const CreateAgent503 = ErrorResponse_2 +export type RetrieveAgent200 = AgentResource +export const RetrieveAgent200 = AgentResource +export type RetrieveAgent400 = ErrorResponse_2 +export const RetrieveAgent400 = ErrorResponse_2 +export type RetrieveAgent401 = ErrorResponse_2 +export const RetrieveAgent401 = ErrorResponse_2 +export type RetrieveAgent403 = ErrorResponse_2 +export const RetrieveAgent403 = ErrorResponse_2 +export type RetrieveAgent404 = ErrorResponse_2 +export const RetrieveAgent404 = ErrorResponse_2 +export type RetrieveAgent409 = ErrorResponse_2 +export const RetrieveAgent409 = ErrorResponse_2 +export type RetrieveAgent500 = ErrorResponse_2 +export const RetrieveAgent500 = ErrorResponse_2 +export type RetrieveAgent503 = ErrorResponse_2 +export const RetrieveAgent503 = ErrorResponse_2 +export type UpdateAgentRequestJson = UpdateAgentParams +export const UpdateAgentRequestJson = UpdateAgentParams +export type UpdateAgent200 = AgentResource +export const UpdateAgent200 = AgentResource +export type UpdateAgent400 = ErrorResponse_2 +export const UpdateAgent400 = ErrorResponse_2 +export type UpdateAgent401 = ErrorResponse_2 +export const UpdateAgent401 = ErrorResponse_2 +export type UpdateAgent403 = ErrorResponse_2 +export const UpdateAgent403 = ErrorResponse_2 +export type UpdateAgent404 = ErrorResponse_2 +export const UpdateAgent404 = ErrorResponse_2 +export type UpdateAgent409 = ErrorResponse_2 +export const UpdateAgent409 = ErrorResponse_2 +export type UpdateAgent500 = ErrorResponse_2 +export const UpdateAgent500 = ErrorResponse_2 +export type UpdateAgent503 = ErrorResponse_2 +export const UpdateAgent503 = ErrorResponse_2 +export type DeleteAgent200 = DeletedAgentResource +export const DeleteAgent200 = DeletedAgentResource +export type DeleteAgent400 = ErrorResponse_2 +export const DeleteAgent400 = ErrorResponse_2 +export type DeleteAgent401 = ErrorResponse_2 +export const DeleteAgent401 = ErrorResponse_2 +export type DeleteAgent403 = ErrorResponse_2 +export const DeleteAgent403 = ErrorResponse_2 +export type DeleteAgent404 = ErrorResponse_2 +export const DeleteAgent404 = ErrorResponse_2 +export type DeleteAgent409 = ErrorResponse_2 +export const DeleteAgent409 = ErrorResponse_2 +export type DeleteAgent500 = ErrorResponse_2 +export const DeleteAgent500 = ErrorResponse_2 +export type DeleteAgent503 = ErrorResponse_2 +export const DeleteAgent503 = ErrorResponse_2 +export type ListAgentEnvironmentTemplatesParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentEnvironmentTemplatesParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentEnvironmentTemplates200 = EnvironmentTemplateListResource +export const ListAgentEnvironmentTemplates200 = Schema.suspend((): Schema.Codec => + EnvironmentTemplateListResource +).annotate({ "description": "A page of reusable environment templates." }) +export type ListAgentEnvironmentTemplates400 = ErrorResponse_2 +export const ListAgentEnvironmentTemplates400 = ErrorResponse_2 +export type ListAgentEnvironmentTemplates401 = ErrorResponse_2 +export const ListAgentEnvironmentTemplates401 = ErrorResponse_2 +export type ListAgentEnvironmentTemplates403 = ErrorResponse_2 +export const ListAgentEnvironmentTemplates403 = ErrorResponse_2 +export type ListAgentEnvironmentTemplates404 = ErrorResponse_2 +export const ListAgentEnvironmentTemplates404 = ErrorResponse_2 +export type ListAgentEnvironmentTemplates500 = ErrorResponse_2 +export const ListAgentEnvironmentTemplates500 = ErrorResponse_2 +export type ListAgentEnvironmentTemplates503 = ErrorResponse_2 +export const ListAgentEnvironmentTemplates503 = ErrorResponse_2 +export type CreateAgentEnvironmentTemplateRequestJson = CreateEnvironmentTemplateParams +export const CreateAgentEnvironmentTemplateRequestJson = CreateEnvironmentTemplateParams +export type CreateAgentEnvironmentTemplate201 = EnvironmentTemplateResource +export const CreateAgentEnvironmentTemplate201 = EnvironmentTemplateResource +export type CreateAgentEnvironmentTemplate400 = ErrorResponse_2 +export const CreateAgentEnvironmentTemplate400 = ErrorResponse_2 +export type CreateAgentEnvironmentTemplate401 = ErrorResponse_2 +export const CreateAgentEnvironmentTemplate401 = ErrorResponse_2 +export type CreateAgentEnvironmentTemplate403 = ErrorResponse_2 +export const CreateAgentEnvironmentTemplate403 = ErrorResponse_2 +export type CreateAgentEnvironmentTemplate404 = ErrorResponse_2 +export const CreateAgentEnvironmentTemplate404 = ErrorResponse_2 +export type CreateAgentEnvironmentTemplate500 = ErrorResponse_2 +export const CreateAgentEnvironmentTemplate500 = ErrorResponse_2 +export type CreateAgentEnvironmentTemplate503 = ErrorResponse_2 +export const CreateAgentEnvironmentTemplate503 = ErrorResponse_2 +export type RetrieveAgentEnvironmentTemplate200 = EnvironmentTemplateResource +export const RetrieveAgentEnvironmentTemplate200 = EnvironmentTemplateResource +export type RetrieveAgentEnvironmentTemplate400 = ErrorResponse_2 +export const RetrieveAgentEnvironmentTemplate400 = ErrorResponse_2 +export type RetrieveAgentEnvironmentTemplate401 = ErrorResponse_2 +export const RetrieveAgentEnvironmentTemplate401 = ErrorResponse_2 +export type RetrieveAgentEnvironmentTemplate403 = ErrorResponse_2 +export const RetrieveAgentEnvironmentTemplate403 = ErrorResponse_2 +export type RetrieveAgentEnvironmentTemplate404 = ErrorResponse_2 +export const RetrieveAgentEnvironmentTemplate404 = ErrorResponse_2 +export type RetrieveAgentEnvironmentTemplate500 = ErrorResponse_2 +export const RetrieveAgentEnvironmentTemplate500 = ErrorResponse_2 +export type RetrieveAgentEnvironmentTemplate503 = ErrorResponse_2 +export const RetrieveAgentEnvironmentTemplate503 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplateRequestJson = UpdateEnvironmentTemplateParams +export const UpdateAgentEnvironmentTemplateRequestJson = UpdateEnvironmentTemplateParams +export type UpdateAgentEnvironmentTemplate200 = EnvironmentTemplateResource +export const UpdateAgentEnvironmentTemplate200 = EnvironmentTemplateResource +export type UpdateAgentEnvironmentTemplate400 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate400 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplate401 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate401 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplate403 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate403 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplate404 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate404 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplate409 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate409 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplate500 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate500 = ErrorResponse_2 +export type UpdateAgentEnvironmentTemplate503 = ErrorResponse_2 +export const UpdateAgentEnvironmentTemplate503 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate200 = DeletedEnvironmentTemplateResource +export const DeleteAgentEnvironmentTemplate200 = DeletedEnvironmentTemplateResource +export type DeleteAgentEnvironmentTemplate400 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate400 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate401 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate401 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate403 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate403 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate404 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate404 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate409 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate409 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate500 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate500 = ErrorResponse_2 +export type DeleteAgentEnvironmentTemplate503 = ErrorResponse_2 +export const DeleteAgentEnvironmentTemplate503 = ErrorResponse_2 +export type ListAgentSessionsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string + readonly "agent_id"?: string +} +export const ListAgentSessionsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "agent_id": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessions200 = SessionListResource +export const ListAgentSessions200 = Schema.suspend((): Schema.Codec => SessionListResource) + .annotate({ "description": "A paginated list of sessions." }) +export type ListAgentSessions400 = ErrorResponse_2 +export const ListAgentSessions400 = ErrorResponse_2 +export type ListAgentSessions401 = ErrorResponse_2 +export const ListAgentSessions401 = ErrorResponse_2 +export type ListAgentSessions403 = ErrorResponse_2 +export const ListAgentSessions403 = ErrorResponse_2 +export type ListAgentSessions404 = ErrorResponse_2 +export const ListAgentSessions404 = ErrorResponse_2 +export type ListAgentSessions409 = ErrorResponse_2 +export const ListAgentSessions409 = ErrorResponse_2 +export type ListAgentSessions500 = ErrorResponse_2 +export const ListAgentSessions500 = ErrorResponse_2 +export type ListAgentSessions503 = ErrorResponse_2 +export const ListAgentSessions503 = ErrorResponse_2 +export type CreateAgentSessionRequestJson = CreateAgentSessionParams +export const CreateAgentSessionRequestJson = CreateAgentSessionParams +export type CreateAgentSession201 = SessionResource +export const CreateAgentSession201 = SessionResource +export type CreateAgentSession201Sse = SessionEvent +export const CreateAgentSession201Sse = SessionEvent +export type CreateAgentSession400 = ErrorResponse_2 +export const CreateAgentSession400 = ErrorResponse_2 +export type CreateAgentSession401 = ErrorResponse_2 +export const CreateAgentSession401 = ErrorResponse_2 +export type CreateAgentSession403 = ErrorResponse_2 +export const CreateAgentSession403 = ErrorResponse_2 +export type CreateAgentSession404 = ErrorResponse_2 +export const CreateAgentSession404 = ErrorResponse_2 +export type CreateAgentSession409 = ErrorResponse_2 +export const CreateAgentSession409 = ErrorResponse_2 +export type CreateAgentSession429 = ErrorResponse_2 +export const CreateAgentSession429 = ErrorResponse_2 +export type CreateAgentSession500 = ErrorResponse_2 +export const CreateAgentSession500 = ErrorResponse_2 +export type CreateAgentSession503 = ErrorResponse_2 +export const CreateAgentSession503 = ErrorResponse_2 +export type RetrieveAgentSession200 = SessionResource +export const RetrieveAgentSession200 = SessionResource +export type RetrieveAgentSession400 = ErrorResponse_2 +export const RetrieveAgentSession400 = ErrorResponse_2 +export type RetrieveAgentSession401 = ErrorResponse_2 +export const RetrieveAgentSession401 = ErrorResponse_2 +export type RetrieveAgentSession403 = ErrorResponse_2 +export const RetrieveAgentSession403 = ErrorResponse_2 +export type RetrieveAgentSession404 = ErrorResponse_2 +export const RetrieveAgentSession404 = ErrorResponse_2 +export type RetrieveAgentSession409 = ErrorResponse_2 +export const RetrieveAgentSession409 = ErrorResponse_2 +export type RetrieveAgentSession500 = ErrorResponse_2 +export const RetrieveAgentSession500 = ErrorResponse_2 +export type RetrieveAgentSession503 = ErrorResponse_2 +export const RetrieveAgentSession503 = ErrorResponse_2 +export type UpdateAgentSessionRequestJson = UpdateAgentSessionParams +export const UpdateAgentSessionRequestJson = UpdateAgentSessionParams +export type UpdateAgentSession200 = SessionResource +export const UpdateAgentSession200 = SessionResource +export type UpdateAgentSession400 = ErrorResponse_2 +export const UpdateAgentSession400 = ErrorResponse_2 +export type UpdateAgentSession401 = ErrorResponse_2 +export const UpdateAgentSession401 = ErrorResponse_2 +export type UpdateAgentSession403 = ErrorResponse_2 +export const UpdateAgentSession403 = ErrorResponse_2 +export type UpdateAgentSession404 = ErrorResponse_2 +export const UpdateAgentSession404 = ErrorResponse_2 +export type UpdateAgentSession409 = ErrorResponse_2 +export const UpdateAgentSession409 = ErrorResponse_2 +export type UpdateAgentSession500 = ErrorResponse_2 +export const UpdateAgentSession500 = ErrorResponse_2 +export type UpdateAgentSession503 = ErrorResponse_2 +export const UpdateAgentSession503 = ErrorResponse_2 +export type DeleteAgentSession200 = DeletedSessionResource +export const DeleteAgentSession200 = DeletedSessionResource +export type DeleteAgentSession400 = ErrorResponse_2 +export const DeleteAgentSession400 = ErrorResponse_2 +export type DeleteAgentSession401 = ErrorResponse_2 +export const DeleteAgentSession401 = ErrorResponse_2 +export type DeleteAgentSession403 = ErrorResponse_2 +export const DeleteAgentSession403 = ErrorResponse_2 +export type DeleteAgentSession404 = ErrorResponse_2 +export const DeleteAgentSession404 = ErrorResponse_2 +export type DeleteAgentSession409 = ErrorResponse_2 +export const DeleteAgentSession409 = ErrorResponse_2 +export type DeleteAgentSession500 = ErrorResponse_2 +export const DeleteAgentSession500 = ErrorResponse_2 +export type DeleteAgentSession503 = ErrorResponse_2 +export const DeleteAgentSession503 = ErrorResponse_2 +export type ListAgentSessionArtifactsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string + readonly "environment_id"?: string | null +} +export const ListAgentSessionArtifactsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "environment_id": Schema.optionalKey( + Schema.Union([ + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })), + Schema.Null + ]) + ) +}) +export type ListAgentSessionArtifacts200 = SessionArtifactListResource +export const ListAgentSessionArtifacts200 = Schema.suspend((): Schema.Codec => + SessionArtifactListResource +).annotate({ "description": "A page of durable artifacts published by a session." }) +export type ListAgentSessionArtifacts400 = ErrorResponse_2 +export const ListAgentSessionArtifacts400 = ErrorResponse_2 +export type ListAgentSessionArtifacts401 = ErrorResponse_2 +export const ListAgentSessionArtifacts401 = ErrorResponse_2 +export type ListAgentSessionArtifacts403 = ErrorResponse_2 +export const ListAgentSessionArtifacts403 = ErrorResponse_2 +export type ListAgentSessionArtifacts404 = ErrorResponse_2 +export const ListAgentSessionArtifacts404 = ErrorResponse_2 +export type ListAgentSessionArtifacts409 = ErrorResponse_2 +export const ListAgentSessionArtifacts409 = ErrorResponse_2 +export type ListAgentSessionArtifacts500 = ErrorResponse_2 +export const ListAgentSessionArtifacts500 = ErrorResponse_2 +export type ListAgentSessionArtifacts503 = ErrorResponse_2 +export const ListAgentSessionArtifacts503 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact200 = SessionArtifactResource +export const RetrieveAgentSessionArtifact200 = SessionArtifactResource +export type RetrieveAgentSessionArtifact400 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact400 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact401 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact401 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact403 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact403 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact404 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact404 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact409 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact409 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact500 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact500 = ErrorResponse_2 +export type RetrieveAgentSessionArtifact503 = ErrorResponse_2 +export const RetrieveAgentSessionArtifact503 = ErrorResponse_2 +export type DeleteAgentSessionArtifact200 = DeletedSessionArtifactResource +export const DeleteAgentSessionArtifact200 = DeletedSessionArtifactResource +export type DeleteAgentSessionArtifact400 = ErrorResponse_2 +export const DeleteAgentSessionArtifact400 = ErrorResponse_2 +export type DeleteAgentSessionArtifact401 = ErrorResponse_2 +export const DeleteAgentSessionArtifact401 = ErrorResponse_2 +export type DeleteAgentSessionArtifact403 = ErrorResponse_2 +export const DeleteAgentSessionArtifact403 = ErrorResponse_2 +export type DeleteAgentSessionArtifact404 = ErrorResponse_2 +export const DeleteAgentSessionArtifact404 = ErrorResponse_2 +export type DeleteAgentSessionArtifact409 = ErrorResponse_2 +export const DeleteAgentSessionArtifact409 = ErrorResponse_2 +export type DeleteAgentSessionArtifact500 = ErrorResponse_2 +export const DeleteAgentSessionArtifact500 = ErrorResponse_2 +export type DeleteAgentSessionArtifact503 = ErrorResponse_2 +export const DeleteAgentSessionArtifact503 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent400 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent400 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent401 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent401 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent403 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent403 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent404 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent404 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent409 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent409 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent500 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent500 = ErrorResponse_2 +export type RetrieveAgentSessionArtifactContent503 = ErrorResponse_2 +export const RetrieveAgentSessionArtifactContent503 = ErrorResponse_2 +export type ListAgentSessionEvents200Sse = SessionEvent +export const ListAgentSessionEvents200Sse = SessionEvent +export type ListAgentSessionEvents400 = ErrorResponse_2 +export const ListAgentSessionEvents400 = ErrorResponse_2 +export type ListAgentSessionEvents401 = ErrorResponse_2 +export const ListAgentSessionEvents401 = ErrorResponse_2 +export type ListAgentSessionEvents403 = ErrorResponse_2 +export const ListAgentSessionEvents403 = ErrorResponse_2 +export type ListAgentSessionEvents404 = ErrorResponse_2 +export const ListAgentSessionEvents404 = ErrorResponse_2 +export type ListAgentSessionEvents409 = ErrorResponse_2 +export const ListAgentSessionEvents409 = ErrorResponse_2 +export type ListAgentSessionEvents500 = ErrorResponse_2 +export const ListAgentSessionEvents500 = ErrorResponse_2 +export type ListAgentSessionEvents503 = ErrorResponse_2 +export const ListAgentSessionEvents503 = ErrorResponse_2 +export type CreateAgentSessionEventsParams = { readonly "Idempotency-Key"?: string } +export const CreateAgentSessionEventsParams = Schema.Struct({ + "Idempotency-Key": Schema.optionalKey( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })).check( + Schema.isMaxCodePoints(256).annotate({ "expected": "a string with at most 256 code points" }) + ) + ) +}) +export type CreateAgentSessionEventsRequestJson = CreateSessionEventsParams +export const CreateAgentSessionEventsRequestJson = CreateSessionEventsParams +export type CreateAgentSessionEvents400 = ErrorResponse_2 +export const CreateAgentSessionEvents400 = ErrorResponse_2 +export type CreateAgentSessionEvents401 = ErrorResponse_2 +export const CreateAgentSessionEvents401 = ErrorResponse_2 +export type CreateAgentSessionEvents403 = ErrorResponse_2 +export const CreateAgentSessionEvents403 = ErrorResponse_2 +export type CreateAgentSessionEvents404 = ErrorResponse_2 +export const CreateAgentSessionEvents404 = ErrorResponse_2 +export type CreateAgentSessionEvents409 = ErrorResponse_2 +export const CreateAgentSessionEvents409 = ErrorResponse_2 +export type CreateAgentSessionEvents500 = ErrorResponse_2 +export const CreateAgentSessionEvents500 = ErrorResponse_2 +export type CreateAgentSessionEvents503 = ErrorResponse_2 +export const CreateAgentSessionEvents503 = ErrorResponse_2 +export type ListAgentSessionItemsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionItemsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionItems200 = SessionItemListResource +export const ListAgentSessionItems200 = SessionItemListResource +export type ListAgentSessionItems400 = ErrorResponse_2 +export const ListAgentSessionItems400 = ErrorResponse_2 +export type ListAgentSessionItems401 = ErrorResponse_2 +export const ListAgentSessionItems401 = ErrorResponse_2 +export type ListAgentSessionItems403 = ErrorResponse_2 +export const ListAgentSessionItems403 = ErrorResponse_2 +export type ListAgentSessionItems404 = ErrorResponse_2 +export const ListAgentSessionItems404 = ErrorResponse_2 +export type ListAgentSessionItems409 = ErrorResponse_2 +export const ListAgentSessionItems409 = ErrorResponse_2 +export type ListAgentSessionItems500 = ErrorResponse_2 +export const ListAgentSessionItems500 = ErrorResponse_2 +export type ListAgentSessionItems503 = ErrorResponse_2 +export const ListAgentSessionItems503 = ErrorResponse_2 +export type ListAgentSessionTracesParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionTracesParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionTraces200 = SessionTraceListResource +export const ListAgentSessionTraces200 = Schema.suspend((): Schema.Codec => + SessionTraceListResource +).annotate({ "description": "A page of published session traces." }) +export type ListAgentSessionTraces400 = ErrorResponse_2 +export const ListAgentSessionTraces400 = ErrorResponse_2 +export type ListAgentSessionTraces401 = ErrorResponse_2 +export const ListAgentSessionTraces401 = ErrorResponse_2 +export type ListAgentSessionTraces403 = ErrorResponse_2 +export const ListAgentSessionTraces403 = ErrorResponse_2 +export type ListAgentSessionTraces404 = ErrorResponse_2 +export const ListAgentSessionTraces404 = ErrorResponse_2 +export type ListAgentSessionTraces409 = ErrorResponse_2 +export const ListAgentSessionTraces409 = ErrorResponse_2 +export type ListAgentSessionTraces500 = ErrorResponse_2 +export const ListAgentSessionTraces500 = ErrorResponse_2 +export type ListAgentSessionTraces503 = ErrorResponse_2 +export const ListAgentSessionTraces503 = ErrorResponse_2 +export type ListAgentSessionTurnsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionTurnsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionTurns200 = SessionTurnListResource +export const ListAgentSessionTurns200 = Schema.suspend((): Schema.Codec => + SessionTurnListResource +).annotate({ "description": "A page of turns from an agent session or subagent." }) +export type ListAgentSessionTurns400 = ErrorResponse_2 +export const ListAgentSessionTurns400 = ErrorResponse_2 +export type ListAgentSessionTurns401 = ErrorResponse_2 +export const ListAgentSessionTurns401 = ErrorResponse_2 +export type ListAgentSessionTurns403 = ErrorResponse_2 +export const ListAgentSessionTurns403 = ErrorResponse_2 +export type ListAgentSessionTurns404 = ErrorResponse_2 +export const ListAgentSessionTurns404 = ErrorResponse_2 +export type ListAgentSessionTurns409 = ErrorResponse_2 +export const ListAgentSessionTurns409 = ErrorResponse_2 +export type ListAgentSessionTurns500 = ErrorResponse_2 +export const ListAgentSessionTurns500 = ErrorResponse_2 +export type ListAgentSessionTurns503 = ErrorResponse_2 +export const ListAgentSessionTurns503 = ErrorResponse_2 +export type RetrieveAgentSessionTurn200 = TurnResource +export const RetrieveAgentSessionTurn200 = TurnResource +export type RetrieveAgentSessionTurn400 = ErrorResponse_2 +export const RetrieveAgentSessionTurn400 = ErrorResponse_2 +export type RetrieveAgentSessionTurn401 = ErrorResponse_2 +export const RetrieveAgentSessionTurn401 = ErrorResponse_2 +export type RetrieveAgentSessionTurn403 = ErrorResponse_2 +export const RetrieveAgentSessionTurn403 = ErrorResponse_2 +export type RetrieveAgentSessionTurn404 = ErrorResponse_2 +export const RetrieveAgentSessionTurn404 = ErrorResponse_2 +export type RetrieveAgentSessionTurn409 = ErrorResponse_2 +export const RetrieveAgentSessionTurn409 = ErrorResponse_2 +export type RetrieveAgentSessionTurn500 = ErrorResponse_2 +export const RetrieveAgentSessionTurn500 = ErrorResponse_2 +export type RetrieveAgentSessionTurn503 = ErrorResponse_2 +export const RetrieveAgentSessionTurn503 = ErrorResponse_2 +export type ListAgentSessionTurnItemsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string +} +export const ListAgentSessionTurnItemsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ) +}) +export type ListAgentSessionTurnItems200 = SessionItemListResource +export const ListAgentSessionTurnItems200 = SessionItemListResource +export type ListAgentSessionTurnItems400 = ErrorResponse_2 +export const ListAgentSessionTurnItems400 = ErrorResponse_2 +export type ListAgentSessionTurnItems401 = ErrorResponse_2 +export const ListAgentSessionTurnItems401 = ErrorResponse_2 +export type ListAgentSessionTurnItems403 = ErrorResponse_2 +export const ListAgentSessionTurnItems403 = ErrorResponse_2 +export type ListAgentSessionTurnItems404 = ErrorResponse_2 +export const ListAgentSessionTurnItems404 = ErrorResponse_2 +export type ListAgentSessionTurnItems409 = ErrorResponse_2 +export const ListAgentSessionTurnItems409 = ErrorResponse_2 +export type ListAgentSessionTurnItems500 = ErrorResponse_2 +export const ListAgentSessionTurnItems500 = ErrorResponse_2 +export type ListAgentSessionTurnItems503 = ErrorResponse_2 +export const ListAgentSessionTurnItems503 = ErrorResponse_2 +export type ListVaultsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string + readonly "metadata"?: { readonly [x: string]: string } + readonly "status"?: VaultStatusFilterParam +} +export const ListVaultsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "metadata": Schema.optionalKey( + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) + ), + "status": Schema.optionalKey(VaultStatusFilterParam) +}) +export type ListVaults200 = VaultListResource +export const ListVaults200 = Schema.suspend((): Schema.Codec => VaultListResource).annotate({ + "description": "A page of vaults." +}) +export type ListVaults400 = ErrorResponse_2 +export const ListVaults400 = ErrorResponse_2 +export type ListVaults401 = ErrorResponse_2 +export const ListVaults401 = ErrorResponse_2 +export type ListVaults403 = ErrorResponse_2 +export const ListVaults403 = ErrorResponse_2 +export type ListVaults404 = ErrorResponse_2 +export const ListVaults404 = ErrorResponse_2 +export type ListVaults409 = ErrorResponse_2 +export const ListVaults409 = ErrorResponse_2 +export type ListVaults500 = ErrorResponse_2 +export const ListVaults500 = ErrorResponse_2 +export type ListVaults503 = ErrorResponse_2 +export const ListVaults503 = ErrorResponse_2 +export type CreateVaultRequestJson = CreateVaultParams +export const CreateVaultRequestJson = CreateVaultParams +export type CreateVault201 = VaultResource +export const CreateVault201 = VaultResource +export type CreateVault400 = ErrorResponse_2 +export const CreateVault400 = ErrorResponse_2 +export type CreateVault401 = ErrorResponse_2 +export const CreateVault401 = ErrorResponse_2 +export type CreateVault403 = ErrorResponse_2 +export const CreateVault403 = ErrorResponse_2 +export type CreateVault404 = ErrorResponse_2 +export const CreateVault404 = ErrorResponse_2 +export type CreateVault409 = ErrorResponse_2 +export const CreateVault409 = ErrorResponse_2 +export type CreateVault500 = ErrorResponse_2 +export const CreateVault500 = ErrorResponse_2 +export type CreateVault503 = ErrorResponse_2 +export const CreateVault503 = ErrorResponse_2 +export type RetrieveVault200 = VaultResource +export const RetrieveVault200 = VaultResource +export type RetrieveVault400 = ErrorResponse_2 +export const RetrieveVault400 = ErrorResponse_2 +export type RetrieveVault401 = ErrorResponse_2 +export const RetrieveVault401 = ErrorResponse_2 +export type RetrieveVault403 = ErrorResponse_2 +export const RetrieveVault403 = ErrorResponse_2 +export type RetrieveVault404 = ErrorResponse_2 +export const RetrieveVault404 = ErrorResponse_2 +export type RetrieveVault409 = ErrorResponse_2 +export const RetrieveVault409 = ErrorResponse_2 +export type RetrieveVault500 = ErrorResponse_2 +export const RetrieveVault500 = ErrorResponse_2 +export type RetrieveVault503 = ErrorResponse_2 +export const RetrieveVault503 = ErrorResponse_2 +export type UpdateVaultRequestJson = UpdateVaultParams +export const UpdateVaultRequestJson = UpdateVaultParams +export type UpdateVault200 = VaultResource +export const UpdateVault200 = VaultResource +export type UpdateVault400 = ErrorResponse_2 +export const UpdateVault400 = ErrorResponse_2 +export type UpdateVault401 = ErrorResponse_2 +export const UpdateVault401 = ErrorResponse_2 +export type UpdateVault403 = ErrorResponse_2 +export const UpdateVault403 = ErrorResponse_2 +export type UpdateVault404 = ErrorResponse_2 +export const UpdateVault404 = ErrorResponse_2 +export type UpdateVault409 = ErrorResponse_2 +export const UpdateVault409 = ErrorResponse_2 +export type UpdateVault500 = ErrorResponse_2 +export const UpdateVault500 = ErrorResponse_2 +export type UpdateVault503 = ErrorResponse_2 +export const UpdateVault503 = ErrorResponse_2 +export type DeleteVault200 = DeletedVaultResource +export const DeleteVault200 = DeletedVaultResource +export type DeleteVault400 = ErrorResponse_2 +export const DeleteVault400 = ErrorResponse_2 +export type DeleteVault401 = ErrorResponse_2 +export const DeleteVault401 = ErrorResponse_2 +export type DeleteVault403 = ErrorResponse_2 +export const DeleteVault403 = ErrorResponse_2 +export type DeleteVault404 = ErrorResponse_2 +export const DeleteVault404 = ErrorResponse_2 +export type DeleteVault409 = ErrorResponse_2 +export const DeleteVault409 = ErrorResponse_2 +export type DeleteVault500 = ErrorResponse_2 +export const DeleteVault500 = ErrorResponse_2 +export type DeleteVault503 = ErrorResponse_2 +export const DeleteVault503 = ErrorResponse_2 +export type ListVaultCredentialsParams = { + readonly "limit"?: number + readonly "order"?: ListOrderParam + readonly "after"?: string + readonly "metadata"?: { readonly [x: string]: string } + readonly "status"?: VaultStatusFilterParam +} +export const ListVaultCredentialsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "order": Schema.optionalKey(ListOrderParam), + "after": Schema.optionalKey( + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(1048576).annotate({ "expected": "a string with at most 1048576 code points" })) + ), + "metadata": Schema.optionalKey( + Schema.Record( + Schema.String, + Schema.String.check(Schema.isMinCodePoints(0).annotate({ "expected": "a string with at least 0 code points" })) + .check(Schema.isMaxCodePoints(512).annotate({ "expected": "a string with at most 512 code points" })) + ).check(Schema.isMinProperties(0).annotate({ "expected": "a value with at least 0 entries" })).check( + Schema.isMaxProperties(16).annotate({ "expected": "a value with at most 16 entries" }) + ).check( + Schema.isPropertyNames( + Schema.String.check(Schema.isMinLength(1).annotate({ "expected": "a value with a length of at least 1" })) + .check(Schema.isMaxCodePoints(64).annotate({ "expected": "a string with at most 64 code points" })) + ).annotate({ "expected": "an object with property names matching the schema" }) + ) + ), + "status": Schema.optionalKey(VaultStatusFilterParam) +}) +export type ListVaultCredentials200 = VaultCredentialListResource +export const ListVaultCredentials200 = Schema.suspend((): Schema.Codec => + VaultCredentialListResource +).annotate({ "description": "A page of credentials in a vault." }) +export type ListVaultCredentials400 = ErrorResponse_2 +export const ListVaultCredentials400 = ErrorResponse_2 +export type ListVaultCredentials401 = ErrorResponse_2 +export const ListVaultCredentials401 = ErrorResponse_2 +export type ListVaultCredentials403 = ErrorResponse_2 +export const ListVaultCredentials403 = ErrorResponse_2 +export type ListVaultCredentials404 = ErrorResponse_2 +export const ListVaultCredentials404 = ErrorResponse_2 +export type ListVaultCredentials409 = ErrorResponse_2 +export const ListVaultCredentials409 = ErrorResponse_2 +export type ListVaultCredentials500 = ErrorResponse_2 +export const ListVaultCredentials500 = ErrorResponse_2 +export type ListVaultCredentials503 = ErrorResponse_2 +export const ListVaultCredentials503 = ErrorResponse_2 +export type CreateVaultCredentialRequestJson = CreateVaultCredentialParams +export const CreateVaultCredentialRequestJson = CreateVaultCredentialParams +export type CreateVaultCredential201 = VaultCredentialResource +export const CreateVaultCredential201 = VaultCredentialResource +export type CreateVaultCredential400 = ErrorResponse_2 +export const CreateVaultCredential400 = ErrorResponse_2 +export type CreateVaultCredential401 = ErrorResponse_2 +export const CreateVaultCredential401 = ErrorResponse_2 +export type CreateVaultCredential403 = ErrorResponse_2 +export const CreateVaultCredential403 = ErrorResponse_2 +export type CreateVaultCredential404 = ErrorResponse_2 +export const CreateVaultCredential404 = ErrorResponse_2 +export type CreateVaultCredential409 = ErrorResponse_2 +export const CreateVaultCredential409 = ErrorResponse_2 +export type CreateVaultCredential500 = ErrorResponse_2 +export const CreateVaultCredential500 = ErrorResponse_2 +export type CreateVaultCredential503 = ErrorResponse_2 +export const CreateVaultCredential503 = ErrorResponse_2 +export type RetrieveVaultCredential200 = VaultCredentialResource +export const RetrieveVaultCredential200 = VaultCredentialResource +export type RetrieveVaultCredential400 = ErrorResponse_2 +export const RetrieveVaultCredential400 = ErrorResponse_2 +export type RetrieveVaultCredential401 = ErrorResponse_2 +export const RetrieveVaultCredential401 = ErrorResponse_2 +export type RetrieveVaultCredential403 = ErrorResponse_2 +export const RetrieveVaultCredential403 = ErrorResponse_2 +export type RetrieveVaultCredential404 = ErrorResponse_2 +export const RetrieveVaultCredential404 = ErrorResponse_2 +export type RetrieveVaultCredential409 = ErrorResponse_2 +export const RetrieveVaultCredential409 = ErrorResponse_2 +export type RetrieveVaultCredential500 = ErrorResponse_2 +export const RetrieveVaultCredential500 = ErrorResponse_2 +export type RetrieveVaultCredential503 = ErrorResponse_2 +export const RetrieveVaultCredential503 = ErrorResponse_2 +export type RotateVaultCredentialRequestJson = RotateVaultCredentialParams +export const RotateVaultCredentialRequestJson = RotateVaultCredentialParams +export type RotateVaultCredential200 = VaultCredentialResource +export const RotateVaultCredential200 = VaultCredentialResource +export type RotateVaultCredential400 = ErrorResponse_2 +export const RotateVaultCredential400 = ErrorResponse_2 +export type RotateVaultCredential401 = ErrorResponse_2 +export const RotateVaultCredential401 = ErrorResponse_2 +export type RotateVaultCredential403 = ErrorResponse_2 +export const RotateVaultCredential403 = ErrorResponse_2 +export type RotateVaultCredential404 = ErrorResponse_2 +export const RotateVaultCredential404 = ErrorResponse_2 +export type RotateVaultCredential409 = ErrorResponse_2 +export const RotateVaultCredential409 = ErrorResponse_2 +export type RotateVaultCredential500 = ErrorResponse_2 +export const RotateVaultCredential500 = ErrorResponse_2 +export type RotateVaultCredential503 = ErrorResponse_2 +export const RotateVaultCredential503 = ErrorResponse_2 +export type DeleteVaultCredential200 = DeletedVaultCredentialResource +export const DeleteVaultCredential200 = DeletedVaultCredentialResource +export type DeleteVaultCredential400 = ErrorResponse_2 +export const DeleteVaultCredential400 = ErrorResponse_2 +export type DeleteVaultCredential401 = ErrorResponse_2 +export const DeleteVaultCredential401 = ErrorResponse_2 +export type DeleteVaultCredential403 = ErrorResponse_2 +export const DeleteVaultCredential403 = ErrorResponse_2 +export type DeleteVaultCredential404 = ErrorResponse_2 +export const DeleteVaultCredential404 = ErrorResponse_2 +export type DeleteVaultCredential409 = ErrorResponse_2 +export const DeleteVaultCredential409 = ErrorResponse_2 +export type DeleteVaultCredential500 = ErrorResponse_2 +export const DeleteVaultCredential500 = ErrorResponse_2 +export type DeleteVaultCredential503 = ErrorResponse_2 +export const DeleteVaultCredential503 = ErrorResponse_2 +export type ListWebhookEndpointsParams = { readonly "limit"?: number; readonly "after"?: string | null } +export const ListWebhookEndpointsParams = Schema.Struct({ + "limit": Schema.optionalKey( + Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" })).check( + Schema.isGreaterThanOrEqualTo(1).annotate({ "expected": "a value greater than or equal to 1" }) + ).check(Schema.isLessThanOrEqualTo(100).annotate({ "expected": "a value less than or equal to 100" })) + ), + "after": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) +}) +export type ListWebhookEndpoints200 = WebhookEndpointListResource +export const ListWebhookEndpoints200 = WebhookEndpointListResource +export type ListWebhookEndpoints400 = ErrorResponse +export const ListWebhookEndpoints400 = ErrorResponse +export type ListWebhookEndpoints404 = ErrorResponse +export const ListWebhookEndpoints404 = ErrorResponse +export type CreateWebhookEndpointRequestJson = PublicCreateEndpointBody +export const CreateWebhookEndpointRequestJson = PublicCreateEndpointBody +export type CreateWebhookEndpoint200 = WebhookEndpointWithSecretResource +export const CreateWebhookEndpoint200 = WebhookEndpointWithSecretResource +export type CreateWebhookEndpoint400 = ErrorResponse +export const CreateWebhookEndpoint400 = ErrorResponse +export type RetrieveWebhookEndpoint200 = WebhookEndpointBody +export const RetrieveWebhookEndpoint200 = WebhookEndpointBody +export type RetrieveWebhookEndpoint400 = ErrorResponse +export const RetrieveWebhookEndpoint400 = ErrorResponse +export type RetrieveWebhookEndpoint404 = ErrorResponse +export const RetrieveWebhookEndpoint404 = ErrorResponse +export type UpdateWebhookEndpointRequestJson = PublicUpdateEndpointBody +export const UpdateWebhookEndpointRequestJson = PublicUpdateEndpointBody +export type UpdateWebhookEndpoint200 = WebhookEndpointBody +export const UpdateWebhookEndpoint200 = WebhookEndpointBody +export type UpdateWebhookEndpoint400 = ErrorResponse +export const UpdateWebhookEndpoint400 = ErrorResponse +export type UpdateWebhookEndpoint404 = ErrorResponse +export const UpdateWebhookEndpoint404 = ErrorResponse +export type DeleteWebhookEndpoint200 = DeletedWebhookEndpointResource +export const DeleteWebhookEndpoint200 = DeletedWebhookEndpointResource +export type DeleteWebhookEndpoint400 = ErrorResponse +export const DeleteWebhookEndpoint400 = ErrorResponse +export type DeleteWebhookEndpoint404 = ErrorResponse +export const DeleteWebhookEndpoint404 = ErrorResponse +export type RotateWebhookEndpointSigningSecretRequestJson = PublicRotateSecretBody +export const RotateWebhookEndpointSigningSecretRequestJson = PublicRotateSecretBody +export type RotateWebhookEndpointSigningSecret200 = WebhookEndpointWithSecretResource +export const RotateWebhookEndpointSigningSecret200 = WebhookEndpointWithSecretResource +export type RotateWebhookEndpointSigningSecret400 = ErrorResponse +export const RotateWebhookEndpointSigningSecret400 = ErrorResponse +export type RotateWebhookEndpointSigningSecret404 = ErrorResponse +export const RotateWebhookEndpointSigningSecret404 = ErrorResponse +export type TestWebhookEndpointRequestJson = PublicTestEndpointBody +export const TestWebhookEndpointRequestJson = PublicTestEndpointBody +export type TestWebhookEndpoint200 = WebhookEndpointTestResultResource +export const TestWebhookEndpoint200 = WebhookEndpointTestResultResource +export type TestWebhookEndpoint400 = ErrorResponse +export const TestWebhookEndpoint400 = ErrorResponse +export type TestWebhookEndpoint404 = ErrorResponse +export const TestWebhookEndpoint404 = ErrorResponse +export type TestWebhookEndpoint500 = ErrorResponse +export const TestWebhookEndpoint500 = ErrorResponse +export type ListWebhookEventTypes200 = WebhookEventTypeListResource +export const ListWebhookEventTypes200 = WebhookEventTypeListResource +export type ListWebhookEventTypes401 = ErrorResponse +export const ListWebhookEventTypes401 = ErrorResponse +export type BetaCreateResponseParams = { readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> } +export const BetaCreateResponseParams = Schema.Struct({ + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaCreateResponseRequestJson = BetaCreateResponse +export const BetaCreateResponseRequestJson = BetaCreateResponse +export type BetaCreateResponse200 = BetaResponse +export const BetaCreateResponse200 = BetaResponse +export type BetaCreateResponse200Sse = BetaResponseStreamEvent +export const BetaCreateResponse200Sse = BetaResponseStreamEvent +export type BetaCreateResponse400 = BetaErrorResponse +export const BetaCreateResponse400 = BetaErrorResponse +export type BetaCreateResponse404 = BetaErrorResponse +export const BetaCreateResponse404 = BetaErrorResponse +export type BetaCreateResponse429 = ErrorResponse +export const BetaCreateResponse429 = ErrorResponse +export type BetaCreateResponse503 = ErrorResponse +export const BetaCreateResponse503 = ErrorResponse +export type BetaGetResponseParams = { + readonly "include"?: ReadonlyArray + readonly "stream"?: boolean + readonly "starting_after"?: number + readonly "include_obfuscation"?: boolean + readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> +} +export const BetaGetResponseParams = Schema.Struct({ + "include": Schema.optionalKey(Schema.Array(BetaIncludeEnum)), + "stream": Schema.optionalKey(Schema.Boolean), + "starting_after": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "include_obfuscation": Schema.optionalKey(Schema.Boolean), + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaGetResponse200 = BetaResponse +export const BetaGetResponse200 = BetaResponse +export type BetaGetResponse404 = BetaErrorResponse +export const BetaGetResponse404 = BetaErrorResponse +export type BetaGetResponse429 = ErrorResponse +export const BetaGetResponse429 = ErrorResponse +export type BetaDeleteResponseParams = { readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> } +export const BetaDeleteResponseParams = Schema.Struct({ + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaDeleteResponse200 = { + readonly "object": "response.deleted" + readonly "deleted": boolean + readonly "id": string +} +export const BetaDeleteResponse200 = Schema.Struct({ + "object": Schema.Literal("response.deleted"), + "deleted": Schema.Boolean, + "id": Schema.String +}) +export type BetaDeleteResponse404 = BetaErrorResponse +export const BetaDeleteResponse404 = BetaErrorResponse +export type BetaDeleteResponse429 = ErrorResponse +export const BetaDeleteResponse429 = ErrorResponse +export type BetaCancelResponseParams = { readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> } +export const BetaCancelResponseParams = Schema.Struct({ + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaCancelResponse200 = BetaResponse +export const BetaCancelResponse200 = BetaResponse +export type BetaCancelResponse404 = BetaErrorResponse +export const BetaCancelResponse404 = BetaErrorResponse +export type BetaCancelResponse429 = ErrorResponse +export const BetaCancelResponse429 = ErrorResponse +export type BetaCompactconversationParams = { readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> } +export const BetaCompactconversationParams = Schema.Struct({ + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaCompactconversationRequestJson = BetaCompactResponseMethodPublicBody +export const BetaCompactconversationRequestJson = BetaCompactResponseMethodPublicBody +export type BetaCompactconversation200 = BetaCompactResource +export const BetaCompactconversation200 = BetaCompactResource +export type BetaCompactconversation400 = BetaErrorResponse +export const BetaCompactconversation400 = BetaErrorResponse +export type BetaCompactconversation429 = ErrorResponse +export const BetaCompactconversation429 = ErrorResponse +export type BetaCompactconversation503 = ErrorResponse +export const BetaCompactconversation503 = ErrorResponse +export type BetaListInputItemsParams = { + readonly "limit"?: number + readonly "order"?: "asc" | "desc" + readonly "after"?: string + readonly "include"?: ReadonlyArray + readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> +} +export const BetaListInputItemsParams = Schema.Struct({ + "limit": Schema.optionalKey(Schema.Number.check(Schema.isInt().annotate({ "expected": "an integer" }))), + "order": Schema.optionalKey(Schema.Literals(["asc", "desc"])), + "after": Schema.optionalKey(Schema.String), + "include": Schema.optionalKey(Schema.Array(BetaIncludeEnum)), + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaListInputItems200 = BetaResponseItemList +export const BetaListInputItems200 = BetaResponseItemList +export type BetaListInputItems404 = BetaErrorResponse +export const BetaListInputItems404 = BetaErrorResponse +export type BetaListInputItems429 = ErrorResponse +export const BetaListInputItems429 = ErrorResponse +export type BetaGetinputtokencountsParams = { readonly "openai-beta"?: ReadonlyArray<"responses_multi_agent=v1"> } +export const BetaGetinputtokencountsParams = Schema.Struct({ + "openai-beta": Schema.optionalKey(Schema.Array(Schema.Literal("responses_multi_agent=v1"))) +}) +export type BetaGetinputtokencountsRequestJson = BetaTokenCountsBody +export const BetaGetinputtokencountsRequestJson = BetaTokenCountsBody +export type BetaGetinputtokencounts200 = BetaTokenCountsResource +export const BetaGetinputtokencounts200 = BetaTokenCountsResource +export type BetaGetinputtokencounts400 = BetaErrorResponse +export const BetaGetinputtokencounts400 = BetaErrorResponse +export type BetaGetinputtokencounts429 = ErrorResponse +export const BetaGetinputtokencounts429 = ErrorResponse + +export interface OperationConfig { + /** + * Whether or not the response should be included in the value returned from + * an operation. + * + * If set to `true`, a tuple of `[A, HttpClientResponse]` will be returned, + * where `A` is the success type of the operation. + * + * If set to `false`, only the success type of the operation will be returned. + */ + readonly includeResponse?: boolean | undefined +} + +/** + * A utility type which optionally includes the response in the return result + * of an operation based upon the value of the `includeResponse` configuration + * option. + */ +export type WithOptionalResponse = Config extends { + readonly includeResponse: true +} ? [A, HttpClientResponse.HttpClientResponse] + : Config extends { readonly includeResponse?: false | undefined } | undefined ? A + : A | [A, HttpClientResponse.HttpClientResponse] + +export const make = ( + httpClient: HttpClient.HttpClient, + options: { + readonly transformClient?: ((client: HttpClient.HttpClient) => Effect.Effect) | undefined + } = {} +): OpenAiClient => { + const unexpectedStatus = (response: HttpClientResponse.HttpClientResponse) => + Effect.flatMap( + Effect.orElseSucceed(response.json, () => "Unexpected status code"), + (description) => + Effect.fail( + new HttpClientError.HttpClientError({ + reason: new HttpClientError.StatusCodeError({ + request: response.request, + response, + description: typeof description === "string" ? description : JSON.stringify(description) + }) + }) + ) + ) + const withResponse = (config: Config | undefined) => + ( + f: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect + ): (request: HttpClientRequest.HttpClientRequest) => Effect.Effect => { + const withOptionalResponse = ( + config?.includeResponse + ? (response: HttpClientResponse.HttpClientResponse) => Effect.map(f(response), (a) => [a, response]) + : (response: HttpClientResponse.HttpClientResponse) => f(response) + ) as any + return options?.transformClient + ? (request) => + Effect.flatMap( + Effect.flatMap(options.transformClient!(httpClient), (client) => client.execute(request)), + withOptionalResponse + ) + : (request) => Effect.flatMap(httpClient.execute(request), withOptionalResponse) + } + const __encodePathParam = encodeURIComponent + const __makePathRequest = ( + method: (url: string) => HttpClientRequest.HttpClientRequest, + parameters: ReadonlyArray, + getPath: () => string + ) => + Effect.suspend(() => { + const fail = (description: string, cause?: unknown) => + Effect.fail( + new HttpClientError.HttpClientError({ + reason: new HttpClientError.InvalidUrlError({ + request: method(""), + cause, + description + }) + }) + ) + if (parameters.some((value) => value === "" || /^(?:\.|%2e){1,2}$/i.test(value))) { + return fail("Path parameters must be non-empty and cannot be dot segments") + } + let path: string + try { + path = getPath() + } catch (cause) { + return fail("Failed to encode path parameter", cause) + } + if (path.split("/").some((segment) => /^(?:\.|%2e){1,2}$/i.test(segment))) { + return fail("Request paths cannot contain dot segments") + } + return Effect.succeed(method(path)) + }) + const executeStreamRequest = (request: HttpClientRequest.HttpClientRequest) => + Effect.suspend(() => + options.transformClient + ? Effect.flatMap(options.transformClient(httpClient), (client) => + HttpClient.filterStatusOk(client).execute(request)) + : HttpClient.filterStatusOk(httpClient).execute(request) + ) + const sseRequest = < + Type, + DecodingServices + >( + schema: Schema.ConstraintDecoder + ) => + ( + request: HttpClientRequest.HttpClientRequest + ): Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: Type }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + DecodingServices + > => + executeStreamRequest(request).pipe( + Effect.map((response) => response.stream), + Stream.unwrap, + Stream.decodeText(), + Stream.pipeThroughChannel(Sse.decodeDataSchema(schema)) + ) + const decodeBinary = (response: HttpClientResponse.HttpClientResponse) => + Effect.map(response.arrayBuffer, (buffer) => new Uint8Array(buffer)) + const binaryRequest = ( + request: HttpClientRequest.HttpClientRequest + ): Stream.Stream => + executeStreamRequest(request).pipe( + Effect.map((response) => response.stream), + Stream.unwrap + ) + const decodeSuccess = + (schema: Schema) => (response: HttpClientResponse.HttpClientResponse) => + HttpClientResponse.schemaBodyJson(schema)(response) + const decodeError = + (tag: Tag, schema: Schema) => + (response: HttpClientResponse.HttpClientResponse) => + Effect.flatMap( + HttpClientResponse.schemaBodyJson(schema)(response), + (cause) => Effect.fail(OpenAiClientError(tag, cause, response)) + ) + return { + httpClient, + "listAssistants": (options: Parameters[0]) => + HttpClientRequest.get("/assistants").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAssistants200), + "429": decodeError("ListAssistants429", ListAssistants429), + orElse: unexpectedStatus + })) + ), + "createAssistant": (options: Parameters[0]) => + HttpClientRequest.post("/assistants").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateAssistant200), + "429": decodeError("CreateAssistant429", CreateAssistant429), + orElse: unexpectedStatus + })) + ), + "getAssistant": (assistantId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [assistantId], + () => "/assistants/" + __encodePathParam(assistantId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetAssistant200), + "429": decodeError("GetAssistant429", GetAssistant429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyAssistant": (assistantId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [assistantId], + () => "/assistants/" + __encodePathParam(assistantId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyAssistant200), + "429": decodeError("ModifyAssistant429", ModifyAssistant429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteAssistant": (assistantId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [assistantId], + () => "/assistants/" + __encodePathParam(assistantId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteAssistant200), + "429": decodeError("DeleteAssistant429", DeleteAssistant429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createSpeech": (options: Parameters[0]) => + HttpClientRequest.post("/audio/speech").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "400": decodeError("CreateSpeech400", CreateSpeech400), + "401": decodeError("CreateSpeech401", CreateSpeech401), + "429": decodeError("CreateSpeech429", CreateSpeech429), + "500": decodeError("CreateSpeech500", CreateSpeech500), + "503": decodeError("CreateSpeech503", CreateSpeech503), + orElse: unexpectedStatus + })) + ), + "createSpeechSse": (options) => + HttpClientRequest.post("/audio/speech").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateSpeech200Sse) + ), + "createSpeechStream": (options) => + HttpClientRequest.post("/audio/speech").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + binaryRequest + ), + "createTranscription": (options: Parameters[0]) => + HttpClientRequest.post("/audio/transcriptions").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateTranscription200), + "400": decodeError("CreateTranscription400", CreateTranscription400), + "401": decodeError("CreateTranscription401", CreateTranscription401), + "413": decodeError("CreateTranscription413", CreateTranscription413), + "429": decodeError("CreateTranscription429", CreateTranscription429), + "500": decodeError("CreateTranscription500", CreateTranscription500), + "502": decodeError("CreateTranscription502", CreateTranscription502), + "503": decodeError("CreateTranscription503", CreateTranscription503), + orElse: unexpectedStatus + })) + ), + "createTranscriptionSse": (options) => + HttpClientRequest.post("/audio/transcriptions").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + sseRequest(CreateTranscription200Sse) + ), + "createTranslation": (options: Parameters[0]) => + HttpClientRequest.post("/audio/translations").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateTranslation200), + "400": decodeError("CreateTranslation400", CreateTranslation400), + "401": decodeError("CreateTranslation401", CreateTranslation401), + "413": decodeError("CreateTranslation413", CreateTranslation413), + "429": decodeError("CreateTranslation429", CreateTranslation429), + "500": decodeError("CreateTranslation500", CreateTranslation500), + "502": decodeError("CreateTranslation502", CreateTranslation502), + "503": decodeError("CreateTranslation503", CreateTranslation503), + orElse: unexpectedStatus + })) + ), + "listVoiceConsents": (options: Parameters[0]) => + HttpClientRequest.get("/audio/voice_consents").pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListVoiceConsents200), + "400": decodeError("ListVoiceConsents400", ListVoiceConsents400), + "404": decodeError("ListVoiceConsents404", ListVoiceConsents404), + orElse: unexpectedStatus + })) + ), + "createVoiceConsent": (options: Parameters[0]) => + HttpClientRequest.post("/audio/voice_consents").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVoiceConsent200), + "400": decodeError("CreateVoiceConsent400", CreateVoiceConsent400), + "404": decodeError("CreateVoiceConsent404", CreateVoiceConsent404), + orElse: unexpectedStatus + })) + ), + "getVoiceConsent": (consentId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [consentId], + () => "/audio/voice_consents/" + __encodePathParam(consentId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetVoiceConsent200), + "400": decodeError("GetVoiceConsent400", GetVoiceConsent400), + "404": decodeError("GetVoiceConsent404", GetVoiceConsent404), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateVoiceConsent": (consentId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [consentId], + () => "/audio/voice_consents/" + __encodePathParam(consentId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateVoiceConsent200), + "400": decodeError("UpdateVoiceConsent400", UpdateVoiceConsent400), + "404": decodeError("UpdateVoiceConsent404", UpdateVoiceConsent404), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteVoiceConsent": (consentId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [consentId], + () => "/audio/voice_consents/" + __encodePathParam(consentId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteVoiceConsent200), + "400": decodeError("DeleteVoiceConsent400", DeleteVoiceConsent400), + "404": decodeError("DeleteVoiceConsent404", DeleteVoiceConsent404), + orElse: unexpectedStatus + })) + ) + ) + ), + "createVoice": (options: Parameters[0]) => + HttpClientRequest.post("/audio/voices").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVoice200), + "400": decodeError("CreateVoice400", CreateVoice400), + "404": decodeError("CreateVoice404", CreateVoice404), + "500": decodeError("CreateVoice500", CreateVoice500), + orElse: unexpectedStatus + })) + ), + "listBatches": (options: Parameters[0]) => + HttpClientRequest.get("/batches").pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListBatches200), + "400": decodeError("ListBatches400", ListBatches400), + "404": decodeError("ListBatches404", ListBatches404), + "429": decodeError("ListBatches429", ListBatches429), + orElse: unexpectedStatus + })) + ), + "createBatch": (options: Parameters[0]) => + HttpClientRequest.post("/batches").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateBatch200), + "400": decodeError("CreateBatch400", CreateBatch400), + "429": decodeError("CreateBatch429", CreateBatch429), + "500": decodeError("CreateBatch500", CreateBatch500), + "503": decodeError("CreateBatch503", CreateBatch503), + orElse: unexpectedStatus + })) + ), + "retrieveBatch": (batchId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [batchId], () => "/batches/" + __encodePathParam(batchId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveBatch200), + "400": decodeError("RetrieveBatch400", RetrieveBatch400), + "404": decodeError("RetrieveBatch404", RetrieveBatch404), + "429": decodeError("RetrieveBatch429", RetrieveBatch429), + orElse: unexpectedStatus + })) + ) + ) + ), + "cancelBatch": (batchId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [batchId], () => "/batches/" + __encodePathParam(batchId) + "/cancel") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelBatch200), + "400": decodeError("CancelBatch400", CancelBatch400), + "404": decodeError("CancelBatch404", CancelBatch404), + "409": decodeError("CancelBatch409", CancelBatch409), + "429": decodeError("CancelBatch429", CancelBatch429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listChatCompletions": (options: Parameters[0]) => + HttpClientRequest.get("/chat/completions").pipe( + HttpClientRequest.setUrlParams({ + "model": options?.params?.["model"] as any, + "metadata": options?.params?.["metadata"] as any, + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListChatCompletions200), + "400": decodeError("ListChatCompletions400", ListChatCompletions400), + "429": decodeError("ListChatCompletions429", ListChatCompletions429), + "500": decodeError("ListChatCompletions500", ListChatCompletions500), + orElse: unexpectedStatus + })) + ), + "createChatCompletion": (options: Parameters[0]) => + HttpClientRequest.post("/chat/completions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateChatCompletion200), + "400": decodeError("CreateChatCompletion400", CreateChatCompletion400), + "401": decodeError("CreateChatCompletion401", CreateChatCompletion401), + "404": decodeError("CreateChatCompletion404", CreateChatCompletion404), + "429": decodeError("CreateChatCompletion429", CreateChatCompletion429), + "500": decodeError("CreateChatCompletion500", CreateChatCompletion500), + "503": decodeError("CreateChatCompletion503", CreateChatCompletion503), + orElse: unexpectedStatus + })) + ), + "createChatCompletionSse": (options) => + HttpClientRequest.post("/chat/completions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateChatCompletion200Sse) + ), + "getChatCompletion": (completionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [completionId], + () => "/chat/completions/" + __encodePathParam(completionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetChatCompletion200), + "404": decodeError("GetChatCompletion404", GetChatCompletion404), + "429": decodeError("GetChatCompletion429", GetChatCompletion429), + "500": decodeError("GetChatCompletion500", GetChatCompletion500), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateChatCompletion": (completionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [completionId], + () => "/chat/completions/" + __encodePathParam(completionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateChatCompletion200), + "400": decodeError("UpdateChatCompletion400", UpdateChatCompletion400), + "404": decodeError("UpdateChatCompletion404", UpdateChatCompletion404), + "429": decodeError("UpdateChatCompletion429", UpdateChatCompletion429), + "500": decodeError("UpdateChatCompletion500", UpdateChatCompletion500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteChatCompletion": (completionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [completionId], + () => "/chat/completions/" + __encodePathParam(completionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteChatCompletion200), + "404": decodeError("DeleteChatCompletion404", DeleteChatCompletion404), + "429": decodeError("DeleteChatCompletion429", DeleteChatCompletion429), + "500": decodeError("DeleteChatCompletion500", DeleteChatCompletion500), + orElse: unexpectedStatus + })) + ) + ) + ), + "getChatCompletionMessages": (completionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [completionId], + () => "/chat/completions/" + __encodePathParam(completionId) + "/messages" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetChatCompletionMessages200), + "400": decodeError("GetChatCompletionMessages400", GetChatCompletionMessages400), + "404": decodeError("GetChatCompletionMessages404", GetChatCompletionMessages404), + "429": decodeError("GetChatCompletionMessages429", GetChatCompletionMessages429), + "500": decodeError("GetChatCompletionMessages500", GetChatCompletionMessages500), + orElse: unexpectedStatus + })) + ) + ) + ), + "createCompletion": (options: Parameters[0]) => + HttpClientRequest.post("/completions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateCompletion200), + "400": decodeError("CreateCompletion400", CreateCompletion400), + "401": decodeError("CreateCompletion401", CreateCompletion401), + "404": decodeError("CreateCompletion404", CreateCompletion404), + "429": decodeError("CreateCompletion429", CreateCompletion429), + "500": decodeError("CreateCompletion500", CreateCompletion500), + "503": decodeError("CreateCompletion503", CreateCompletion503), + orElse: unexpectedStatus + })) + ), + "ListContainers": (options: Parameters[0]) => + HttpClientRequest.get("/containers").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "name": options?.params?.["name"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListContainers200), + "404": decodeError("ListContainers404", ListContainers404), + "429": decodeError("ListContainers429", ListContainers429), + orElse: unexpectedStatus + })) + ), + "CreateContainer": (options: Parameters[0]) => + HttpClientRequest.post("/containers").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateContainer200), + "400": decodeError("CreateContainer400", CreateContainer400), + "429": decodeError("CreateContainer429", CreateContainer429), + orElse: unexpectedStatus + })) + ), + "RetrieveContainer": (containerId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [containerId], + () => "/containers/" + __encodePathParam(containerId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveContainer200), + "404": decodeError("RetrieveContainer404", RetrieveContainer404), + "429": decodeError("RetrieveContainer429", RetrieveContainer429), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteContainer": (containerId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [containerId], + () => "/containers/" + __encodePathParam(containerId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteContainer200), + "404": decodeError("DeleteContainer404", DeleteContainer404), + "429": decodeError("DeleteContainer429", DeleteContainer429), + orElse: unexpectedStatus + })) + ) + ) + ), + "ListContainerFiles": (containerId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [containerId], + () => "/containers/" + __encodePathParam(containerId) + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListContainerFiles200), + "404": decodeError("ListContainerFiles404", ListContainerFiles404), + "429": decodeError("ListContainerFiles429", ListContainerFiles429), + orElse: unexpectedStatus + })) + ) + ) + ), + "CreateContainerFile": (containerId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [containerId], + () => "/containers/" + __encodePathParam(containerId) + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateContainerFile200), + "400": decodeError("CreateContainerFile400", CreateContainerFile400), + "404": decodeError("CreateContainerFile404", CreateContainerFile404), + "429": decodeError("CreateContainerFile429", CreateContainerFile429), + orElse: unexpectedStatus + })) + ) + ) + ), + "RetrieveContainerFile": (containerId, fileId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [containerId, fileId], + () => "/containers/" + __encodePathParam(containerId) + "/files/" + __encodePathParam(fileId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveContainerFile200), + "404": decodeError("RetrieveContainerFile404", RetrieveContainerFile404), + "429": decodeError("RetrieveContainerFile429", RetrieveContainerFile429), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteContainerFile": (containerId, fileId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [containerId, fileId], + () => "/containers/" + __encodePathParam(containerId) + "/files/" + __encodePathParam(fileId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteContainerFile200), + "404": decodeError("DeleteContainerFile404", DeleteContainerFile404), + "429": decodeError("DeleteContainerFile429", DeleteContainerFile429), + orElse: unexpectedStatus + })) + ) + ) + ), + "RetrieveContainerFileContent": ( + containerId, + fileId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [containerId, fileId], + () => "/containers/" + __encodePathParam(containerId) + "/files/" + __encodePathParam(fileId) + "/content" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "404": decodeError("RetrieveContainerFileContent404", RetrieveContainerFileContent404), + "429": decodeError("RetrieveContainerFileContent429", RetrieveContainerFileContent429), + orElse: unexpectedStatus + })) + ) + ) + ), + "RetrieveContainerFileContentStream": (containerId, fileId) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [containerId, fileId], () => + "/containers/" + __encodePathParam(containerId) + "/files/" + __encodePathParam(fileId) + "/content").pipe( + Effect.map((request) => + request.pipe( + binaryRequest + ) + ) + ) + ), + "listConversationItems": (conversationId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [conversationId], + () => "/conversations/" + __encodePathParam(conversationId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "include": options?.params?.["include"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListConversationItems200), + "400": decodeError("ListConversationItems400", ListConversationItems400), + "404": decodeError("ListConversationItems404", ListConversationItems404), + "429": decodeError("ListConversationItems429", ListConversationItems429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createConversationItems": (conversationId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [conversationId], + () => "/conversations/" + __encodePathParam(conversationId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "include": options.params?.["include"] as any }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateConversationItems200), + "400": decodeError("CreateConversationItems400", CreateConversationItems400), + "404": decodeError("CreateConversationItems404", CreateConversationItems404), + "429": decodeError("CreateConversationItems429", CreateConversationItems429), + "500": decodeError("CreateConversationItems500", CreateConversationItems500), + orElse: unexpectedStatus + })) + ) + ) + ), + "getConversationItem": (conversationId, itemId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [conversationId, itemId], + () => "/conversations/" + __encodePathParam(conversationId) + "/items/" + __encodePathParam(itemId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "include": options?.params?.["include"] as any }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetConversationItem200), + "404": decodeError("GetConversationItem404", GetConversationItem404), + "429": decodeError("GetConversationItem429", GetConversationItem429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteConversationItem": ( + conversationId, + itemId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.delete, + [conversationId, itemId], + () => "/conversations/" + __encodePathParam(conversationId) + "/items/" + __encodePathParam(itemId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteConversationItem200), + "400": decodeError("DeleteConversationItem400", DeleteConversationItem400), + "404": decodeError("DeleteConversationItem404", DeleteConversationItem404), + "429": decodeError("DeleteConversationItem429", DeleteConversationItem429), + "500": decodeError("DeleteConversationItem500", DeleteConversationItem500), + orElse: unexpectedStatus + })) + ) + ) + ), + "createEmbedding": (options: Parameters[0]) => + HttpClientRequest.post("/embeddings").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateEmbedding200), + "400": decodeError("CreateEmbedding400", CreateEmbedding400), + "401": decodeError("CreateEmbedding401", CreateEmbedding401), + "429": decodeError("CreateEmbedding429", CreateEmbedding429), + "500": decodeError("CreateEmbedding500", CreateEmbedding500), + "503": decodeError("CreateEmbedding503", CreateEmbedding503), + orElse: unexpectedStatus + })) + ), + "listEvals": (options: Parameters[0]) => + HttpClientRequest.get("/evals").pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "order_by": options?.params?.["order_by"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListEvals200), + "429": decodeError("ListEvals429", ListEvals429), + orElse: unexpectedStatus + })) + ), + "createEval": (options: Parameters[0]) => + HttpClientRequest.post("/evals").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateEval201), + "400": decodeError("CreateEval400", CreateEval400), + "429": decodeError("CreateEval429", CreateEval429), + orElse: unexpectedStatus + })) + ), + "getEval": (evalId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [evalId], () => "/evals/" + __encodePathParam(evalId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetEval200), + "404": decodeError("GetEval404", GetEval404), + "429": decodeError("GetEval429", GetEval429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateEval": (evalId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [evalId], () => "/evals/" + __encodePathParam(evalId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateEval200), + "400": decodeError("UpdateEval400", UpdateEval400), + "404": decodeError("UpdateEval404", UpdateEval404), + "429": decodeError("UpdateEval429", UpdateEval429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteEval": (evalId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [evalId], () => "/evals/" + __encodePathParam(evalId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteEval200), + "400": decodeError("DeleteEval400", DeleteEval400), + "404": decodeError("DeleteEval404", DeleteEval404), + "429": decodeError("DeleteEval429", DeleteEval429), + orElse: unexpectedStatus + })) + ) + ) + ), + "getEvalRuns": (evalId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [evalId], () => "/evals/" + __encodePathParam(evalId) + "/runs").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "status": options?.params?.["status"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetEvalRuns200), + "400": decodeError("GetEvalRuns400", GetEvalRuns400), + "404": decodeError("GetEvalRuns404", GetEvalRuns404), + "429": decodeError("GetEvalRuns429", GetEvalRuns429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createEvalRun": (evalId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [evalId], () => "/evals/" + __encodePathParam(evalId) + "/runs").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateEvalRun201), + "400": decodeError("CreateEvalRun400", CreateEvalRun400), + "404": decodeError("CreateEvalRun404", CreateEvalRun404), + "429": decodeError("CreateEvalRun429", CreateEvalRun429), + "500": decodeError("CreateEvalRun500", CreateEvalRun500), + orElse: unexpectedStatus + })) + ) + ) + ), + "getEvalRun": (evalId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [evalId, runId], + () => "/evals/" + __encodePathParam(evalId) + "/runs/" + __encodePathParam(runId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetEvalRun200), + "400": decodeError("GetEvalRun400", GetEvalRun400), + "404": decodeError("GetEvalRun404", GetEvalRun404), + "429": decodeError("GetEvalRun429", GetEvalRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteEvalRun": (evalId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [evalId, runId], + () => "/evals/" + __encodePathParam(evalId) + "/runs/" + __encodePathParam(runId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteEvalRun200), + "400": decodeError("DeleteEvalRun400", DeleteEvalRun400), + "404": decodeError("DeleteEvalRun404", DeleteEvalRun404), + "429": decodeError("DeleteEvalRun429", DeleteEvalRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "cancelEvalRun": (evalId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [evalId, runId], + () => "/evals/" + __encodePathParam(evalId) + "/runs/" + __encodePathParam(runId) + "/cancel" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelEvalRun200), + "429": decodeError("CancelEvalRun429", CancelEvalRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "getEvalRunOutputItems": (evalId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [evalId, runId], + () => "/evals/" + __encodePathParam(evalId) + "/runs/" + __encodePathParam(runId) + "/output_items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "status": options?.params?.["status"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetEvalRunOutputItems200), + "400": decodeError("GetEvalRunOutputItems400", GetEvalRunOutputItems400), + "404": decodeError("GetEvalRunOutputItems404", GetEvalRunOutputItems404), + "429": decodeError("GetEvalRunOutputItems429", GetEvalRunOutputItems429), + orElse: unexpectedStatus + })) + ) + ) + ), + "getEvalRunOutputItem": ( + evalId, + runId, + outputItemId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.get, + [evalId, runId, outputItemId], + () => + "/evals/" + __encodePathParam(evalId) + "/runs/" + __encodePathParam(runId) + "/output_items/" + + __encodePathParam(outputItemId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetEvalRunOutputItem200), + "400": decodeError("GetEvalRunOutputItem400", GetEvalRunOutputItem400), + "404": decodeError("GetEvalRunOutputItem404", GetEvalRunOutputItem404), + "429": decodeError("GetEvalRunOutputItem429", GetEvalRunOutputItem429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listFiles": (options: Parameters[0]) => + HttpClientRequest.get("/files").pipe( + HttpClientRequest.setUrlParams({ + "purpose": options?.params?.["purpose"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListFiles200), + "400": decodeError("ListFiles400", ListFiles400), + "404": decodeError("ListFiles404", ListFiles404), + "429": decodeError("ListFiles429", ListFiles429), + orElse: unexpectedStatus + })) + ), + "createFile": (options: Parameters[0]) => + HttpClientRequest.post("/files").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateFile200), + "400": decodeError("CreateFile400", CreateFile400), + "429": decodeError("CreateFile429", CreateFile429), + "500": decodeError("CreateFile500", CreateFile500), + orElse: unexpectedStatus + })) + ), + "retrieveFile": (fileId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [fileId], () => "/files/" + __encodePathParam(fileId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveFile200), + "404": decodeError("RetrieveFile404", RetrieveFile404), + "429": decodeError("RetrieveFile429", RetrieveFile429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteFile": (fileId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [fileId], () => "/files/" + __encodePathParam(fileId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteFile200), + "404": decodeError("DeleteFile404", DeleteFile404), + "429": decodeError("DeleteFile429", DeleteFile429), + orElse: unexpectedStatus + })) + ) + ) + ), + "downloadFile": (fileId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [fileId], () => "/files/" + __encodePathParam(fileId) + "/content").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "400": decodeError("DownloadFile400", DownloadFile400), + "404": decodeError("DownloadFile404", DownloadFile404), + "429": decodeError("DownloadFile429", DownloadFile429), + "500": decodeError("DownloadFile500", DownloadFile500), + orElse: unexpectedStatus + })) + ) + ) + ), + "downloadFileStream": (fileId) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [fileId], () => "/files/" + __encodePathParam(fileId) + "/content") + .pipe( + Effect.map((request) => + request.pipe( + binaryRequest + ) + ) + ) + ), + "runGrader": (options: Parameters[0]) => + HttpClientRequest.post("/fine_tuning/alpha/graders/run").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RunGrader200), + "400": decodeError("RunGrader400", RunGrader400), + "429": decodeError("RunGrader429", RunGrader429), + orElse: unexpectedStatus + })) + ), + "validateGrader": (options: Parameters[0]) => + HttpClientRequest.post("/fine_tuning/alpha/graders/validate").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ValidateGrader200), + "400": decodeError("ValidateGrader400", ValidateGrader400), + "429": decodeError("ValidateGrader429", ValidateGrader429), + orElse: unexpectedStatus + })) + ), + "listFineTuningCheckpointPermissions": ( + fineTunedModelCheckpoint, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [fineTunedModelCheckpoint], + () => "/fine_tuning/checkpoints/" + __encodePathParam(fineTunedModelCheckpoint) + "/permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "project_id": options?.params?.["project_id"] as any, + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListFineTuningCheckpointPermissions200), + "400": decodeError("ListFineTuningCheckpointPermissions400", ListFineTuningCheckpointPermissions400), + "429": decodeError("ListFineTuningCheckpointPermissions429", ListFineTuningCheckpointPermissions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createFineTuningCheckpointPermission": ( + fineTunedModelCheckpoint, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [fineTunedModelCheckpoint], + () => "/fine_tuning/checkpoints/" + __encodePathParam(fineTunedModelCheckpoint) + "/permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateFineTuningCheckpointPermission200), + "400": decodeError("CreateFineTuningCheckpointPermission400", CreateFineTuningCheckpointPermission400), + "404": decodeError("CreateFineTuningCheckpointPermission404", CreateFineTuningCheckpointPermission404), + "429": decodeError("CreateFineTuningCheckpointPermission429", CreateFineTuningCheckpointPermission429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteFineTuningCheckpointPermission": ( + fineTunedModelCheckpoint, + permissionId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.delete, + [fineTunedModelCheckpoint, permissionId], + () => + "/fine_tuning/checkpoints/" + __encodePathParam(fineTunedModelCheckpoint) + "/permissions/" + + __encodePathParam(permissionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteFineTuningCheckpointPermission200), + "400": decodeError("DeleteFineTuningCheckpointPermission400", DeleteFineTuningCheckpointPermission400), + "404": decodeError("DeleteFineTuningCheckpointPermission404", DeleteFineTuningCheckpointPermission404), + "429": decodeError("DeleteFineTuningCheckpointPermission429", DeleteFineTuningCheckpointPermission429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listPaginatedFineTuningJobs": (options: Parameters[0]) => + HttpClientRequest.get("/fine_tuning/jobs").pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any, + "metadata": options?.params?.["metadata"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListPaginatedFineTuningJobs200), + "400": decodeError("ListPaginatedFineTuningJobs400", ListPaginatedFineTuningJobs400), + "429": decodeError("ListPaginatedFineTuningJobs429", ListPaginatedFineTuningJobs429), + orElse: unexpectedStatus + })) + ), + "createFineTuningJob": (options: Parameters[0]) => + HttpClientRequest.post("/fine_tuning/jobs").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateFineTuningJob200), + "400": decodeError("CreateFineTuningJob400", CreateFineTuningJob400), + "429": decodeError("CreateFineTuningJob429", CreateFineTuningJob429), + orElse: unexpectedStatus + })) + ), + "retrieveFineTuningJob": (fineTuningJobId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [fineTuningJobId], + () => "/fine_tuning/jobs/" + __encodePathParam(fineTuningJobId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveFineTuningJob200), + "404": decodeError("RetrieveFineTuningJob404", RetrieveFineTuningJob404), + "429": decodeError("RetrieveFineTuningJob429", RetrieveFineTuningJob429), + orElse: unexpectedStatus + })) + ) + ) + ), + "cancelFineTuningJob": (fineTuningJobId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [fineTuningJobId], + () => "/fine_tuning/jobs/" + __encodePathParam(fineTuningJobId) + "/cancel" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelFineTuningJob200), + "400": decodeError("CancelFineTuningJob400", CancelFineTuningJob400), + "404": decodeError("CancelFineTuningJob404", CancelFineTuningJob404), + "429": decodeError("CancelFineTuningJob429", CancelFineTuningJob429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listFineTuningJobCheckpoints": ( + fineTuningJobId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [fineTuningJobId], + () => "/fine_tuning/jobs/" + __encodePathParam(fineTuningJobId) + "/checkpoints" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListFineTuningJobCheckpoints200), + "400": decodeError("ListFineTuningJobCheckpoints400", ListFineTuningJobCheckpoints400), + "404": decodeError("ListFineTuningJobCheckpoints404", ListFineTuningJobCheckpoints404), + "429": decodeError("ListFineTuningJobCheckpoints429", ListFineTuningJobCheckpoints429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listFineTuningEvents": (fineTuningJobId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [fineTuningJobId], + () => "/fine_tuning/jobs/" + __encodePathParam(fineTuningJobId) + "/events" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "limit": options?.params?.["limit"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListFineTuningEvents200), + "400": decodeError("ListFineTuningEvents400", ListFineTuningEvents400), + "404": decodeError("ListFineTuningEvents404", ListFineTuningEvents404), + "429": decodeError("ListFineTuningEvents429", ListFineTuningEvents429), + orElse: unexpectedStatus + })) + ) + ) + ), + "pauseFineTuningJob": (fineTuningJobId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [fineTuningJobId], + () => "/fine_tuning/jobs/" + __encodePathParam(fineTuningJobId) + "/pause" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(PauseFineTuningJob200), + "400": decodeError("PauseFineTuningJob400", PauseFineTuningJob400), + "404": decodeError("PauseFineTuningJob404", PauseFineTuningJob404), + "429": decodeError("PauseFineTuningJob429", PauseFineTuningJob429), + orElse: unexpectedStatus + })) + ) + ) + ), + "resumeFineTuningJob": (fineTuningJobId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [fineTuningJobId], + () => "/fine_tuning/jobs/" + __encodePathParam(fineTuningJobId) + "/resume" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ResumeFineTuningJob200), + "400": decodeError("ResumeFineTuningJob400", ResumeFineTuningJob400), + "404": decodeError("ResumeFineTuningJob404", ResumeFineTuningJob404), + "429": decodeError("ResumeFineTuningJob429", ResumeFineTuningJob429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createImageEdit": (options: Parameters[0]) => + HttpClientRequest.post("/images/edits").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateImageEdit200), + "400": decodeError("CreateImageEdit400", CreateImageEdit400), + "403": decodeError("CreateImageEdit403", CreateImageEdit403), + "410": decodeError("CreateImageEdit410", CreateImageEdit410), + "429": decodeError("CreateImageEdit429", CreateImageEdit429), + "500": decodeError("CreateImageEdit500", CreateImageEdit500), + "503": decodeError("CreateImageEdit503", CreateImageEdit503), + orElse: unexpectedStatus + })) + ), + "createImageEditSse": (options) => + HttpClientRequest.post("/images/edits").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + sseRequest(CreateImageEdit200Sse) + ), + "createImage": (options: Parameters[0]) => + HttpClientRequest.post("/images/generations").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateImage200), + "400": decodeError("CreateImage400", CreateImage400), + "403": decodeError("CreateImage403", CreateImage403), + "410": decodeError("CreateImage410", CreateImage410), + "429": decodeError("CreateImage429", CreateImage429), + "500": decodeError("CreateImage500", CreateImage500), + "503": decodeError("CreateImage503", CreateImage503), + orElse: unexpectedStatus + })) + ), + "createImageSse": (options) => + HttpClientRequest.post("/images/generations").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateImage200Sse) + ), + "createImageVariation": (options: Parameters[0]) => + HttpClientRequest.post("/images/variations").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateImageVariation200), + "400": decodeError("CreateImageVariation400", CreateImageVariation400), + "403": decodeError("CreateImageVariation403", CreateImageVariation403), + "410": decodeError("CreateImageVariation410", CreateImageVariation410), + "429": decodeError("CreateImageVariation429", CreateImageVariation429), + "500": decodeError("CreateImageVariation500", CreateImageVariation500), + orElse: unexpectedStatus + })) + ), + "createLive": (options: Parameters[0]) => + HttpClientRequest.post("/live/sessions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateLive201), + "400": decodeError("CreateLive400", CreateLive400), + "413": decodeError("CreateLive413", CreateLive413), + "429": decodeError("CreateLive429", CreateLive429), + "503": decodeError("CreateLive503", CreateLive503), + "504": decodeError("CreateLive504", CreateLive504), + orElse: unexpectedStatus + })) + ), + "acceptLiveSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/live/sessions/" + __encodePathParam(sessionId) + "/accept" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "400": decodeError("AcceptLiveSession400", AcceptLiveSession400), + "404": decodeError("AcceptLiveSession404", AcceptLiveSession404), + "409": decodeError("AcceptLiveSession409", AcceptLiveSession409), + "429": decodeError("AcceptLiveSession429", AcceptLiveSession429), + "503": decodeError("AcceptLiveSession503", AcceptLiveSession503), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "forkLiveSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/live/sessions/" + __encodePathParam(sessionId) + "/fork" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ForkLiveSession201), + "400": decodeError("ForkLiveSession400", ForkLiveSession400), + "413": decodeError("ForkLiveSession413", ForkLiveSession413), + "429": decodeError("ForkLiveSession429", ForkLiveSession429), + "503": decodeError("ForkLiveSession503", ForkLiveSession503), + orElse: unexpectedStatus + })) + ) + ) + ), + "hangupLiveSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/live/sessions/" + __encodePathParam(sessionId) + "/hangup" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "404": decodeError("HangupLiveSession404", HangupLiveSession404), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "referLiveSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/live/sessions/" + __encodePathParam(sessionId) + "/refer" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "400": decodeError("ReferLiveSession400", ReferLiveSession400), + "404": decodeError("ReferLiveSession404", ReferLiveSession404), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "rejectLiveSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/live/sessions/" + __encodePathParam(sessionId) + "/reject" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "400": decodeError("RejectLiveSession400", RejectLiveSession400), + "404": decodeError("RejectLiveSession404", RejectLiveSession404), + "409": decodeError("RejectLiveSession409", RejectLiveSession409), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "listModels": (options: Parameters[0]) => + HttpClientRequest.get("/models").pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListModels200), + "403": decodeError("ListModels403", ListModels403), + orElse: unexpectedStatus + })) + ), + "retrieveModel": (model, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [model], () => "/models/" + __encodePathParam(model) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveModel200), + "403": decodeError("RetrieveModel403", RetrieveModel403), + "404": decodeError("RetrieveModel404", RetrieveModel404), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteModel": (model, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [model], () => "/models/" + __encodePathParam(model) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteModel200), + "403": decodeError("DeleteModel403", DeleteModel403), + "404": decodeError("DeleteModel404", DeleteModel404), + orElse: unexpectedStatus + })) + ) + ) + ), + "createModeration": (options: Parameters[0]) => + HttpClientRequest.post("/moderations").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateModeration200), + "400": decodeError("CreateModeration400", CreateModeration400), + "403": decodeError("CreateModeration403", CreateModeration403), + "429": decodeError("CreateModeration429", CreateModeration429), + "500": decodeError("CreateModeration500", CreateModeration500), + "503": decodeError("CreateModeration503", CreateModeration503), + orElse: unexpectedStatus + })) + ), + "adminApiKeysList": (options: Parameters[0]) => + HttpClientRequest.get("/organization/admin_api_keys").pipe( + HttpClientRequest.setUrlParams({ + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any, + "limit": options?.params?.["limit"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AdminApiKeysList200), + "400": decodeError("AdminApiKeysList400", AdminApiKeysList400), + "403": decodeError("AdminApiKeysList403", AdminApiKeysList403), + orElse: unexpectedStatus + })) + ), + "adminApiKeysCreate": (options: Parameters[0]) => + HttpClientRequest.post("/organization/admin_api_keys").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AdminApiKeysCreate200), + "400": decodeError("AdminApiKeysCreate400", AdminApiKeysCreate400), + "403": decodeError("AdminApiKeysCreate403", AdminApiKeysCreate403), + "404": decodeError("AdminApiKeysCreate404", AdminApiKeysCreate404), + orElse: unexpectedStatus + })) + ), + "adminApiKeysGet": (keyId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [keyId], + () => "/organization/admin_api_keys/" + __encodePathParam(keyId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AdminApiKeysGet200), + "403": decodeError("AdminApiKeysGet403", AdminApiKeysGet403), + "404": decodeError("AdminApiKeysGet404", AdminApiKeysGet404), + orElse: unexpectedStatus + })) + ) + ) + ), + "adminApiKeysDelete": (keyId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [keyId], + () => "/organization/admin_api_keys/" + __encodePathParam(keyId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AdminApiKeysDelete200), + "403": decodeError("AdminApiKeysDelete403", AdminApiKeysDelete403), + "404": decodeError("AdminApiKeysDelete404", AdminApiKeysDelete404), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAuditLogs": (options: Parameters[0]) => + HttpClientRequest.get("/organization/audit_logs").pipe( + HttpClientRequest.setUrlParams({ + "effective_at[gt]": options?.params?.["effective_at[gt]"] as any, + "effective_at[gte]": options?.params?.["effective_at[gte]"] as any, + "effective_at[lt]": options?.params?.["effective_at[lt]"] as any, + "effective_at[lte]": options?.params?.["effective_at[lte]"] as any, + "project_ids[]": options?.params?.["project_ids[]"] as any, + "event_types[]": options?.params?.["event_types[]"] as any, + "actor_ids[]": options?.params?.["actor_ids[]"] as any, + "actor_emails[]": options?.params?.["actor_emails[]"] as any, + "resource_ids[]": options?.params?.["resource_ids[]"] as any, + "tenant_only": options?.params?.["tenant_only"] as any, + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAuditLogs200), + "400": decodeError("ListAuditLogs400", ListAuditLogs400), + "404": decodeError("ListAuditLogs404", ListAuditLogs404), + "429": decodeError("ListAuditLogs429", ListAuditLogs429), + orElse: unexpectedStatus + })) + ), + "listOrganizationCertificates": (options: Parameters[0]) => + HttpClientRequest.get("/organization/certificates").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListOrganizationCertificates200), + "400": decodeError("ListOrganizationCertificates400", ListOrganizationCertificates400), + "403": decodeError("ListOrganizationCertificates403", ListOrganizationCertificates403), + "429": decodeError("ListOrganizationCertificates429", ListOrganizationCertificates429), + orElse: unexpectedStatus + })) + ), + "uploadCertificate": (options: Parameters[0]) => + HttpClientRequest.post("/organization/certificates").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UploadCertificate200), + "400": decodeError("UploadCertificate400", UploadCertificate400), + "403": decodeError("UploadCertificate403", UploadCertificate403), + "429": decodeError("UploadCertificate429", UploadCertificate429), + orElse: unexpectedStatus + })) + ), + "activateOrganizationCertificates": (options: Parameters[0]) => + HttpClientRequest.post("/organization/certificates/activate").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ActivateOrganizationCertificates200), + "400": decodeError("ActivateOrganizationCertificates400", ActivateOrganizationCertificates400), + "403": decodeError("ActivateOrganizationCertificates403", ActivateOrganizationCertificates403), + "404": decodeError("ActivateOrganizationCertificates404", ActivateOrganizationCertificates404), + "429": decodeError("ActivateOrganizationCertificates429", ActivateOrganizationCertificates429), + "500": decodeError("ActivateOrganizationCertificates500", ActivateOrganizationCertificates500), + orElse: unexpectedStatus + })) + ), + "deactivateOrganizationCertificates": ( + options: Parameters[0] + ) => + HttpClientRequest.post("/organization/certificates/deactivate").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeactivateOrganizationCertificates200), + "400": decodeError("DeactivateOrganizationCertificates400", DeactivateOrganizationCertificates400), + "403": decodeError("DeactivateOrganizationCertificates403", DeactivateOrganizationCertificates403), + "404": decodeError("DeactivateOrganizationCertificates404", DeactivateOrganizationCertificates404), + "429": decodeError("DeactivateOrganizationCertificates429", DeactivateOrganizationCertificates429), + "500": decodeError("DeactivateOrganizationCertificates500", DeactivateOrganizationCertificates500), + orElse: unexpectedStatus + })) + ), + "getCertificate": (certificateId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [certificateId], + () => "/organization/certificates/" + __encodePathParam(certificateId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "include": options?.params?.["include"] as any }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetCertificate200), + "400": decodeError("GetCertificate400", GetCertificate400), + "403": decodeError("GetCertificate403", GetCertificate403), + "404": decodeError("GetCertificate404", GetCertificate404), + "429": decodeError("GetCertificate429", GetCertificate429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyCertificate": (certificateId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [certificateId], + () => "/organization/certificates/" + __encodePathParam(certificateId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyCertificate200), + "400": decodeError("ModifyCertificate400", ModifyCertificate400), + "403": decodeError("ModifyCertificate403", ModifyCertificate403), + "404": decodeError("ModifyCertificate404", ModifyCertificate404), + "429": decodeError("ModifyCertificate429", ModifyCertificate429), + "500": decodeError("ModifyCertificate500", ModifyCertificate500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteCertificate": (certificateId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [certificateId], + () => "/organization/certificates/" + __encodePathParam(certificateId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteCertificate200), + "400": decodeError("DeleteCertificate400", DeleteCertificate400), + "403": decodeError("DeleteCertificate403", DeleteCertificate403), + "404": decodeError("DeleteCertificate404", DeleteCertificate404), + "429": decodeError("DeleteCertificate429", DeleteCertificate429), + orElse: unexpectedStatus + })) + ) + ) + ), + "usageCosts": (options: Parameters[0]) => + HttpClientRequest.get("/organization/costs").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "line_items": options.params["line_items"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageCosts200), + "400": decodeError("UsageCosts400", UsageCosts400), + "403": decodeError("UsageCosts403", UsageCosts403), + "429": decodeError("UsageCosts429", UsageCosts429), + orElse: unexpectedStatus + })) + ), + "retrieveOrganizationDataRetention": (options: Parameters[0]) => + HttpClientRequest.get("/organization/data_retention").pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveOrganizationDataRetention200), + "403": decodeError("RetrieveOrganizationDataRetention403", RetrieveOrganizationDataRetention403), + "404": decodeError("RetrieveOrganizationDataRetention404", RetrieveOrganizationDataRetention404), + orElse: unexpectedStatus + })) + ), + "updateOrganizationDataRetention": (options: Parameters[0]) => + HttpClientRequest.post("/organization/data_retention").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateOrganizationDataRetention200), + "400": decodeError("UpdateOrganizationDataRetention400", UpdateOrganizationDataRetention400), + "403": decodeError("UpdateOrganizationDataRetention403", UpdateOrganizationDataRetention403), + "404": decodeError("UpdateOrganizationDataRetention404", UpdateOrganizationDataRetention404), + orElse: unexpectedStatus + })) + ), + "listGroups": (options: Parameters[0]) => + HttpClientRequest.get("/organization/groups").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListGroups200), + "400": decodeError("ListGroups400", ListGroups400), + "403": decodeError("ListGroups403", ListGroups403), + "404": decodeError("ListGroups404", ListGroups404), + "429": decodeError("ListGroups429", ListGroups429), + orElse: unexpectedStatus + })) + ), + "createGroup": (options: Parameters[0]) => + HttpClientRequest.post("/organization/groups").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateGroup200), + "400": decodeError("CreateGroup400", CreateGroup400), + "403": decodeError("CreateGroup403", CreateGroup403), + "404": decodeError("CreateGroup404", CreateGroup404), + "422": decodeError("CreateGroup422", CreateGroup422), + "429": decodeError("CreateGroup429", CreateGroup429), + "500": decodeError("CreateGroup500", CreateGroup500), + orElse: unexpectedStatus + })) + ), + "retrieveGroup": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveGroup200), + "400": decodeError("RetrieveGroup400", RetrieveGroup400), + "403": decodeError("RetrieveGroup403", RetrieveGroup403), + "404": decodeError("RetrieveGroup404", RetrieveGroup404), + "429": decodeError("RetrieveGroup429", RetrieveGroup429), + "500": decodeError("RetrieveGroup500", RetrieveGroup500), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateGroup": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateGroup200), + "400": decodeError("UpdateGroup400", UpdateGroup400), + "403": decodeError("UpdateGroup403", UpdateGroup403), + "404": decodeError("UpdateGroup404", UpdateGroup404), + "429": decodeError("UpdateGroup429", UpdateGroup429), + "500": decodeError("UpdateGroup500", UpdateGroup500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteGroup": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteGroup200), + "400": decodeError("DeleteGroup400", DeleteGroup400), + "403": decodeError("DeleteGroup403", DeleteGroup403), + "404": decodeError("DeleteGroup404", DeleteGroup404), + "429": decodeError("DeleteGroup429", DeleteGroup429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listGroupRoleAssignments": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListGroupRoleAssignments200), + "400": decodeError("ListGroupRoleAssignments400", ListGroupRoleAssignments400), + "403": decodeError("ListGroupRoleAssignments403", ListGroupRoleAssignments403), + "404": decodeError("ListGroupRoleAssignments404", ListGroupRoleAssignments404), + "429": decodeError("ListGroupRoleAssignments429", ListGroupRoleAssignments429), + orElse: unexpectedStatus + })) + ) + ) + ), + "assignGroupRole": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AssignGroupRole200), + "400": decodeError("AssignGroupRole400", AssignGroupRole400), + "403": decodeError("AssignGroupRole403", AssignGroupRole403), + "404": decodeError("AssignGroupRole404", AssignGroupRole404), + "429": decodeError("AssignGroupRole429", AssignGroupRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveGroupRole": (groupId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [groupId, roleId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveGroupRole200), + "400": decodeError("RetrieveGroupRole400", RetrieveGroupRole400), + "403": decodeError("RetrieveGroupRole403", RetrieveGroupRole403), + "404": decodeError("RetrieveGroupRole404", RetrieveGroupRole404), + "429": decodeError("RetrieveGroupRole429", RetrieveGroupRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "unassignGroupRole": (groupId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [groupId, roleId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UnassignGroupRole200), + "400": decodeError("UnassignGroupRole400", UnassignGroupRole400), + "403": decodeError("UnassignGroupRole403", UnassignGroupRole403), + "404": decodeError("UnassignGroupRole404", UnassignGroupRole404), + "429": decodeError("UnassignGroupRole429", UnassignGroupRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listGroupUsers": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/users" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListGroupUsers200), + "400": decodeError("ListGroupUsers400", ListGroupUsers400), + "403": decodeError("ListGroupUsers403", ListGroupUsers403), + "404": decodeError("ListGroupUsers404", ListGroupUsers404), + "429": decodeError("ListGroupUsers429", ListGroupUsers429), + "502": decodeError("ListGroupUsers502", ListGroupUsers502), + orElse: unexpectedStatus + })) + ) + ) + ), + "addGroupUser": (groupId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [groupId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/users" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AddGroupUser200), + "400": decodeError("AddGroupUser400", AddGroupUser400), + "403": decodeError("AddGroupUser403", AddGroupUser403), + "404": decodeError("AddGroupUser404", AddGroupUser404), + "409": decodeError("AddGroupUser409", AddGroupUser409), + "429": decodeError("AddGroupUser429", AddGroupUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveGroupUser": (groupId, userId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [groupId, userId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/users/" + __encodePathParam(userId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveGroupUser200), + "400": decodeError("RetrieveGroupUser400", RetrieveGroupUser400), + "403": decodeError("RetrieveGroupUser403", RetrieveGroupUser403), + "404": decodeError("RetrieveGroupUser404", RetrieveGroupUser404), + "429": decodeError("RetrieveGroupUser429", RetrieveGroupUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "removeGroupUser": (groupId, userId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [groupId, userId], + () => "/organization/groups/" + __encodePathParam(groupId) + "/users/" + __encodePathParam(userId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RemoveGroupUser200), + "400": decodeError("RemoveGroupUser400", RemoveGroupUser400), + "403": decodeError("RemoveGroupUser403", RemoveGroupUser403), + "404": decodeError("RemoveGroupUser404", RemoveGroupUser404), + "409": decodeError("RemoveGroupUser409", RemoveGroupUser409), + "429": decodeError("RemoveGroupUser429", RemoveGroupUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listInvites": (options: Parameters[0]) => + HttpClientRequest.get("/organization/invites").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListInvites200), + "400": decodeError("ListInvites400", ListInvites400), + "403": decodeError("ListInvites403", ListInvites403), + "404": decodeError("ListInvites404", ListInvites404), + "429": decodeError("ListInvites429", ListInvites429), + orElse: unexpectedStatus + })) + ), + "inviteUser": (options: Parameters[0]) => + HttpClientRequest.post("/organization/invites").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "200": decodeSuccess(InviteUser200), + "201": decodeSuccess(InviteUser201), + "400": decodeError("InviteUser400", InviteUser400), + "403": decodeError("InviteUser403", InviteUser403), + "429": decodeError("InviteUser429", InviteUser429), + orElse: unexpectedStatus + })) + ), + "retrieveInvite": (inviteId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [inviteId], + () => "/organization/invites/" + __encodePathParam(inviteId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveInvite200), + "400": decodeError("RetrieveInvite400", RetrieveInvite400), + "403": decodeError("RetrieveInvite403", RetrieveInvite403), + "404": decodeError("RetrieveInvite404", RetrieveInvite404), + "429": decodeError("RetrieveInvite429", RetrieveInvite429), + "500": decodeError("RetrieveInvite500", RetrieveInvite500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteInvite": (inviteId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [inviteId], + () => "/organization/invites/" + __encodePathParam(inviteId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteInvite200), + "400": decodeError("DeleteInvite400", DeleteInvite400), + "403": decodeError("DeleteInvite403", DeleteInvite403), + "404": decodeError("DeleteInvite404", DeleteInvite404), + "429": decodeError("DeleteInvite429", DeleteInvite429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjects": (options: Parameters[0]) => + HttpClientRequest.get("/organization/projects").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "include_archived": options?.params?.["include_archived"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjects200), + "400": decodeError("ListProjects400", ListProjects400), + "403": decodeError("ListProjects403", ListProjects403), + "404": decodeError("ListProjects404", ListProjects404), + "429": decodeError("ListProjects429", ListProjects429), + orElse: unexpectedStatus + })) + ), + "createProject": (options: Parameters[0]) => + HttpClientRequest.post("/organization/projects").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "200": decodeSuccess(CreateProject200), + "201": decodeSuccess(CreateProject201), + "400": decodeError("CreateProject400", CreateProject400), + "403": decodeError("CreateProject403", CreateProject403), + "429": decodeError("CreateProject429", CreateProject429), + orElse: unexpectedStatus + })) + ), + "retrieveProject": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProject200), + "403": decodeError("RetrieveProject403", RetrieveProject403), + "404": decodeError("RetrieveProject404", RetrieveProject404), + "429": decodeError("RetrieveProject429", RetrieveProject429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyProject": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyProject200), + "400": decodeError("ModifyProject400", ModifyProject400), + "403": decodeError("ModifyProject403", ModifyProject403), + "404": decodeError("ModifyProject404", ModifyProject404), + "429": decodeError("ModifyProject429", ModifyProject429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectApiKeys": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/api_keys" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "owner_project_access": options?.params?.["owner_project_access"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectApiKeys200), + "400": decodeError("ListProjectApiKeys400", ListProjectApiKeys400), + "403": decodeError("ListProjectApiKeys403", ListProjectApiKeys403), + "404": decodeError("ListProjectApiKeys404", ListProjectApiKeys404), + "429": decodeError("ListProjectApiKeys429", ListProjectApiKeys429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectApiKey": (projectId, apiKeyId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [projectId, apiKeyId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/api_keys/" + __encodePathParam(apiKeyId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectApiKey200), + "400": decodeError("RetrieveProjectApiKey400", RetrieveProjectApiKey400), + "403": decodeError("RetrieveProjectApiKey403", RetrieveProjectApiKey403), + "404": decodeError("RetrieveProjectApiKey404", RetrieveProjectApiKey404), + "429": decodeError("RetrieveProjectApiKey429", RetrieveProjectApiKey429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteProjectApiKey": (projectId, apiKeyId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, apiKeyId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/api_keys/" + __encodePathParam(apiKeyId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteProjectApiKey200), + "400": decodeError("DeleteProjectApiKey400", DeleteProjectApiKey400), + "403": decodeError("DeleteProjectApiKey403", DeleteProjectApiKey403), + "404": decodeError("DeleteProjectApiKey404", DeleteProjectApiKey404), + "429": decodeError("DeleteProjectApiKey429", DeleteProjectApiKey429), + orElse: unexpectedStatus + })) + ) + ) + ), + "archiveProject": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/archive" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ArchiveProject200), + "400": decodeError("ArchiveProject400", ArchiveProject400), + "403": decodeError("ArchiveProject403", ArchiveProject403), + "404": decodeError("ArchiveProject404", ArchiveProject404), + "429": decodeError("ArchiveProject429", ArchiveProject429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectCertificates": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/certificates" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectCertificates200), + "400": decodeError("ListProjectCertificates400", ListProjectCertificates400), + "403": decodeError("ListProjectCertificates403", ListProjectCertificates403), + "429": decodeError("ListProjectCertificates429", ListProjectCertificates429), + orElse: unexpectedStatus + })) + ) + ) + ), + "activateProjectCertificates": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/certificates/activate" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ActivateProjectCertificates200), + "400": decodeError("ActivateProjectCertificates400", ActivateProjectCertificates400), + "403": decodeError("ActivateProjectCertificates403", ActivateProjectCertificates403), + "404": decodeError("ActivateProjectCertificates404", ActivateProjectCertificates404), + "429": decodeError("ActivateProjectCertificates429", ActivateProjectCertificates429), + "500": decodeError("ActivateProjectCertificates500", ActivateProjectCertificates500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deactivateProjectCertificates": ( + projectId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/certificates/deactivate" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeactivateProjectCertificates200), + "400": decodeError("DeactivateProjectCertificates400", DeactivateProjectCertificates400), + "403": decodeError("DeactivateProjectCertificates403", DeactivateProjectCertificates403), + "404": decodeError("DeactivateProjectCertificates404", DeactivateProjectCertificates404), + "429": decodeError("DeactivateProjectCertificates429", DeactivateProjectCertificates429), + "500": decodeError("DeactivateProjectCertificates500", DeactivateProjectCertificates500), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectDataRetention": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/data_retention" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectDataRetention200), + "400": decodeError("RetrieveProjectDataRetention400", RetrieveProjectDataRetention400), + "403": decodeError("RetrieveProjectDataRetention403", RetrieveProjectDataRetention403), + "404": decodeError("RetrieveProjectDataRetention404", RetrieveProjectDataRetention404), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectDataRetention": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/data_retention" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectDataRetention200), + "400": decodeError("UpdateProjectDataRetention400", UpdateProjectDataRetention400), + "403": decodeError("UpdateProjectDataRetention403", UpdateProjectDataRetention403), + "404": decodeError("UpdateProjectDataRetention404", UpdateProjectDataRetention404), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectGroups": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/groups" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectGroups200), + "400": decodeError("ListProjectGroups400", ListProjectGroups400), + "403": decodeError("ListProjectGroups403", ListProjectGroups403), + "404": decodeError("ListProjectGroups404", ListProjectGroups404), + "429": decodeError("ListProjectGroups429", ListProjectGroups429), + orElse: unexpectedStatus + })) + ) + ) + ), + "addProjectGroup": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/groups" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AddProjectGroup200), + "400": decodeError("AddProjectGroup400", AddProjectGroup400), + "403": decodeError("AddProjectGroup403", AddProjectGroup403), + "404": decodeError("AddProjectGroup404", AddProjectGroup404), + "429": decodeError("AddProjectGroup429", AddProjectGroup429), + "500": decodeError("AddProjectGroup500", AddProjectGroup500), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectGroup": (projectId, groupId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [projectId, groupId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/groups/" + __encodePathParam(groupId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "group_type": options?.params?.["group_type"] as any }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectGroup200), + "400": decodeError("RetrieveProjectGroup400", RetrieveProjectGroup400), + "403": decodeError("RetrieveProjectGroup403", RetrieveProjectGroup403), + "404": decodeError("RetrieveProjectGroup404", RetrieveProjectGroup404), + "429": decodeError("RetrieveProjectGroup429", RetrieveProjectGroup429), + orElse: unexpectedStatus + })) + ) + ) + ), + "removeProjectGroup": (projectId, groupId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, groupId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/groups/" + __encodePathParam(groupId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RemoveProjectGroup200), + "400": decodeError("RemoveProjectGroup400", RemoveProjectGroup400), + "403": decodeError("RemoveProjectGroup403", RemoveProjectGroup403), + "404": decodeError("RemoveProjectGroup404", RemoveProjectGroup404), + "429": decodeError("RemoveProjectGroup429", RemoveProjectGroup429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectHostedToolPermissions": ( + projectId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/hosted_tool_permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectHostedToolPermissions200), + "400": decodeError("RetrieveProjectHostedToolPermissions400", RetrieveProjectHostedToolPermissions400), + "403": decodeError("RetrieveProjectHostedToolPermissions403", RetrieveProjectHostedToolPermissions403), + "404": decodeError("RetrieveProjectHostedToolPermissions404", RetrieveProjectHostedToolPermissions404), + "429": decodeError("RetrieveProjectHostedToolPermissions429", RetrieveProjectHostedToolPermissions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectHostedToolPermissions": ( + projectId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/hosted_tool_permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectHostedToolPermissions200), + "400": decodeError("UpdateProjectHostedToolPermissions400", UpdateProjectHostedToolPermissions400), + "403": decodeError("UpdateProjectHostedToolPermissions403", UpdateProjectHostedToolPermissions403), + "404": decodeError("UpdateProjectHostedToolPermissions404", UpdateProjectHostedToolPermissions404), + "409": decodeError("UpdateProjectHostedToolPermissions409", UpdateProjectHostedToolPermissions409), + "429": decodeError("UpdateProjectHostedToolPermissions429", UpdateProjectHostedToolPermissions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectModelPermissions": ( + projectId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/model_permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectModelPermissions200), + "400": decodeError("RetrieveProjectModelPermissions400", RetrieveProjectModelPermissions400), + "403": decodeError("RetrieveProjectModelPermissions403", RetrieveProjectModelPermissions403), + "404": decodeError("RetrieveProjectModelPermissions404", RetrieveProjectModelPermissions404), + "429": decodeError("RetrieveProjectModelPermissions429", RetrieveProjectModelPermissions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectModelPermissions": ( + projectId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/model_permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectModelPermissions200), + "400": decodeError("UpdateProjectModelPermissions400", UpdateProjectModelPermissions400), + "403": decodeError("UpdateProjectModelPermissions403", UpdateProjectModelPermissions403), + "404": decodeError("UpdateProjectModelPermissions404", UpdateProjectModelPermissions404), + "429": decodeError("UpdateProjectModelPermissions429", UpdateProjectModelPermissions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteProjectModelPermissions": ( + projectId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.delete, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/model_permissions" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteProjectModelPermissions200), + "400": decodeError("DeleteProjectModelPermissions400", DeleteProjectModelPermissions400), + "403": decodeError("DeleteProjectModelPermissions403", DeleteProjectModelPermissions403), + "404": decodeError("DeleteProjectModelPermissions404", DeleteProjectModelPermissions404), + "429": decodeError("DeleteProjectModelPermissions429", DeleteProjectModelPermissions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectRateLimits": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/rate_limits" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectRateLimits200), + "400": decodeError("ListProjectRateLimits400", ListProjectRateLimits400), + "403": decodeError("ListProjectRateLimits403", ListProjectRateLimits403), + "404": decodeError("ListProjectRateLimits404", ListProjectRateLimits404), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectRateLimits": ( + projectId, + rateLimitId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.post, + [projectId, rateLimitId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/rate_limits/" + __encodePathParam(rateLimitId) + + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectRateLimits200), + "400": decodeError("UpdateProjectRateLimits400", UpdateProjectRateLimits400), + "403": decodeError("UpdateProjectRateLimits403", UpdateProjectRateLimits403), + "404": decodeError("UpdateProjectRateLimits404", UpdateProjectRateLimits404), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectServiceAccounts": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/service_accounts" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectServiceAccounts200), + "400": decodeError("ListProjectServiceAccounts400", ListProjectServiceAccounts400), + "403": decodeError("ListProjectServiceAccounts403", ListProjectServiceAccounts403), + "404": decodeError("ListProjectServiceAccounts404", ListProjectServiceAccounts404), + "429": decodeError("ListProjectServiceAccounts429", ListProjectServiceAccounts429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createProjectServiceAccount": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/service_accounts" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "200": decodeSuccess(CreateProjectServiceAccount200), + "201": decodeSuccess(CreateProjectServiceAccount201), + "400": decodeError("CreateProjectServiceAccount400", CreateProjectServiceAccount400), + "403": decodeError("CreateProjectServiceAccount403", CreateProjectServiceAccount403), + "404": decodeError("CreateProjectServiceAccount404", CreateProjectServiceAccount404), + "429": decodeError("CreateProjectServiceAccount429", CreateProjectServiceAccount429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectServiceAccount": ( + projectId, + serviceAccountId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId, serviceAccountId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/service_accounts/" + + __encodePathParam(serviceAccountId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectServiceAccount200), + "400": decodeError("RetrieveProjectServiceAccount400", RetrieveProjectServiceAccount400), + "403": decodeError("RetrieveProjectServiceAccount403", RetrieveProjectServiceAccount403), + "404": decodeError("RetrieveProjectServiceAccount404", RetrieveProjectServiceAccount404), + "429": decodeError("RetrieveProjectServiceAccount429", RetrieveProjectServiceAccount429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectServiceAccount": ( + projectId, + serviceAccountId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.post, + [projectId, serviceAccountId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/service_accounts/" + + __encodePathParam(serviceAccountId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectServiceAccount200), + "400": decodeError("UpdateProjectServiceAccount400", UpdateProjectServiceAccount400), + "403": decodeError("UpdateProjectServiceAccount403", UpdateProjectServiceAccount403), + "404": decodeError("UpdateProjectServiceAccount404", UpdateProjectServiceAccount404), + "429": decodeError("UpdateProjectServiceAccount429", UpdateProjectServiceAccount429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteProjectServiceAccount": ( + projectId, + serviceAccountId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, serviceAccountId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/service_accounts/" + + __encodePathParam(serviceAccountId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteProjectServiceAccount200), + "400": decodeError("DeleteProjectServiceAccount400", DeleteProjectServiceAccount400), + "403": decodeError("DeleteProjectServiceAccount403", DeleteProjectServiceAccount403), + "404": decodeError("DeleteProjectServiceAccount404", DeleteProjectServiceAccount404), + "429": decodeError("DeleteProjectServiceAccount429", DeleteProjectServiceAccount429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectSpendAlerts": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/spend_alerts" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectSpendAlerts200), + "400": decodeError("ListProjectSpendAlerts400", ListProjectSpendAlerts400), + "403": decodeError("ListProjectSpendAlerts403", ListProjectSpendAlerts403), + "404": decodeError("ListProjectSpendAlerts404", ListProjectSpendAlerts404), + orElse: unexpectedStatus + })) + ) + ) + ), + "createProjectSpendAlert": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/spend_alerts" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateProjectSpendAlert200), + "400": decodeError("CreateProjectSpendAlert400", CreateProjectSpendAlert400), + "403": decodeError("CreateProjectSpendAlert403", CreateProjectSpendAlert403), + "404": decodeError("CreateProjectSpendAlert404", CreateProjectSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectSpendAlert": ( + projectId, + alertId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId, alertId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/spend_alerts/" + __encodePathParam(alertId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectSpendAlert200), + "400": decodeError("RetrieveProjectSpendAlert400", RetrieveProjectSpendAlert400), + "403": decodeError("RetrieveProjectSpendAlert403", RetrieveProjectSpendAlert403), + "404": decodeError("RetrieveProjectSpendAlert404", RetrieveProjectSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectSpendAlert": (projectId, alertId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [projectId, alertId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/spend_alerts/" + __encodePathParam(alertId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectSpendAlert200), + "400": decodeError("UpdateProjectSpendAlert400", UpdateProjectSpendAlert400), + "403": decodeError("UpdateProjectSpendAlert403", UpdateProjectSpendAlert403), + "404": decodeError("UpdateProjectSpendAlert404", UpdateProjectSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteProjectSpendAlert": (projectId, alertId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, alertId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/spend_alerts/" + __encodePathParam(alertId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteProjectSpendAlert200), + "400": decodeError("DeleteProjectSpendAlert400", DeleteProjectSpendAlert400), + "403": decodeError("DeleteProjectSpendAlert403", DeleteProjectSpendAlert403), + "404": decodeError("DeleteProjectSpendAlert404", DeleteProjectSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectUsers": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/users" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectUsers200), + "400": decodeError("ListProjectUsers400", ListProjectUsers400), + "403": decodeError("ListProjectUsers403", ListProjectUsers403), + "404": decodeError("ListProjectUsers404", ListProjectUsers404), + "429": decodeError("ListProjectUsers429", ListProjectUsers429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createProjectUser": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/users" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "200": decodeSuccess(CreateProjectUser200), + "201": decodeSuccess(CreateProjectUser201), + "400": decodeError("CreateProjectUser400", CreateProjectUser400), + "403": decodeError("CreateProjectUser403", CreateProjectUser403), + "404": decodeError("CreateProjectUser404", CreateProjectUser404), + "409": decodeError("CreateProjectUser409", CreateProjectUser409), + "429": decodeError("CreateProjectUser429", CreateProjectUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectUser": (projectId, userId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [projectId, userId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectUser200), + "400": decodeError("RetrieveProjectUser400", RetrieveProjectUser400), + "403": decodeError("RetrieveProjectUser403", RetrieveProjectUser403), + "404": decodeError("RetrieveProjectUser404", RetrieveProjectUser404), + "429": decodeError("RetrieveProjectUser429", RetrieveProjectUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyProjectUser": (projectId, userId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [projectId, userId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyProjectUser200), + "400": decodeError("ModifyProjectUser400", ModifyProjectUser400), + "403": decodeError("ModifyProjectUser403", ModifyProjectUser403), + "404": decodeError("ModifyProjectUser404", ModifyProjectUser404), + "429": decodeError("ModifyProjectUser429", ModifyProjectUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteProjectUser": (projectId, userId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, userId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteProjectUser200), + "400": decodeError("DeleteProjectUser400", DeleteProjectUser400), + "403": decodeError("DeleteProjectUser403", DeleteProjectUser403), + "404": decodeError("DeleteProjectUser404", DeleteProjectUser404), + "429": decodeError("DeleteProjectUser429", DeleteProjectUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listRoles": (options: Parameters[0]) => + HttpClientRequest.get("/organization/roles").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListRoles200), + "400": decodeError("ListRoles400", ListRoles400), + "403": decodeError("ListRoles403", ListRoles403), + "404": decodeError("ListRoles404", ListRoles404), + "429": decodeError("ListRoles429", ListRoles429), + orElse: unexpectedStatus + })) + ), + "createRole": (options: Parameters[0]) => + HttpClientRequest.post("/organization/roles").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateRole200), + "400": decodeError("CreateRole400", CreateRole400), + "403": decodeError("CreateRole403", CreateRole403), + "404": decodeError("CreateRole404", CreateRole404), + "429": decodeError("CreateRole429", CreateRole429), + orElse: unexpectedStatus + })) + ), + "retrieveRole": (roleId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [roleId], () => "/organization/roles/" + __encodePathParam(roleId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveRole200), + "403": decodeError("RetrieveRole403", RetrieveRole403), + "404": decodeError("RetrieveRole404", RetrieveRole404), + "429": decodeError("RetrieveRole429", RetrieveRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateRole": (roleId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [roleId], () => "/organization/roles/" + __encodePathParam(roleId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateRole200), + "400": decodeError("UpdateRole400", UpdateRole400), + "403": decodeError("UpdateRole403", UpdateRole403), + "404": decodeError("UpdateRole404", UpdateRole404), + "429": decodeError("UpdateRole429", UpdateRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteRole": (roleId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [roleId], + () => "/organization/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteRole200), + "400": decodeError("DeleteRole400", DeleteRole400), + "403": decodeError("DeleteRole403", DeleteRole403), + "404": decodeError("DeleteRole404", DeleteRole404), + "429": decodeError("DeleteRole429", DeleteRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listOrganizationSpendAlerts": (options: Parameters[0]) => + HttpClientRequest.get("/organization/spend_alerts").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListOrganizationSpendAlerts200), + "400": decodeError("ListOrganizationSpendAlerts400", ListOrganizationSpendAlerts400), + "403": decodeError("ListOrganizationSpendAlerts403", ListOrganizationSpendAlerts403), + "404": decodeError("ListOrganizationSpendAlerts404", ListOrganizationSpendAlerts404), + orElse: unexpectedStatus + })) + ), + "createOrganizationSpendAlert": (options: Parameters[0]) => + HttpClientRequest.post("/organization/spend_alerts").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateOrganizationSpendAlert200), + "400": decodeError("CreateOrganizationSpendAlert400", CreateOrganizationSpendAlert400), + "403": decodeError("CreateOrganizationSpendAlert403", CreateOrganizationSpendAlert403), + orElse: unexpectedStatus + })) + ), + "retrieveOrganizationSpendAlert": ( + alertId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [alertId], + () => "/organization/spend_alerts/" + __encodePathParam(alertId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveOrganizationSpendAlert200), + "403": decodeError("RetrieveOrganizationSpendAlert403", RetrieveOrganizationSpendAlert403), + "404": decodeError("RetrieveOrganizationSpendAlert404", RetrieveOrganizationSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateOrganizationSpendAlert": (alertId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [alertId], + () => "/organization/spend_alerts/" + __encodePathParam(alertId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateOrganizationSpendAlert200), + "400": decodeError("UpdateOrganizationSpendAlert400", UpdateOrganizationSpendAlert400), + "403": decodeError("UpdateOrganizationSpendAlert403", UpdateOrganizationSpendAlert403), + "404": decodeError("UpdateOrganizationSpendAlert404", UpdateOrganizationSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteOrganizationSpendAlert": (alertId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [alertId], + () => "/organization/spend_alerts/" + __encodePathParam(alertId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteOrganizationSpendAlert200), + "403": decodeError("DeleteOrganizationSpendAlert403", DeleteOrganizationSpendAlert403), + "404": decodeError("DeleteOrganizationSpendAlert404", DeleteOrganizationSpendAlert404), + orElse: unexpectedStatus + })) + ) + ) + ), + "usageAudioSpeeches": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/audio_speeches").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageAudioSpeeches200), + "400": decodeError("UsageAudioSpeeches400", UsageAudioSpeeches400), + "403": decodeError("UsageAudioSpeeches403", UsageAudioSpeeches403), + "429": decodeError("UsageAudioSpeeches429", UsageAudioSpeeches429), + orElse: unexpectedStatus + })) + ), + "usageAudioTranscriptions": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/audio_transcriptions").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageAudioTranscriptions200), + "400": decodeError("UsageAudioTranscriptions400", UsageAudioTranscriptions400), + "403": decodeError("UsageAudioTranscriptions403", UsageAudioTranscriptions403), + "429": decodeError("UsageAudioTranscriptions429", UsageAudioTranscriptions429), + orElse: unexpectedStatus + })) + ), + "usageCodeInterpreterSessions": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/code_interpreter_sessions").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageCodeInterpreterSessions200), + "400": decodeError("UsageCodeInterpreterSessions400", UsageCodeInterpreterSessions400), + "403": decodeError("UsageCodeInterpreterSessions403", UsageCodeInterpreterSessions403), + "429": decodeError("UsageCodeInterpreterSessions429", UsageCodeInterpreterSessions429), + orElse: unexpectedStatus + })) + ), + "usageCompletions": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/completions").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "batch": options.params["batch"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageCompletions200), + "400": decodeError("UsageCompletions400", UsageCompletions400), + "403": decodeError("UsageCompletions403", UsageCompletions403), + "429": decodeError("UsageCompletions429", UsageCompletions429), + orElse: unexpectedStatus + })) + ), + "usageEmbeddings": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/embeddings").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageEmbeddings200), + "400": decodeError("UsageEmbeddings400", UsageEmbeddings400), + "403": decodeError("UsageEmbeddings403", UsageEmbeddings403), + "429": decodeError("UsageEmbeddings429", UsageEmbeddings429), + orElse: unexpectedStatus + })) + ), + "usageFileSearchCalls": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/file_search_calls").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "vector_store_ids": options.params["vector_store_ids"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageFileSearchCalls200), + "400": decodeError("UsageFileSearchCalls400", UsageFileSearchCalls400), + "403": decodeError("UsageFileSearchCalls403", UsageFileSearchCalls403), + "429": decodeError("UsageFileSearchCalls429", UsageFileSearchCalls429), + orElse: unexpectedStatus + })) + ), + "usageImages": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/images").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "sources": options.params["sources"] as any, + "sizes": options.params["sizes"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageImages200), + "400": decodeError("UsageImages400", UsageImages400), + "403": decodeError("UsageImages403", UsageImages403), + "429": decodeError("UsageImages429", UsageImages429), + orElse: unexpectedStatus + })) + ), + "usageModerations": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/moderations").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageModerations200), + "400": decodeError("UsageModerations400", UsageModerations400), + "403": decodeError("UsageModerations403", UsageModerations403), + "429": decodeError("UsageModerations429", UsageModerations429), + orElse: unexpectedStatus + })) + ), + "usageVectorStores": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/vector_stores").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageVectorStores200), + "400": decodeError("UsageVectorStores400", UsageVectorStores400), + "403": decodeError("UsageVectorStores403", UsageVectorStores403), + "429": decodeError("UsageVectorStores429", UsageVectorStores429), + orElse: unexpectedStatus + })) + ), + "usageWebSearchCalls": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/web_search_calls").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "project_ids": options.params["project_ids"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "context_levels": options.params["context_levels"] as any, + "group_by": options.params["group_by"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UsageWebSearchCalls200), + "400": decodeError("UsageWebSearchCalls400", UsageWebSearchCalls400), + "403": decodeError("UsageWebSearchCalls403", UsageWebSearchCalls403), + "429": decodeError("UsageWebSearchCalls429", UsageWebSearchCalls429), + orElse: unexpectedStatus + })) + ), + "listUsers": (options: Parameters[0]) => + HttpClientRequest.get("/organization/users").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "emails": options?.params?.["emails"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListUsers200), + "400": decodeError("ListUsers400", ListUsers400), + "403": decodeError("ListUsers403", ListUsers403), + "429": decodeError("ListUsers429", ListUsers429), + orElse: unexpectedStatus + })) + ), + "retrieveUser": (userId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [userId], () => "/organization/users/" + __encodePathParam(userId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveUser200), + "403": decodeError("RetrieveUser403", RetrieveUser403), + "404": decodeError("RetrieveUser404", RetrieveUser404), + "429": decodeError("RetrieveUser429", RetrieveUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyUser": (userId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [userId], () => "/organization/users/" + __encodePathParam(userId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyUser200), + "400": decodeError("ModifyUser400", ModifyUser400), + "403": decodeError("ModifyUser403", ModifyUser403), + "404": decodeError("ModifyUser404", ModifyUser404), + "429": decodeError("ModifyUser429", ModifyUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteUser": (userId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [userId], + () => "/organization/users/" + __encodePathParam(userId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteUser200), + "400": decodeError("DeleteUser400", DeleteUser400), + "403": decodeError("DeleteUser403", DeleteUser403), + "404": decodeError("DeleteUser404", DeleteUser404), + "429": decodeError("DeleteUser429", DeleteUser429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listUserRoleAssignments": (userId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [userId], + () => "/organization/users/" + __encodePathParam(userId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListUserRoleAssignments200), + "400": decodeError("ListUserRoleAssignments400", ListUserRoleAssignments400), + "403": decodeError("ListUserRoleAssignments403", ListUserRoleAssignments403), + "404": decodeError("ListUserRoleAssignments404", ListUserRoleAssignments404), + "429": decodeError("ListUserRoleAssignments429", ListUserRoleAssignments429), + orElse: unexpectedStatus + })) + ) + ) + ), + "assignUserRole": (userId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [userId], + () => "/organization/users/" + __encodePathParam(userId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AssignUserRole200), + "400": decodeError("AssignUserRole400", AssignUserRole400), + "403": decodeError("AssignUserRole403", AssignUserRole403), + "404": decodeError("AssignUserRole404", AssignUserRole404), + "429": decodeError("AssignUserRole429", AssignUserRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveUserRole": (userId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [userId, roleId], + () => "/organization/users/" + __encodePathParam(userId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveUserRole200), + "400": decodeError("RetrieveUserRole400", RetrieveUserRole400), + "403": decodeError("RetrieveUserRole403", RetrieveUserRole403), + "404": decodeError("RetrieveUserRole404", RetrieveUserRole404), + "429": decodeError("RetrieveUserRole429", RetrieveUserRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "unassignUserRole": (userId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [userId, roleId], + () => "/organization/users/" + __encodePathParam(userId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UnassignUserRole200), + "400": decodeError("UnassignUserRole400", UnassignUserRole400), + "403": decodeError("UnassignUserRole403", UnassignUserRole403), + "404": decodeError("UnassignUserRole404", UnassignUserRole404), + "429": decodeError("UnassignUserRole429", UnassignUserRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectGroupRoleAssignments": ( + projectId, + groupId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId, groupId], + () => "/projects/" + __encodePathParam(projectId) + "/groups/" + __encodePathParam(groupId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectGroupRoleAssignments200), + "400": decodeError("ListProjectGroupRoleAssignments400", ListProjectGroupRoleAssignments400), + "403": decodeError("ListProjectGroupRoleAssignments403", ListProjectGroupRoleAssignments403), + "404": decodeError("ListProjectGroupRoleAssignments404", ListProjectGroupRoleAssignments404), + "429": decodeError("ListProjectGroupRoleAssignments429", ListProjectGroupRoleAssignments429), + orElse: unexpectedStatus + })) + ) + ) + ), + "assignProjectGroupRole": (projectId, groupId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [projectId, groupId], + () => "/projects/" + __encodePathParam(projectId) + "/groups/" + __encodePathParam(groupId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AssignProjectGroupRole200), + "400": decodeError("AssignProjectGroupRole400", AssignProjectGroupRole400), + "403": decodeError("AssignProjectGroupRole403", AssignProjectGroupRole403), + "404": decodeError("AssignProjectGroupRole404", AssignProjectGroupRole404), + "429": decodeError("AssignProjectGroupRole429", AssignProjectGroupRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectGroupRole": ( + projectId, + groupId, + roleId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId, groupId, roleId], + () => + "/projects/" + __encodePathParam(projectId) + "/groups/" + __encodePathParam(groupId) + "/roles/" + + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectGroupRole200), + "400": decodeError("RetrieveProjectGroupRole400", RetrieveProjectGroupRole400), + "403": decodeError("RetrieveProjectGroupRole403", RetrieveProjectGroupRole403), + "404": decodeError("RetrieveProjectGroupRole404", RetrieveProjectGroupRole404), + "429": decodeError("RetrieveProjectGroupRole429", RetrieveProjectGroupRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "unassignProjectGroupRole": ( + projectId, + groupId, + roleId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, groupId, roleId], + () => + "/projects/" + __encodePathParam(projectId) + "/groups/" + __encodePathParam(groupId) + "/roles/" + + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UnassignProjectGroupRole200), + "400": decodeError("UnassignProjectGroupRole400", UnassignProjectGroupRole400), + "403": decodeError("UnassignProjectGroupRole403", UnassignProjectGroupRole403), + "404": decodeError("UnassignProjectGroupRole404", UnassignProjectGroupRole404), + "429": decodeError("UnassignProjectGroupRole429", UnassignProjectGroupRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectRoles": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/projects/" + __encodePathParam(projectId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectRoles200), + "400": decodeError("ListProjectRoles400", ListProjectRoles400), + "403": decodeError("ListProjectRoles403", ListProjectRoles403), + "404": decodeError("ListProjectRoles404", ListProjectRoles404), + "429": decodeError("ListProjectRoles429", ListProjectRoles429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createProjectRole": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/projects/" + __encodePathParam(projectId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateProjectRole200), + "400": decodeError("CreateProjectRole400", CreateProjectRole400), + "403": decodeError("CreateProjectRole403", CreateProjectRole403), + "404": decodeError("CreateProjectRole404", CreateProjectRole404), + "429": decodeError("CreateProjectRole429", CreateProjectRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectRole": (projectId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [projectId, roleId], + () => "/projects/" + __encodePathParam(projectId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectRole200), + "403": decodeError("RetrieveProjectRole403", RetrieveProjectRole403), + "404": decodeError("RetrieveProjectRole404", RetrieveProjectRole404), + "429": decodeError("RetrieveProjectRole429", RetrieveProjectRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateProjectRole": (projectId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [projectId, roleId], + () => "/projects/" + __encodePathParam(projectId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateProjectRole200), + "400": decodeError("UpdateProjectRole400", UpdateProjectRole400), + "403": decodeError("UpdateProjectRole403", UpdateProjectRole403), + "404": decodeError("UpdateProjectRole404", UpdateProjectRole404), + "429": decodeError("UpdateProjectRole429", UpdateProjectRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteProjectRole": (projectId, roleId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, roleId], + () => "/projects/" + __encodePathParam(projectId) + "/roles/" + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteProjectRole200), + "400": decodeError("DeleteProjectRole400", DeleteProjectRole400), + "403": decodeError("DeleteProjectRole403", DeleteProjectRole403), + "404": decodeError("DeleteProjectRole404", DeleteProjectRole404), + "429": decodeError("DeleteProjectRole429", DeleteProjectRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listProjectUserRoleAssignments": ( + projectId, + userId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId, userId], + () => "/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListProjectUserRoleAssignments200), + "400": decodeError("ListProjectUserRoleAssignments400", ListProjectUserRoleAssignments400), + "403": decodeError("ListProjectUserRoleAssignments403", ListProjectUserRoleAssignments403), + "404": decodeError("ListProjectUserRoleAssignments404", ListProjectUserRoleAssignments404), + "429": decodeError("ListProjectUserRoleAssignments429", ListProjectUserRoleAssignments429), + orElse: unexpectedStatus + })) + ) + ) + ), + "assignProjectUserRole": (projectId, userId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [projectId, userId], + () => "/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "/roles" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AssignProjectUserRole200), + "400": decodeError("AssignProjectUserRole400", AssignProjectUserRole400), + "403": decodeError("AssignProjectUserRole403", AssignProjectUserRole403), + "404": decodeError("AssignProjectUserRole404", AssignProjectUserRole404), + "429": decodeError("AssignProjectUserRole429", AssignProjectUserRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveProjectUserRole": ( + projectId, + userId, + roleId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.get, + [projectId, userId, roleId], + () => + "/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "/roles/" + + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveProjectUserRole200), + "400": decodeError("RetrieveProjectUserRole400", RetrieveProjectUserRole400), + "403": decodeError("RetrieveProjectUserRole403", RetrieveProjectUserRole403), + "404": decodeError("RetrieveProjectUserRole404", RetrieveProjectUserRole404), + "429": decodeError("RetrieveProjectUserRole429", RetrieveProjectUserRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "unassignProjectUserRole": ( + projectId, + userId, + roleId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.delete, + [projectId, userId, roleId], + () => + "/projects/" + __encodePathParam(projectId) + "/users/" + __encodePathParam(userId) + "/roles/" + + __encodePathParam(roleId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UnassignProjectUserRole200), + "400": decodeError("UnassignProjectUserRole400", UnassignProjectUserRole400), + "403": decodeError("UnassignProjectUserRole403", UnassignProjectUserRole403), + "404": decodeError("UnassignProjectUserRole404", UnassignProjectUserRole404), + "429": decodeError("UnassignProjectUserRole429", UnassignProjectUserRole429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createRealtimeCall": (options: Parameters[0]) => + HttpClientRequest.post("/realtime/calls").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + orElse: unexpectedStatus + })) + ), + "acceptRealtimeCall": (callId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [callId], + () => "/realtime/calls/" + __encodePathParam(callId) + "/accept" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "404": decodeError("AcceptRealtimeCall404", AcceptRealtimeCall404), + "409": decodeError("AcceptRealtimeCall409", AcceptRealtimeCall409), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "hangupRealtimeCall": (callId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [callId], + () => "/realtime/calls/" + __encodePathParam(callId) + "/hangup" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "404": decodeError("HangupRealtimeCall404", HangupRealtimeCall404), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "referRealtimeCall": (callId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [callId], + () => "/realtime/calls/" + __encodePathParam(callId) + "/refer" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "404": decodeError("ReferRealtimeCall404", ReferRealtimeCall404), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "rejectRealtimeCall": (callId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [callId], + () => "/realtime/calls/" + __encodePathParam(callId) + "/reject" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "404": decodeError("RejectRealtimeCall404", RejectRealtimeCall404), + "409": decodeError("RejectRealtimeCall409", RejectRealtimeCall409), + "200": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "createRealtimeClientSecret": (options: Parameters[0]) => + HttpClientRequest.post("/realtime/client_secrets").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateRealtimeClientSecret200), + "400": decodeError("CreateRealtimeClientSecret400", CreateRealtimeClientSecret400), + "404": decodeError("CreateRealtimeClientSecret404", CreateRealtimeClientSecret404), + "500": decodeError("CreateRealtimeClientSecret500", CreateRealtimeClientSecret500), + orElse: unexpectedStatus + })) + ), + "createRealtimeSession": (options: Parameters[0]) => + HttpClientRequest.post("/realtime/sessions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateRealtimeSession200), + orElse: unexpectedStatus + })) + ), + "createRealtimeTranscriptionSession": ( + options: Parameters[0] + ) => + HttpClientRequest.post("/realtime/transcription_sessions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateRealtimeTranscriptionSession200), + orElse: unexpectedStatus + })) + ), + "createRealtimeTranslationClientSecret": ( + options: Parameters[0] + ) => + HttpClientRequest.post("/realtime/translations/client_secrets").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateRealtimeTranslationClientSecret200), + "400": decodeError("CreateRealtimeTranslationClientSecret400", CreateRealtimeTranslationClientSecret400), + "404": decodeError("CreateRealtimeTranslationClientSecret404", CreateRealtimeTranslationClientSecret404), + "500": decodeError("CreateRealtimeTranslationClientSecret500", CreateRealtimeTranslationClientSecret500), + orElse: unexpectedStatus + })) + ), + "createResponse": (options: Parameters[0]) => + HttpClientRequest.post("/responses").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateResponse200), + "400": decodeError("CreateResponse400", CreateResponse400), + "404": decodeError("CreateResponse404", CreateResponse404), + "429": decodeError("CreateResponse429", CreateResponse429), + "503": decodeError("CreateResponse503", CreateResponse503), + orElse: unexpectedStatus + })) + ), + "createResponseSse": (options) => + HttpClientRequest.post("/responses").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateResponse200Sse) + ), + "getResponse": (responseId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [responseId], () => "/responses/" + __encodePathParam(responseId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "include": options?.params?.["include"] as any, + "stream": options?.params?.["stream"] as any, + "starting_after": options?.params?.["starting_after"] as any, + "include_obfuscation": options?.params?.["include_obfuscation"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetResponse200), + "404": decodeError("GetResponse404", GetResponse404), + "429": decodeError("GetResponse429", GetResponse429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteResponse": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteResponse200), + "404": decodeError("DeleteResponse404", DeleteResponse404), + "429": decodeError("DeleteResponse429", DeleteResponse429), + orElse: unexpectedStatus + })) + ) + ) + ), + "cancelResponse": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "/cancel" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelResponse200), + "404": decodeError("CancelResponse404", CancelResponse404), + "429": decodeError("CancelResponse429", CancelResponse429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listInputItems": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "/input_items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "include": options?.params?.["include"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListInputItems200), + "404": decodeError("ListInputItems404", ListInputItems404), + "429": decodeError("ListInputItems429", ListInputItems429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createThread": (options: Parameters[0]) => + HttpClientRequest.post("/threads").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateThread200), + "429": decodeError("CreateThread429", CreateThread429), + orElse: unexpectedStatus + })) + ), + "createThreadAndRun": (options: Parameters[0]) => + HttpClientRequest.post("/threads/runs").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateThreadAndRun200), + "429": decodeError("CreateThreadAndRun429", CreateThreadAndRun429), + orElse: unexpectedStatus + })) + ), + "createThreadAndRunSse": (options) => + HttpClientRequest.post("/threads/runs").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateThreadAndRun200Sse) + ), + "getThread": (threadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [threadId], () => "/threads/" + __encodePathParam(threadId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetThread200), + "429": decodeError("GetThread429", GetThread429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyThread": (threadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [threadId], () => "/threads/" + __encodePathParam(threadId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyThread200), + "429": decodeError("ModifyThread429", ModifyThread429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteThread": (threadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [threadId], () => "/threads/" + __encodePathParam(threadId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteThread200), + "429": decodeError("DeleteThread429", DeleteThread429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listMessages": (threadId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [threadId], + () => "/threads/" + __encodePathParam(threadId) + "/messages" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any, + "run_id": options?.params?.["run_id"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListMessages200), + "429": decodeError("ListMessages429", ListMessages429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createMessage": (threadId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [threadId], + () => "/threads/" + __encodePathParam(threadId) + "/messages" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateMessage200), + "429": decodeError("CreateMessage429", CreateMessage429), + orElse: unexpectedStatus + })) + ) + ) + ), + "getMessage": (threadId, messageId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [threadId, messageId], + () => "/threads/" + __encodePathParam(threadId) + "/messages/" + __encodePathParam(messageId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetMessage200), + "429": decodeError("GetMessage429", GetMessage429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyMessage": (threadId, messageId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [threadId, messageId], + () => "/threads/" + __encodePathParam(threadId) + "/messages/" + __encodePathParam(messageId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyMessage200), + "429": decodeError("ModifyMessage429", ModifyMessage429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteMessage": (threadId, messageId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [threadId, messageId], + () => "/threads/" + __encodePathParam(threadId) + "/messages/" + __encodePathParam(messageId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteMessage200), + "429": decodeError("DeleteMessage429", DeleteMessage429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listRuns": (threadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [threadId], () => "/threads/" + __encodePathParam(threadId) + "/runs") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListRuns200), + "429": decodeError("ListRuns429", ListRuns429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createRun": (threadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [threadId], () => "/threads/" + __encodePathParam(threadId) + "/runs") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "include[]": options.params?.["include[]"] as any }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateRun200), + "429": decodeError("CreateRun429", CreateRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createRunSse": (threadId, options) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.post, [threadId], () => "/threads/" + __encodePathParam(threadId) + "/runs") + .pipe( + Effect.map((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "include[]": options.params?.["include[]"] as any }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateRun200Sse) + ) + ) + ) + ), + "getRun": (threadId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [threadId, runId], + () => "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetRun200), + "429": decodeError("GetRun429", GetRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyRun": (threadId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [threadId, runId], + () => "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyRun200), + "429": decodeError("ModifyRun429", ModifyRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "cancelRun": (threadId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [threadId, runId], + () => "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "/cancel" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelRun200), + "429": decodeError("CancelRun429", CancelRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listRunSteps": (threadId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [threadId, runId], + () => "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "/steps" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any, + "include[]": options?.params?.["include[]"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListRunSteps200), + "429": decodeError("ListRunSteps429", ListRunSteps429), + orElse: unexpectedStatus + })) + ) + ) + ), + "getRunStep": (threadId, runId, stepId, options: Parameters[3]) => + __makePathRequest( + HttpClientRequest.get, + [threadId, runId, stepId], + () => + "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "/steps/" + + __encodePathParam(stepId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "include[]": options?.params?.["include[]"] as any }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetRunStep200), + "429": decodeError("GetRunStep429", GetRunStep429), + orElse: unexpectedStatus + })) + ) + ) + ), + "submitToolOuputsToRun": (threadId, runId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [threadId, runId], + () => "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "/submit_tool_outputs" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(SubmitToolOuputsToRun200), + "429": decodeError("SubmitToolOuputsToRun429", SubmitToolOuputsToRun429), + orElse: unexpectedStatus + })) + ) + ) + ), + "submitToolOuputsToRunSse": (threadId, runId, options) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.post, [threadId, runId], () => + "/threads/" + __encodePathParam(threadId) + "/runs/" + __encodePathParam(runId) + "/submit_tool_outputs") + .pipe( + Effect.map((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(SubmitToolOuputsToRun200Sse) + ) + ) + ) + ), + "createUpload": (options: Parameters[0]) => + HttpClientRequest.post("/uploads").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateUpload200), + "400": decodeError("CreateUpload400", CreateUpload400), + "429": decodeError("CreateUpload429", CreateUpload429), + orElse: unexpectedStatus + })) + ), + "cancelUpload": (uploadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [uploadId], () => "/uploads/" + __encodePathParam(uploadId) + "/cancel") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelUpload200), + "400": decodeError("CancelUpload400", CancelUpload400), + "404": decodeError("CancelUpload404", CancelUpload404), + "410": decodeError("CancelUpload410", CancelUpload410), + "429": decodeError("CancelUpload429", CancelUpload429), + orElse: unexpectedStatus + })) + ) + ) + ), + "completeUpload": (uploadId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [uploadId], + () => "/uploads/" + __encodePathParam(uploadId) + "/complete" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CompleteUpload200), + "400": decodeError("CompleteUpload400", CompleteUpload400), + "404": decodeError("CompleteUpload404", CompleteUpload404), + "410": decodeError("CompleteUpload410", CompleteUpload410), + "429": decodeError("CompleteUpload429", CompleteUpload429), + "500": decodeError("CompleteUpload500", CompleteUpload500), + orElse: unexpectedStatus + })) + ) + ) + ), + "addUploadPart": (uploadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [uploadId], () => "/uploads/" + __encodePathParam(uploadId) + "/parts") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(AddUploadPart200), + "400": decodeError("AddUploadPart400", AddUploadPart400), + "404": decodeError("AddUploadPart404", AddUploadPart404), + "410": decodeError("AddUploadPart410", AddUploadPart410), + "429": decodeError("AddUploadPart429", AddUploadPart429), + "500": decodeError("AddUploadPart500", AddUploadPart500), + orElse: unexpectedStatus + })) + ) + ) + ), + "listVectorStores": (options: Parameters[0]) => + HttpClientRequest.get("/vector_stores").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListVectorStores200), + "429": decodeError("ListVectorStores429", ListVectorStores429), + "503": decodeError("ListVectorStores503", ListVectorStores503), + orElse: unexpectedStatus + })) + ), + "createVectorStore": (options: Parameters[0]) => + HttpClientRequest.post("/vector_stores").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVectorStore200), + "400": decodeError("CreateVectorStore400", CreateVectorStore400), + "404": decodeError("CreateVectorStore404", CreateVectorStore404), + "429": decodeError("CreateVectorStore429", CreateVectorStore429), + orElse: unexpectedStatus + })) + ), + "getVectorStore": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetVectorStore200), + "404": decodeError("GetVectorStore404", GetVectorStore404), + "429": decodeError("GetVectorStore429", GetVectorStore429), + orElse: unexpectedStatus + })) + ) + ) + ), + "modifyVectorStore": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ModifyVectorStore200), + "400": decodeError("ModifyVectorStore400", ModifyVectorStore400), + "403": decodeError("ModifyVectorStore403", ModifyVectorStore403), + "404": decodeError("ModifyVectorStore404", ModifyVectorStore404), + "429": decodeError("ModifyVectorStore429", ModifyVectorStore429), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteVectorStore": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteVectorStore200), + "404": decodeError("DeleteVectorStore404", DeleteVectorStore404), + "409": decodeError("DeleteVectorStore409", DeleteVectorStore409), + "429": decodeError("DeleteVectorStore429", DeleteVectorStore429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createVectorStoreFileBatch": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/file_batches" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVectorStoreFileBatch200), + "400": decodeError("CreateVectorStoreFileBatch400", CreateVectorStoreFileBatch400), + "404": decodeError("CreateVectorStoreFileBatch404", CreateVectorStoreFileBatch404), + "409": decodeError("CreateVectorStoreFileBatch409", CreateVectorStoreFileBatch409), + "429": decodeError("CreateVectorStoreFileBatch429", CreateVectorStoreFileBatch429), + "500": decodeError("CreateVectorStoreFileBatch500", CreateVectorStoreFileBatch500), + orElse: unexpectedStatus + })) + ) + ) + ), + "getVectorStoreFileBatch": ( + vectorStoreId, + batchId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [vectorStoreId, batchId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/file_batches/" + __encodePathParam(batchId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetVectorStoreFileBatch200), + "400": decodeError("GetVectorStoreFileBatch400", GetVectorStoreFileBatch400), + "404": decodeError("GetVectorStoreFileBatch404", GetVectorStoreFileBatch404), + "429": decodeError("GetVectorStoreFileBatch429", GetVectorStoreFileBatch429), + orElse: unexpectedStatus + })) + ) + ) + ), + "cancelVectorStoreFileBatch": ( + vectorStoreId, + batchId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.post, + [vectorStoreId, batchId], + () => + "/vector_stores/" + __encodePathParam(vectorStoreId) + "/file_batches/" + __encodePathParam(batchId) + + "/cancel" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelVectorStoreFileBatch200), + "400": decodeError("CancelVectorStoreFileBatch400", CancelVectorStoreFileBatch400), + "404": decodeError("CancelVectorStoreFileBatch404", CancelVectorStoreFileBatch404), + "409": decodeError("CancelVectorStoreFileBatch409", CancelVectorStoreFileBatch409), + "429": decodeError("CancelVectorStoreFileBatch429", CancelVectorStoreFileBatch429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listFilesInVectorStoreBatch": ( + vectorStoreId, + batchId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [vectorStoreId, batchId], + () => + "/vector_stores/" + __encodePathParam(vectorStoreId) + "/file_batches/" + __encodePathParam(batchId) + + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any, + "filter": options?.params?.["filter"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListFilesInVectorStoreBatch200), + "400": decodeError("ListFilesInVectorStoreBatch400", ListFilesInVectorStoreBatch400), + "404": decodeError("ListFilesInVectorStoreBatch404", ListFilesInVectorStoreBatch404), + "429": decodeError("ListFilesInVectorStoreBatch429", ListFilesInVectorStoreBatch429), + orElse: unexpectedStatus + })) + ) + ) + ), + "listVectorStoreFiles": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any, + "filter": options?.params?.["filter"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListVectorStoreFiles200), + "400": decodeError("ListVectorStoreFiles400", ListVectorStoreFiles400), + "404": decodeError("ListVectorStoreFiles404", ListVectorStoreFiles404), + "429": decodeError("ListVectorStoreFiles429", ListVectorStoreFiles429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createVectorStoreFile": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVectorStoreFile200), + "400": decodeError("CreateVectorStoreFile400", CreateVectorStoreFile400), + "404": decodeError("CreateVectorStoreFile404", CreateVectorStoreFile404), + "409": decodeError("CreateVectorStoreFile409", CreateVectorStoreFile409), + "429": decodeError("CreateVectorStoreFile429", CreateVectorStoreFile429), + "500": decodeError("CreateVectorStoreFile500", CreateVectorStoreFile500), + orElse: unexpectedStatus + })) + ) + ) + ), + "getVectorStoreFile": (vectorStoreId, fileId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [vectorStoreId, fileId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/files/" + __encodePathParam(fileId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetVectorStoreFile200), + "404": decodeError("GetVectorStoreFile404", GetVectorStoreFile404), + "429": decodeError("GetVectorStoreFile429", GetVectorStoreFile429), + "500": decodeError("GetVectorStoreFile500", GetVectorStoreFile500), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateVectorStoreFileAttributes": ( + vectorStoreId, + fileId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.post, + [vectorStoreId, fileId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/files/" + __encodePathParam(fileId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateVectorStoreFileAttributes200), + "400": decodeError("UpdateVectorStoreFileAttributes400", UpdateVectorStoreFileAttributes400), + "404": decodeError("UpdateVectorStoreFileAttributes404", UpdateVectorStoreFileAttributes404), + "409": decodeError("UpdateVectorStoreFileAttributes409", UpdateVectorStoreFileAttributes409), + "429": decodeError("UpdateVectorStoreFileAttributes429", UpdateVectorStoreFileAttributes429), + "500": decodeError("UpdateVectorStoreFileAttributes500", UpdateVectorStoreFileAttributes500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteVectorStoreFile": (vectorStoreId, fileId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [vectorStoreId, fileId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/files/" + __encodePathParam(fileId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteVectorStoreFile200), + "400": decodeError("DeleteVectorStoreFile400", DeleteVectorStoreFile400), + "404": decodeError("DeleteVectorStoreFile404", DeleteVectorStoreFile404), + "409": decodeError("DeleteVectorStoreFile409", DeleteVectorStoreFile409), + "429": decodeError("DeleteVectorStoreFile429", DeleteVectorStoreFile429), + "500": decodeError("DeleteVectorStoreFile500", DeleteVectorStoreFile500), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveVectorStoreFileContent": ( + vectorStoreId, + fileId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [vectorStoreId, fileId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/files/" + __encodePathParam(fileId) + "/content" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveVectorStoreFileContent200), + "400": decodeError("RetrieveVectorStoreFileContent400", RetrieveVectorStoreFileContent400), + "404": decodeError("RetrieveVectorStoreFileContent404", RetrieveVectorStoreFileContent404), + "429": decodeError("RetrieveVectorStoreFileContent429", RetrieveVectorStoreFileContent429), + "500": decodeError("RetrieveVectorStoreFileContent500", RetrieveVectorStoreFileContent500), + orElse: unexpectedStatus + })) + ) + ) + ), + "searchVectorStore": (vectorStoreId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [vectorStoreId], + () => "/vector_stores/" + __encodePathParam(vectorStoreId) + "/search" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(SearchVectorStore200), + "400": decodeError("SearchVectorStore400", SearchVectorStore400), + "404": decodeError("SearchVectorStore404", SearchVectorStore404), + "429": decodeError("SearchVectorStore429", SearchVectorStore429), + orElse: unexpectedStatus + })) + ) + ) + ), + "createConversation": (options: Parameters[0]) => + HttpClientRequest.post("/conversations").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateConversation200), + "400": decodeError("CreateConversation400", CreateConversation400), + "429": decodeError("CreateConversation429", CreateConversation429), + "500": decodeError("CreateConversation500", CreateConversation500), + orElse: unexpectedStatus + })) + ), + "getConversation": (conversationId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [conversationId], + () => "/conversations/" + __encodePathParam(conversationId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetConversation200), + "404": decodeError("GetConversation404", GetConversation404), + "429": decodeError("GetConversation429", GetConversation429), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateConversation": (conversationId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [conversationId], + () => "/conversations/" + __encodePathParam(conversationId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateConversation200), + "400": decodeError("UpdateConversation400", UpdateConversation400), + "404": decodeError("UpdateConversation404", UpdateConversation404), + "429": decodeError("UpdateConversation429", UpdateConversation429), + "500": decodeError("UpdateConversation500", UpdateConversation500), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteConversation": (conversationId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [conversationId], + () => "/conversations/" + __encodePathParam(conversationId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteConversation200), + "404": decodeError("DeleteConversation404", DeleteConversation404), + "429": decodeError("DeleteConversation429", DeleteConversation429), + orElse: unexpectedStatus + })) + ) + ) + ), + "Getsafetycase": (id, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [id], () => "/safety/cases/" + __encodePathParam(id) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Getsafetycase200), + "400": decodeError("Getsafetycase400", Getsafetycase400), + "404": decodeError("Getsafetycase404", Getsafetycase404), + "429": decodeError("Getsafetycase429", Getsafetycase429), + "500": decodeError("Getsafetycase500", Getsafetycase500), + "503": decodeError("Getsafetycase503", Getsafetycase503), + orElse: unexpectedStatus + })) + ) + ) + ), + "Getprojectsafetyalert": (id, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [id], () => "/safety/alerts/" + __encodePathParam(id) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Getprojectsafetyalert200), + "400": decodeError("Getprojectsafetyalert400", Getprojectsafetyalert400), + "404": decodeError("Getprojectsafetyalert404", Getprojectsafetyalert404), + "429": decodeError("Getprojectsafetyalert429", Getprojectsafetyalert429), + "503": decodeError("Getprojectsafetyalert503", Getprojectsafetyalert503), + orElse: unexpectedStatus + })) + ) + ) + ), + "downloadLiveRecording": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/live/sessions/" + __encodePathParam(sessionId) + "/content" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "400": decodeError("DownloadLiveRecording400", DownloadLiveRecording400), + "404": decodeError("DownloadLiveRecording404", DownloadLiveRecording404), + "429": decodeError("DownloadLiveRecording429", DownloadLiveRecording429), + "500": decodeError("DownloadLiveRecording500", DownloadLiveRecording500), + "503": decodeError("DownloadLiveRecording503", DownloadLiveRecording503), + orElse: unexpectedStatus + })) + ) + ) + ), + "downloadLiveRecordingStream": (sessionId) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [sessionId], () => + "/live/sessions/" + __encodePathParam(sessionId) + "/content").pipe( + Effect.map((request) => + request.pipe( + binaryRequest + ) + ) + ) + ), + "OrganizationDecisionsusage": (options: Parameters[0]) => + HttpClientRequest.get("/organization/usage/decisions").pipe( + HttpClientRequest.setUrlParams({ + "start_time": options.params["start_time"] as any, + "end_time": options.params["end_time"] as any, + "bucket_width": options.params["bucket_width"] as any, + "limit": options.params["limit"] as any, + "page": options.params["page"] as any, + "project_ids": options.params["project_ids"] as any, + "group_by": options.params["group_by"] as any, + "user_ids": options.params["user_ids"] as any, + "api_key_ids": options.params["api_key_ids"] as any, + "models": options.params["models"] as any, + "batch": options.params["batch"] as any + }), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(OrganizationDecisionsusage200), + "400": decodeError("OrganizationDecisionsusage400", OrganizationDecisionsusage400), + "403": decodeError("OrganizationDecisionsusage403", OrganizationDecisionsusage403), + "429": decodeError("OrganizationDecisionsusage429", OrganizationDecisionsusage429), + orElse: unexpectedStatus + })) + ), + "Listexternalstorageconfigurations": (options: Parameters[0]) => + HttpClientRequest.get("/organization/external_storage").pipe( + HttpClientRequest.setUrlParams({ + "project_id": options?.params?.["project_id"] as any, + "after": options?.params?.["after"] as any, + "order": options?.params?.["order"] as any, + "limit": options?.params?.["limit"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Listexternalstorageconfigurations200), + "400": decodeError("Listexternalstorageconfigurations400", Listexternalstorageconfigurations400), + "403": decodeError("Listexternalstorageconfigurations403", Listexternalstorageconfigurations403), + "404": decodeError("Listexternalstorageconfigurations404", Listexternalstorageconfigurations404), + orElse: unexpectedStatus + })) + ), + "Createanexternalstorageconfiguration": ( + options: Parameters[0] + ) => + HttpClientRequest.post("/organization/external_storage").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Createanexternalstorageconfiguration200), + "400": decodeError("Createanexternalstorageconfiguration400", Createanexternalstorageconfiguration400), + "403": decodeError("Createanexternalstorageconfiguration403", Createanexternalstorageconfiguration403), + "404": decodeError("Createanexternalstorageconfiguration404", Createanexternalstorageconfiguration404), + "503": decodeError("Createanexternalstorageconfiguration503", Createanexternalstorageconfiguration503), + orElse: unexpectedStatus + })) + ), + "Getanexternalstorageconfiguration": ( + externalStorageId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [externalStorageId], + () => "/organization/external_storage/" + __encodePathParam(externalStorageId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Getanexternalstorageconfiguration200), + "400": decodeError("Getanexternalstorageconfiguration400", Getanexternalstorageconfiguration400), + "403": decodeError("Getanexternalstorageconfiguration403", Getanexternalstorageconfiguration403), + "404": decodeError("Getanexternalstorageconfiguration404", Getanexternalstorageconfiguration404), + orElse: unexpectedStatus + })) + ) + ) + ), + "Deleteanexternalstorageconfiguration": ( + externalStorageId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.delete, + [externalStorageId], + () => "/organization/external_storage/" + __encodePathParam(externalStorageId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Deleteanexternalstorageconfiguration200), + "400": decodeError("Deleteanexternalstorageconfiguration400", Deleteanexternalstorageconfiguration400), + "403": decodeError("Deleteanexternalstorageconfiguration403", Deleteanexternalstorageconfiguration403), + "404": decodeError("Deleteanexternalstorageconfiguration404", Deleteanexternalstorageconfiguration404), + orElse: unexpectedStatus + })) + ) + ) + ), + "Validateanexternalstorageconfiguration": ( + externalStorageId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [externalStorageId], + () => "/organization/external_storage/" + __encodePathParam(externalStorageId) + "/validate" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Validateanexternalstorageconfiguration200), + "400": decodeError( + "Validateanexternalstorageconfiguration400", + Validateanexternalstorageconfiguration400 + ), + "403": decodeError( + "Validateanexternalstorageconfiguration403", + Validateanexternalstorageconfiguration403 + ), + "404": decodeError( + "Validateanexternalstorageconfiguration404", + Validateanexternalstorageconfiguration404 + ), + "503": decodeError( + "Validateanexternalstorageconfiguration503", + Validateanexternalstorageconfiguration503 + ), + orElse: unexpectedStatus + })) + ) + ) + ), + "Getorganizationspendlimit": (options: Parameters[0]) => + HttpClientRequest.get("/organization/spend_limit").pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Getorganizationspendlimit200), + "403": decodeError("Getorganizationspendlimit403", Getorganizationspendlimit403), + "404": decodeError("Getorganizationspendlimit404", Getorganizationspendlimit404), + orElse: unexpectedStatus + })) + ), + "Updateorganizationspendlimit": (options: Parameters[0]) => + HttpClientRequest.post("/organization/spend_limit").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Updateorganizationspendlimit200), + "400": decodeError("Updateorganizationspendlimit400", Updateorganizationspendlimit400), + "403": decodeError("Updateorganizationspendlimit403", Updateorganizationspendlimit403), + orElse: unexpectedStatus + })) + ), + "Deleteorganizationspendlimit": (options: Parameters[0]) => + HttpClientRequest.delete("/organization/spend_limit").pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Deleteorganizationspendlimit200), + "403": decodeError("Deleteorganizationspendlimit403", Deleteorganizationspendlimit403), + "404": decodeError("Deleteorganizationspendlimit404", Deleteorganizationspendlimit404), + orElse: unexpectedStatus + })) + ), + "Getprojectspendlimit": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/spend_limit" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Getprojectspendlimit200), + "400": decodeError("Getprojectspendlimit400", Getprojectspendlimit400), + "403": decodeError("Getprojectspendlimit403", Getprojectspendlimit403), + "404": decodeError("Getprojectspendlimit404", Getprojectspendlimit404), + orElse: unexpectedStatus + })) + ) + ) + ), + "Updateprojectspendlimit": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/spend_limit" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Updateprojectspendlimit200), + "400": decodeError("Updateprojectspendlimit400", Updateprojectspendlimit400), + "403": decodeError("Updateprojectspendlimit403", Updateprojectspendlimit403), + "404": decodeError("Updateprojectspendlimit404", Updateprojectspendlimit404), + orElse: unexpectedStatus + })) + ) + ) + ), + "Deleteprojectspendlimit": (projectId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [projectId], + () => "/organization/projects/" + __encodePathParam(projectId) + "/spend_limit" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Deleteprojectspendlimit200), + "400": decodeError("Deleteprojectspendlimit400", Deleteprojectspendlimit400), + "403": decodeError("Deleteprojectspendlimit403", Deleteprojectspendlimit403), + "404": decodeError("Deleteprojectspendlimit404", Deleteprojectspendlimit404), + orElse: unexpectedStatus + })) + ) + ) + ), + "CreateanAPIkeyforaserviceaccount": ( + projectId, + serviceAccountId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.post, + [projectId, serviceAccountId], + () => + "/organization/projects/" + __encodePathParam(projectId) + "/service_accounts/" + + __encodePathParam(serviceAccountId) + "/api_keys" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateanAPIkeyforaserviceaccount200), + "400": decodeError("CreateanAPIkeyforaserviceaccount400", CreateanAPIkeyforaserviceaccount400), + "403": decodeError("CreateanAPIkeyforaserviceaccount403", CreateanAPIkeyforaserviceaccount403), + "404": decodeError("CreateanAPIkeyforaserviceaccount404", CreateanAPIkeyforaserviceaccount404), + "409": decodeError("CreateanAPIkeyforaserviceaccount409", CreateanAPIkeyforaserviceaccount409), + "429": decodeError("CreateanAPIkeyforaserviceaccount429", CreateanAPIkeyforaserviceaccount429), + orElse: unexpectedStatus + })) + ) + ) + ), + "Createcontentprovenancecheck": (options: Parameters[0]) => + HttpClientRequest.post("/content_provenance_checks").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Createcontentprovenancecheck200), + "400": decodeError("Createcontentprovenancecheck400", Createcontentprovenancecheck400), + "404": decodeError("Createcontentprovenancecheck404", Createcontentprovenancecheck404), + "429": decodeError("Createcontentprovenancecheck429", Createcontentprovenancecheck429), + "500": decodeError("Createcontentprovenancecheck500", Createcontentprovenancecheck500), + "503": decodeError("Createcontentprovenancecheck503", Createcontentprovenancecheck503), + orElse: unexpectedStatus + })) + ), + "ListVideos": (options: Parameters[0]) => + HttpClientRequest.get("/videos").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListVideos200), + "400": decodeError("ListVideos400", ListVideos400), + "404": decodeError("ListVideos404", ListVideos404), + orElse: unexpectedStatus + })) + ), + "createVideo": (options: Parameters[0]) => + HttpClientRequest.post("/videos").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVideo200), + "400": decodeError("CreateVideo400", CreateVideo400), + "404": decodeError("CreateVideo404", CreateVideo404), + "429": decodeError("CreateVideo429", CreateVideo429), + "500": decodeError("CreateVideo500", CreateVideo500), + "503": decodeError("CreateVideo503", CreateVideo503), + orElse: unexpectedStatus + })) + ), + "CreateVideoCharacter": (options: Parameters[0]) => + HttpClientRequest.post("/videos/characters").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVideoCharacter200), + "400": decodeError("CreateVideoCharacter400", CreateVideoCharacter400), + "404": decodeError("CreateVideoCharacter404", CreateVideoCharacter404), + "500": decodeError("CreateVideoCharacter500", CreateVideoCharacter500), + "504": decodeError("CreateVideoCharacter504", CreateVideoCharacter504), + orElse: unexpectedStatus + })) + ), + "GetVideoCharacter": (characterId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [characterId], + () => "/videos/characters/" + __encodePathParam(characterId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetVideoCharacter200), + "400": decodeError("GetVideoCharacter400", GetVideoCharacter400), + "404": decodeError("GetVideoCharacter404", GetVideoCharacter404), + orElse: unexpectedStatus + })) + ) + ) + ), + "CreateVideoEdit": (options: Parameters[0]) => + HttpClientRequest.post("/videos/edits").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVideoEdit200), + "400": decodeError("CreateVideoEdit400", CreateVideoEdit400), + "404": decodeError("CreateVideoEdit404", CreateVideoEdit404), + "429": decodeError("CreateVideoEdit429", CreateVideoEdit429), + "500": decodeError("CreateVideoEdit500", CreateVideoEdit500), + "503": decodeError("CreateVideoEdit503", CreateVideoEdit503), + orElse: unexpectedStatus + })) + ), + "CreateVideoExtend": (options: Parameters[0]) => + HttpClientRequest.post("/videos/extensions").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVideoExtend200), + "400": decodeError("CreateVideoExtend400", CreateVideoExtend400), + "404": decodeError("CreateVideoExtend404", CreateVideoExtend404), + "429": decodeError("CreateVideoExtend429", CreateVideoExtend429), + "500": decodeError("CreateVideoExtend500", CreateVideoExtend500), + "503": decodeError("CreateVideoExtend503", CreateVideoExtend503), + orElse: unexpectedStatus + })) + ), + "GetVideo": (videoId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [videoId], () => "/videos/" + __encodePathParam(videoId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetVideo200), + "400": decodeError("GetVideo400", GetVideo400), + "404": decodeError("GetVideo404", GetVideo404), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteVideo": (videoId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [videoId], () => "/videos/" + __encodePathParam(videoId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteVideo200), + "400": decodeError("DeleteVideo400", DeleteVideo400), + "404": decodeError("DeleteVideo404", DeleteVideo404), + "500": decodeError("DeleteVideo500", DeleteVideo500), + orElse: unexpectedStatus + })) + ) + ) + ), + "RetrieveVideoContent": (videoId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [videoId], () => "/videos/" + __encodePathParam(videoId) + "/content") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "variant": options?.params?.["variant"] as any }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "400": decodeError("RetrieveVideoContent400", RetrieveVideoContent400), + "404": decodeError("RetrieveVideoContent404", RetrieveVideoContent404), + "500": decodeError("RetrieveVideoContent500", RetrieveVideoContent500), + orElse: unexpectedStatus + })) + ) + ) + ), + "RetrieveVideoContentStream": (videoId, options) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [videoId], () => "/videos/" + __encodePathParam(videoId) + "/content") + .pipe( + Effect.map((request) => + request.pipe( + HttpClientRequest.setUrlParams({ "variant": options?.params?.["variant"] as any }), + binaryRequest + ) + ) + ) + ), + "CreateVideoRemix": (videoId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [videoId], () => "/videos/" + __encodePathParam(videoId) + "/remix") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVideoRemix200), + "400": decodeError("CreateVideoRemix400", CreateVideoRemix400), + "404": decodeError("CreateVideoRemix404", CreateVideoRemix404), + "429": decodeError("CreateVideoRemix429", CreateVideoRemix429), + "500": decodeError("CreateVideoRemix500", CreateVideoRemix500), + "503": decodeError("CreateVideoRemix503", CreateVideoRemix503), + orElse: unexpectedStatus + })) + ) + ) + ), + "Getinputtokencounts": (options: Parameters[0]) => + HttpClientRequest.post("/responses/input_tokens").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Getinputtokencounts200), + "400": decodeError("Getinputtokencounts400", Getinputtokencounts400), + "429": decodeError("Getinputtokencounts429", Getinputtokencounts429), + orElse: unexpectedStatus + })) + ), + "Compactconversation": (options: Parameters[0]) => + HttpClientRequest.post("/responses/compact").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(Compactconversation200), + "400": decodeError("Compactconversation400", Compactconversation400), + "429": decodeError("Compactconversation429", Compactconversation429), + "503": decodeError("Compactconversation503", Compactconversation503), + orElse: unexpectedStatus + })) + ), + "ListSkills": (options: Parameters[0]) => + HttpClientRequest.get("/skills").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListSkills200), + "404": decodeError("ListSkills404", ListSkills404), + "429": decodeError("ListSkills429", ListSkills429), + orElse: unexpectedStatus + })) + ), + "CreateSkill": (options: Parameters[0]) => + HttpClientRequest.post("/skills").pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateSkill200), + "400": decodeError("CreateSkill400", CreateSkill400), + "429": decodeError("CreateSkill429", CreateSkill429), + "500": decodeError("CreateSkill500", CreateSkill500), + orElse: unexpectedStatus + })) + ), + "GetSkill": (skillId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [skillId], () => "/skills/" + __encodePathParam(skillId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetSkill200), + "404": decodeError("GetSkill404", GetSkill404), + "429": decodeError("GetSkill429", GetSkill429), + orElse: unexpectedStatus + })) + ) + ) + ), + "UpdateSkillDefaultVersion": (skillId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [skillId], () => "/skills/" + __encodePathParam(skillId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateSkillDefaultVersion200), + "400": decodeError("UpdateSkillDefaultVersion400", UpdateSkillDefaultVersion400), + "404": decodeError("UpdateSkillDefaultVersion404", UpdateSkillDefaultVersion404), + "429": decodeError("UpdateSkillDefaultVersion429", UpdateSkillDefaultVersion429), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteSkill": (skillId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [skillId], () => "/skills/" + __encodePathParam(skillId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteSkill200), + "404": decodeError("DeleteSkill404", DeleteSkill404), + "429": decodeError("DeleteSkill429", DeleteSkill429), + orElse: unexpectedStatus + })) + ) + ) + ), + "GetSkillContent": (skillId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [skillId], () => "/skills/" + __encodePathParam(skillId) + "/content") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "404": decodeError("GetSkillContent404", GetSkillContent404), + "429": decodeError("GetSkillContent429", GetSkillContent429), + orElse: unexpectedStatus + })) + ) + ) + ), + "GetSkillContentStream": (skillId) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [skillId], () => "/skills/" + __encodePathParam(skillId) + "/content") + .pipe( + Effect.map((request) => + request.pipe( + binaryRequest + ) + ) + ) + ), + "ListSkillVersions": (skillId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [skillId], () => "/skills/" + __encodePathParam(skillId) + "/versions") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListSkillVersions200), + "400": decodeError("ListSkillVersions400", ListSkillVersions400), + "404": decodeError("ListSkillVersions404", ListSkillVersions404), + "429": decodeError("ListSkillVersions429", ListSkillVersions429), + orElse: unexpectedStatus + })) + ) + ) + ), + "CreateSkillVersion": (skillId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [skillId], () => "/skills/" + __encodePathParam(skillId) + "/versions") + .pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyFormDataRecord(options.payload as any), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateSkillVersion200), + "400": decodeError("CreateSkillVersion400", CreateSkillVersion400), + "404": decodeError("CreateSkillVersion404", CreateSkillVersion404), + "429": decodeError("CreateSkillVersion429", CreateSkillVersion429), + "500": decodeError("CreateSkillVersion500", CreateSkillVersion500), + orElse: unexpectedStatus + })) + ) + ) + ), + "GetSkillVersion": (skillId, version, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [skillId, version], + () => "/skills/" + __encodePathParam(skillId) + "/versions/" + __encodePathParam(version) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetSkillVersion200), + "400": decodeError("GetSkillVersion400", GetSkillVersion400), + "404": decodeError("GetSkillVersion404", GetSkillVersion404), + "429": decodeError("GetSkillVersion429", GetSkillVersion429), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteSkillVersion": (skillId, version, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [skillId, version], + () => "/skills/" + __encodePathParam(skillId) + "/versions/" + __encodePathParam(version) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteSkillVersion200), + "400": decodeError("DeleteSkillVersion400", DeleteSkillVersion400), + "404": decodeError("DeleteSkillVersion404", DeleteSkillVersion404), + "429": decodeError("DeleteSkillVersion429", DeleteSkillVersion429), + orElse: unexpectedStatus + })) + ) + ) + ), + "GetSkillVersionContent": (skillId, version, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [skillId, version], + () => "/skills/" + __encodePathParam(skillId) + "/versions/" + __encodePathParam(version) + "/content" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "400": decodeError("GetSkillVersionContent400", GetSkillVersionContent400), + "404": decodeError("GetSkillVersionContent404", GetSkillVersionContent404), + "429": decodeError("GetSkillVersionContent429", GetSkillVersionContent429), + orElse: unexpectedStatus + })) + ) + ) + ), + "GetSkillVersionContentStream": (skillId, version) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [skillId, version], () => + "/skills/" + __encodePathParam(skillId) + "/versions/" + __encodePathParam(version) + "/content").pipe( + Effect.map((request) => + request.pipe( + binaryRequest + ) + ) + ) + ), + "CancelChatSessionMethod": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/chatkit/sessions/" + __encodePathParam(sessionId) + "/cancel" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CancelChatSessionMethod200), + "400": decodeError("CancelChatSessionMethod400", CancelChatSessionMethod400), + "404": decodeError("CancelChatSessionMethod404", CancelChatSessionMethod404), + "429": decodeError("CancelChatSessionMethod429", CancelChatSessionMethod429), + orElse: unexpectedStatus + })) + ) + ) + ), + "CreateChatSessionMethod": (options: Parameters[0]) => + HttpClientRequest.post("/chatkit/sessions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateChatSessionMethod200), + "400": decodeError("CreateChatSessionMethod400", CreateChatSessionMethod400), + "404": decodeError("CreateChatSessionMethod404", CreateChatSessionMethod404), + "429": decodeError("CreateChatSessionMethod429", CreateChatSessionMethod429), + orElse: unexpectedStatus + })) + ), + "ListThreadItemsMethod": (threadId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [threadId], + () => "/chatkit/threads/" + __encodePathParam(threadId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListThreadItemsMethod200), + "400": decodeError("ListThreadItemsMethod400", ListThreadItemsMethod400), + "404": decodeError("ListThreadItemsMethod404", ListThreadItemsMethod404), + "429": decodeError("ListThreadItemsMethod429", ListThreadItemsMethod429), + orElse: unexpectedStatus + })) + ) + ) + ), + "GetThreadMethod": (threadId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [threadId], () => "/chatkit/threads/" + __encodePathParam(threadId) + "") + .pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(GetThreadMethod200), + "400": decodeError("GetThreadMethod400", GetThreadMethod400), + "404": decodeError("GetThreadMethod404", GetThreadMethod404), + "429": decodeError("GetThreadMethod429", GetThreadMethod429), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteThreadMethod": (threadId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [threadId], + () => "/chatkit/threads/" + __encodePathParam(threadId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteThreadMethod200), + "400": decodeError("DeleteThreadMethod400", DeleteThreadMethod400), + "404": decodeError("DeleteThreadMethod404", DeleteThreadMethod404), + "429": decodeError("DeleteThreadMethod429", DeleteThreadMethod429), + orElse: unexpectedStatus + })) + ) + ) + ), + "ListThreadsMethod": (options: Parameters[0]) => + HttpClientRequest.get("/chatkit/threads").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "before": options?.params?.["before"] as any, + "user": options?.params?.["user"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListThreadsMethod200), + "400": decodeError("ListThreadsMethod400", ListThreadsMethod400), + "404": decodeError("ListThreadsMethod404", ListThreadsMethod404), + "429": decodeError("ListThreadsMethod429", ListThreadsMethod429), + orElse: unexpectedStatus + })) + ), + "createDecision": (options: Parameters[0]) => + HttpClientRequest.post("/decisions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateDecision200), + orElse: unexpectedStatus + })) + ), + "listAgentEnvironments": (options: Parameters[0]) => + HttpClientRequest.get("/agents/environments").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "type": options?.params?.["type"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentEnvironments200), + "400": decodeError("ListAgentEnvironments400", ListAgentEnvironments400), + "401": decodeError("ListAgentEnvironments401", ListAgentEnvironments401), + "403": decodeError("ListAgentEnvironments403", ListAgentEnvironments403), + "404": decodeError("ListAgentEnvironments404", ListAgentEnvironments404), + "409": decodeError("ListAgentEnvironments409", ListAgentEnvironments409), + "500": decodeError("ListAgentEnvironments500", ListAgentEnvironments500), + "503": decodeError("ListAgentEnvironments503", ListAgentEnvironments503), + orElse: unexpectedStatus + })) + ), + "createAgentEnvironment": (options: Parameters[0]) => + HttpClientRequest.post("/agents/environments").pipe( + HttpClientRequest.setHeaders({ "Idempotency-Key": options.params?.["Idempotency-Key"] ?? undefined }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateAgentEnvironment201), + "400": decodeError("CreateAgentEnvironment400", CreateAgentEnvironment400), + "401": decodeError("CreateAgentEnvironment401", CreateAgentEnvironment401), + "403": decodeError("CreateAgentEnvironment403", CreateAgentEnvironment403), + "404": decodeError("CreateAgentEnvironment404", CreateAgentEnvironment404), + "409": decodeError("CreateAgentEnvironment409", CreateAgentEnvironment409), + "429": decodeError("CreateAgentEnvironment429", CreateAgentEnvironment429), + "500": decodeError("CreateAgentEnvironment500", CreateAgentEnvironment500), + "503": decodeError("CreateAgentEnvironment503", CreateAgentEnvironment503), + orElse: unexpectedStatus + })) + ), + "retrieveAgentEnvironment": (environmentId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [environmentId], + () => "/agents/environments/" + __encodePathParam(environmentId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentEnvironment200), + "400": decodeError("RetrieveAgentEnvironment400", RetrieveAgentEnvironment400), + "401": decodeError("RetrieveAgentEnvironment401", RetrieveAgentEnvironment401), + "403": decodeError("RetrieveAgentEnvironment403", RetrieveAgentEnvironment403), + "404": decodeError("RetrieveAgentEnvironment404", RetrieveAgentEnvironment404), + "409": decodeError("RetrieveAgentEnvironment409", RetrieveAgentEnvironment409), + "500": decodeError("RetrieveAgentEnvironment500", RetrieveAgentEnvironment500), + "503": decodeError("RetrieveAgentEnvironment503", RetrieveAgentEnvironment503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentEnvironmentFiles": (environmentId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [environmentId], + () => "/agents/environments/" + __encodePathParam(environmentId) + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "path": options?.params?.["path"] as any, + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "page": options?.params?.["page"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentEnvironmentFiles200), + "400": decodeError("ListAgentEnvironmentFiles400", ListAgentEnvironmentFiles400), + "401": decodeError("ListAgentEnvironmentFiles401", ListAgentEnvironmentFiles401), + "403": decodeError("ListAgentEnvironmentFiles403", ListAgentEnvironmentFiles403), + "404": decodeError("ListAgentEnvironmentFiles404", ListAgentEnvironmentFiles404), + "409": decodeError("ListAgentEnvironmentFiles409", ListAgentEnvironmentFiles409), + "500": decodeError("ListAgentEnvironmentFiles500", ListAgentEnvironmentFiles500), + "503": decodeError("ListAgentEnvironmentFiles503", ListAgentEnvironmentFiles503), + orElse: unexpectedStatus + })) + ) + ) + ), + "createAgentEnvironmentFile": (environmentId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [environmentId], + () => "/agents/environments/" + __encodePathParam(environmentId) + "/files" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateAgentEnvironmentFile201), + "400": decodeError("CreateAgentEnvironmentFile400", CreateAgentEnvironmentFile400), + "401": decodeError("CreateAgentEnvironmentFile401", CreateAgentEnvironmentFile401), + "403": decodeError("CreateAgentEnvironmentFile403", CreateAgentEnvironmentFile403), + "404": decodeError("CreateAgentEnvironmentFile404", CreateAgentEnvironmentFile404), + "409": decodeError("CreateAgentEnvironmentFile409", CreateAgentEnvironmentFile409), + "429": decodeError("CreateAgentEnvironmentFile429", CreateAgentEnvironmentFile429), + "500": decodeError("CreateAgentEnvironmentFile500", CreateAgentEnvironmentFile500), + "503": decodeError("CreateAgentEnvironmentFile503", CreateAgentEnvironmentFile503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionSubagents": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/subagents" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionSubagents200), + "400": decodeError("ListAgentSessionSubagents400", ListAgentSessionSubagents400), + "401": decodeError("ListAgentSessionSubagents401", ListAgentSessionSubagents401), + "403": decodeError("ListAgentSessionSubagents403", ListAgentSessionSubagents403), + "404": decodeError("ListAgentSessionSubagents404", ListAgentSessionSubagents404), + "409": decodeError("ListAgentSessionSubagents409", ListAgentSessionSubagents409), + "500": decodeError("ListAgentSessionSubagents500", ListAgentSessionSubagents500), + "503": decodeError("ListAgentSessionSubagents503", ListAgentSessionSubagents503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveAgentSessionSubagent": ( + sessionId, + subagentId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, subagentId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/subagents/" + __encodePathParam(subagentId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentSessionSubagent200), + "400": decodeError("RetrieveAgentSessionSubagent400", RetrieveAgentSessionSubagent400), + "401": decodeError("RetrieveAgentSessionSubagent401", RetrieveAgentSessionSubagent401), + "403": decodeError("RetrieveAgentSessionSubagent403", RetrieveAgentSessionSubagent403), + "404": decodeError("RetrieveAgentSessionSubagent404", RetrieveAgentSessionSubagent404), + "409": decodeError("RetrieveAgentSessionSubagent409", RetrieveAgentSessionSubagent409), + "500": decodeError("RetrieveAgentSessionSubagent500", RetrieveAgentSessionSubagent500), + "503": decodeError("RetrieveAgentSessionSubagent503", RetrieveAgentSessionSubagent503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionSubagentItems": ( + sessionId, + subagentId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, subagentId], + () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/subagents/" + __encodePathParam(subagentId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionSubagentItems200), + "400": decodeError("ListAgentSessionSubagentItems400", ListAgentSessionSubagentItems400), + "401": decodeError("ListAgentSessionSubagentItems401", ListAgentSessionSubagentItems401), + "403": decodeError("ListAgentSessionSubagentItems403", ListAgentSessionSubagentItems403), + "404": decodeError("ListAgentSessionSubagentItems404", ListAgentSessionSubagentItems404), + "409": decodeError("ListAgentSessionSubagentItems409", ListAgentSessionSubagentItems409), + "500": decodeError("ListAgentSessionSubagentItems500", ListAgentSessionSubagentItems500), + "503": decodeError("ListAgentSessionSubagentItems503", ListAgentSessionSubagentItems503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionSubagentTurns": ( + sessionId, + subagentId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, subagentId], + () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/subagents/" + __encodePathParam(subagentId) + "/turns" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionSubagentTurns200), + "400": decodeError("ListAgentSessionSubagentTurns400", ListAgentSessionSubagentTurns400), + "401": decodeError("ListAgentSessionSubagentTurns401", ListAgentSessionSubagentTurns401), + "403": decodeError("ListAgentSessionSubagentTurns403", ListAgentSessionSubagentTurns403), + "404": decodeError("ListAgentSessionSubagentTurns404", ListAgentSessionSubagentTurns404), + "409": decodeError("ListAgentSessionSubagentTurns409", ListAgentSessionSubagentTurns409), + "500": decodeError("ListAgentSessionSubagentTurns500", ListAgentSessionSubagentTurns500), + "503": decodeError("ListAgentSessionSubagentTurns503", ListAgentSessionSubagentTurns503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveAgentSessionSubagentTurn": ( + sessionId, + subagentId, + turnId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, subagentId, turnId], + () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/subagents/" + __encodePathParam(subagentId) + + "/turns/" + __encodePathParam(turnId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentSessionSubagentTurn200), + "400": decodeError("RetrieveAgentSessionSubagentTurn400", RetrieveAgentSessionSubagentTurn400), + "401": decodeError("RetrieveAgentSessionSubagentTurn401", RetrieveAgentSessionSubagentTurn401), + "403": decodeError("RetrieveAgentSessionSubagentTurn403", RetrieveAgentSessionSubagentTurn403), + "404": decodeError("RetrieveAgentSessionSubagentTurn404", RetrieveAgentSessionSubagentTurn404), + "409": decodeError("RetrieveAgentSessionSubagentTurn409", RetrieveAgentSessionSubagentTurn409), + "500": decodeError("RetrieveAgentSessionSubagentTurn500", RetrieveAgentSessionSubagentTurn500), + "503": decodeError("RetrieveAgentSessionSubagentTurn503", RetrieveAgentSessionSubagentTurn503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionSubagentTurnItems": ( + sessionId, + subagentId, + turnId, + options: Parameters[3] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, subagentId, turnId], + () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/subagents/" + __encodePathParam(subagentId) + + "/turns/" + __encodePathParam(turnId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionSubagentTurnItems200), + "400": decodeError("ListAgentSessionSubagentTurnItems400", ListAgentSessionSubagentTurnItems400), + "401": decodeError("ListAgentSessionSubagentTurnItems401", ListAgentSessionSubagentTurnItems401), + "403": decodeError("ListAgentSessionSubagentTurnItems403", ListAgentSessionSubagentTurnItems403), + "404": decodeError("ListAgentSessionSubagentTurnItems404", ListAgentSessionSubagentTurnItems404), + "409": decodeError("ListAgentSessionSubagentTurnItems409", ListAgentSessionSubagentTurnItems409), + "500": decodeError("ListAgentSessionSubagentTurnItems500", ListAgentSessionSubagentTurnItems500), + "503": decodeError("ListAgentSessionSubagentTurnItems503", ListAgentSessionSubagentTurnItems503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgents": (options: Parameters[0]) => + HttpClientRequest.get("/agents").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgents200), + "400": decodeError("ListAgents400", ListAgents400), + "401": decodeError("ListAgents401", ListAgents401), + "403": decodeError("ListAgents403", ListAgents403), + "404": decodeError("ListAgents404", ListAgents404), + "409": decodeError("ListAgents409", ListAgents409), + "500": decodeError("ListAgents500", ListAgents500), + "503": decodeError("ListAgents503", ListAgents503), + orElse: unexpectedStatus + })) + ), + "createAgent": (options: Parameters[0]) => + HttpClientRequest.post("/agents").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateAgent201), + "400": decodeError("CreateAgent400", CreateAgent400), + "401": decodeError("CreateAgent401", CreateAgent401), + "403": decodeError("CreateAgent403", CreateAgent403), + "404": decodeError("CreateAgent404", CreateAgent404), + "409": decodeError("CreateAgent409", CreateAgent409), + "500": decodeError("CreateAgent500", CreateAgent500), + "503": decodeError("CreateAgent503", CreateAgent503), + orElse: unexpectedStatus + })) + ), + "retrieveAgent": (agentId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [agentId], () => "/agents/" + __encodePathParam(agentId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgent200), + "400": decodeError("RetrieveAgent400", RetrieveAgent400), + "401": decodeError("RetrieveAgent401", RetrieveAgent401), + "403": decodeError("RetrieveAgent403", RetrieveAgent403), + "404": decodeError("RetrieveAgent404", RetrieveAgent404), + "409": decodeError("RetrieveAgent409", RetrieveAgent409), + "500": decodeError("RetrieveAgent500", RetrieveAgent500), + "503": decodeError("RetrieveAgent503", RetrieveAgent503), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateAgent": (agentId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [agentId], () => "/agents/" + __encodePathParam(agentId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateAgent200), + "400": decodeError("UpdateAgent400", UpdateAgent400), + "401": decodeError("UpdateAgent401", UpdateAgent401), + "403": decodeError("UpdateAgent403", UpdateAgent403), + "404": decodeError("UpdateAgent404", UpdateAgent404), + "409": decodeError("UpdateAgent409", UpdateAgent409), + "500": decodeError("UpdateAgent500", UpdateAgent500), + "503": decodeError("UpdateAgent503", UpdateAgent503), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteAgent": (agentId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [agentId], () => "/agents/" + __encodePathParam(agentId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteAgent200), + "400": decodeError("DeleteAgent400", DeleteAgent400), + "401": decodeError("DeleteAgent401", DeleteAgent401), + "403": decodeError("DeleteAgent403", DeleteAgent403), + "404": decodeError("DeleteAgent404", DeleteAgent404), + "409": decodeError("DeleteAgent409", DeleteAgent409), + "500": decodeError("DeleteAgent500", DeleteAgent500), + "503": decodeError("DeleteAgent503", DeleteAgent503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentEnvironmentTemplates": (options: Parameters[0]) => + HttpClientRequest.get("/agents/environments/templates").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentEnvironmentTemplates200), + "400": decodeError("ListAgentEnvironmentTemplates400", ListAgentEnvironmentTemplates400), + "401": decodeError("ListAgentEnvironmentTemplates401", ListAgentEnvironmentTemplates401), + "403": decodeError("ListAgentEnvironmentTemplates403", ListAgentEnvironmentTemplates403), + "404": decodeError("ListAgentEnvironmentTemplates404", ListAgentEnvironmentTemplates404), + "500": decodeError("ListAgentEnvironmentTemplates500", ListAgentEnvironmentTemplates500), + "503": decodeError("ListAgentEnvironmentTemplates503", ListAgentEnvironmentTemplates503), + orElse: unexpectedStatus + })) + ), + "createAgentEnvironmentTemplate": (options: Parameters[0]) => + HttpClientRequest.post("/agents/environments/templates").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateAgentEnvironmentTemplate201), + "400": decodeError("CreateAgentEnvironmentTemplate400", CreateAgentEnvironmentTemplate400), + "401": decodeError("CreateAgentEnvironmentTemplate401", CreateAgentEnvironmentTemplate401), + "403": decodeError("CreateAgentEnvironmentTemplate403", CreateAgentEnvironmentTemplate403), + "404": decodeError("CreateAgentEnvironmentTemplate404", CreateAgentEnvironmentTemplate404), + "500": decodeError("CreateAgentEnvironmentTemplate500", CreateAgentEnvironmentTemplate500), + "503": decodeError("CreateAgentEnvironmentTemplate503", CreateAgentEnvironmentTemplate503), + orElse: unexpectedStatus + })) + ), + "retrieveAgentEnvironmentTemplate": ( + environmentTemplateId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.get, + [environmentTemplateId], + () => "/agents/environments/templates/" + __encodePathParam(environmentTemplateId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentEnvironmentTemplate200), + "400": decodeError("RetrieveAgentEnvironmentTemplate400", RetrieveAgentEnvironmentTemplate400), + "401": decodeError("RetrieveAgentEnvironmentTemplate401", RetrieveAgentEnvironmentTemplate401), + "403": decodeError("RetrieveAgentEnvironmentTemplate403", RetrieveAgentEnvironmentTemplate403), + "404": decodeError("RetrieveAgentEnvironmentTemplate404", RetrieveAgentEnvironmentTemplate404), + "500": decodeError("RetrieveAgentEnvironmentTemplate500", RetrieveAgentEnvironmentTemplate500), + "503": decodeError("RetrieveAgentEnvironmentTemplate503", RetrieveAgentEnvironmentTemplate503), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateAgentEnvironmentTemplate": ( + environmentTemplateId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [environmentTemplateId], + () => "/agents/environments/templates/" + __encodePathParam(environmentTemplateId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateAgentEnvironmentTemplate200), + "400": decodeError("UpdateAgentEnvironmentTemplate400", UpdateAgentEnvironmentTemplate400), + "401": decodeError("UpdateAgentEnvironmentTemplate401", UpdateAgentEnvironmentTemplate401), + "403": decodeError("UpdateAgentEnvironmentTemplate403", UpdateAgentEnvironmentTemplate403), + "404": decodeError("UpdateAgentEnvironmentTemplate404", UpdateAgentEnvironmentTemplate404), + "409": decodeError("UpdateAgentEnvironmentTemplate409", UpdateAgentEnvironmentTemplate409), + "500": decodeError("UpdateAgentEnvironmentTemplate500", UpdateAgentEnvironmentTemplate500), + "503": decodeError("UpdateAgentEnvironmentTemplate503", UpdateAgentEnvironmentTemplate503), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteAgentEnvironmentTemplate": ( + environmentTemplateId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.delete, + [environmentTemplateId], + () => "/agents/environments/templates/" + __encodePathParam(environmentTemplateId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteAgentEnvironmentTemplate200), + "400": decodeError("DeleteAgentEnvironmentTemplate400", DeleteAgentEnvironmentTemplate400), + "401": decodeError("DeleteAgentEnvironmentTemplate401", DeleteAgentEnvironmentTemplate401), + "403": decodeError("DeleteAgentEnvironmentTemplate403", DeleteAgentEnvironmentTemplate403), + "404": decodeError("DeleteAgentEnvironmentTemplate404", DeleteAgentEnvironmentTemplate404), + "409": decodeError("DeleteAgentEnvironmentTemplate409", DeleteAgentEnvironmentTemplate409), + "500": decodeError("DeleteAgentEnvironmentTemplate500", DeleteAgentEnvironmentTemplate500), + "503": decodeError("DeleteAgentEnvironmentTemplate503", DeleteAgentEnvironmentTemplate503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessions": (options: Parameters[0]) => + HttpClientRequest.get("/agents/sessions").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "agent_id": options?.params?.["agent_id"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessions200), + "400": decodeError("ListAgentSessions400", ListAgentSessions400), + "401": decodeError("ListAgentSessions401", ListAgentSessions401), + "403": decodeError("ListAgentSessions403", ListAgentSessions403), + "404": decodeError("ListAgentSessions404", ListAgentSessions404), + "409": decodeError("ListAgentSessions409", ListAgentSessions409), + "500": decodeError("ListAgentSessions500", ListAgentSessions500), + "503": decodeError("ListAgentSessions503", ListAgentSessions503), + orElse: unexpectedStatus + })) + ), + "createAgentSession": (options: Parameters[0]) => + HttpClientRequest.post("/agents/sessions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateAgentSession201), + "400": decodeError("CreateAgentSession400", CreateAgentSession400), + "401": decodeError("CreateAgentSession401", CreateAgentSession401), + "403": decodeError("CreateAgentSession403", CreateAgentSession403), + "404": decodeError("CreateAgentSession404", CreateAgentSession404), + "409": decodeError("CreateAgentSession409", CreateAgentSession409), + "429": decodeError("CreateAgentSession429", CreateAgentSession429), + "500": decodeError("CreateAgentSession500", CreateAgentSession500), + "503": decodeError("CreateAgentSession503", CreateAgentSession503), + orElse: unexpectedStatus + })) + ), + "createAgentSessionSse": (options) => + HttpClientRequest.post("/agents/sessions").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(CreateAgentSession201Sse) + ), + "retrieveAgentSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentSession200), + "400": decodeError("RetrieveAgentSession400", RetrieveAgentSession400), + "401": decodeError("RetrieveAgentSession401", RetrieveAgentSession401), + "403": decodeError("RetrieveAgentSession403", RetrieveAgentSession403), + "404": decodeError("RetrieveAgentSession404", RetrieveAgentSession404), + "409": decodeError("RetrieveAgentSession409", RetrieveAgentSession409), + "500": decodeError("RetrieveAgentSession500", RetrieveAgentSession500), + "503": decodeError("RetrieveAgentSession503", RetrieveAgentSession503), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateAgentSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateAgentSession200), + "400": decodeError("UpdateAgentSession400", UpdateAgentSession400), + "401": decodeError("UpdateAgentSession401", UpdateAgentSession401), + "403": decodeError("UpdateAgentSession403", UpdateAgentSession403), + "404": decodeError("UpdateAgentSession404", UpdateAgentSession404), + "409": decodeError("UpdateAgentSession409", UpdateAgentSession409), + "500": decodeError("UpdateAgentSession500", UpdateAgentSession500), + "503": decodeError("UpdateAgentSession503", UpdateAgentSession503), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteAgentSession": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteAgentSession200), + "400": decodeError("DeleteAgentSession400", DeleteAgentSession400), + "401": decodeError("DeleteAgentSession401", DeleteAgentSession401), + "403": decodeError("DeleteAgentSession403", DeleteAgentSession403), + "404": decodeError("DeleteAgentSession404", DeleteAgentSession404), + "409": decodeError("DeleteAgentSession409", DeleteAgentSession409), + "500": decodeError("DeleteAgentSession500", DeleteAgentSession500), + "503": decodeError("DeleteAgentSession503", DeleteAgentSession503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionArtifacts": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/artifacts" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "environment_id": options?.params?.["environment_id"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionArtifacts200), + "400": decodeError("ListAgentSessionArtifacts400", ListAgentSessionArtifacts400), + "401": decodeError("ListAgentSessionArtifacts401", ListAgentSessionArtifacts401), + "403": decodeError("ListAgentSessionArtifacts403", ListAgentSessionArtifacts403), + "404": decodeError("ListAgentSessionArtifacts404", ListAgentSessionArtifacts404), + "409": decodeError("ListAgentSessionArtifacts409", ListAgentSessionArtifacts409), + "500": decodeError("ListAgentSessionArtifacts500", ListAgentSessionArtifacts500), + "503": decodeError("ListAgentSessionArtifacts503", ListAgentSessionArtifacts503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveAgentSessionArtifact": ( + sessionId, + artifactId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, artifactId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/artifacts/" + __encodePathParam(artifactId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentSessionArtifact200), + "400": decodeError("RetrieveAgentSessionArtifact400", RetrieveAgentSessionArtifact400), + "401": decodeError("RetrieveAgentSessionArtifact401", RetrieveAgentSessionArtifact401), + "403": decodeError("RetrieveAgentSessionArtifact403", RetrieveAgentSessionArtifact403), + "404": decodeError("RetrieveAgentSessionArtifact404", RetrieveAgentSessionArtifact404), + "409": decodeError("RetrieveAgentSessionArtifact409", RetrieveAgentSessionArtifact409), + "500": decodeError("RetrieveAgentSessionArtifact500", RetrieveAgentSessionArtifact500), + "503": decodeError("RetrieveAgentSessionArtifact503", RetrieveAgentSessionArtifact503), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteAgentSessionArtifact": ( + sessionId, + artifactId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.delete, + [sessionId, artifactId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/artifacts/" + __encodePathParam(artifactId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteAgentSessionArtifact200), + "400": decodeError("DeleteAgentSessionArtifact400", DeleteAgentSessionArtifact400), + "401": decodeError("DeleteAgentSessionArtifact401", DeleteAgentSessionArtifact401), + "403": decodeError("DeleteAgentSessionArtifact403", DeleteAgentSessionArtifact403), + "404": decodeError("DeleteAgentSessionArtifact404", DeleteAgentSessionArtifact404), + "409": decodeError("DeleteAgentSessionArtifact409", DeleteAgentSessionArtifact409), + "500": decodeError("DeleteAgentSessionArtifact500", DeleteAgentSessionArtifact500), + "503": decodeError("DeleteAgentSessionArtifact503", DeleteAgentSessionArtifact503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveAgentSessionArtifactContent": ( + sessionId, + artifactId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, artifactId], + () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/artifacts/" + __encodePathParam(artifactId) + + "/content" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeBinary, + "400": decodeError("RetrieveAgentSessionArtifactContent400", RetrieveAgentSessionArtifactContent400), + "401": decodeError("RetrieveAgentSessionArtifactContent401", RetrieveAgentSessionArtifactContent401), + "403": decodeError("RetrieveAgentSessionArtifactContent403", RetrieveAgentSessionArtifactContent403), + "404": decodeError("RetrieveAgentSessionArtifactContent404", RetrieveAgentSessionArtifactContent404), + "409": decodeError("RetrieveAgentSessionArtifactContent409", RetrieveAgentSessionArtifactContent409), + "500": decodeError("RetrieveAgentSessionArtifactContent500", RetrieveAgentSessionArtifactContent500), + "503": decodeError("RetrieveAgentSessionArtifactContent503", RetrieveAgentSessionArtifactContent503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveAgentSessionArtifactContentStream": (sessionId, artifactId) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [sessionId, artifactId], () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/artifacts/" + __encodePathParam(artifactId) + + "/content").pipe( + Effect.map((request) => + request.pipe( + binaryRequest + ) + ) + ) + ), + "listAgentSessionEvents": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/events" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "400": decodeError("ListAgentSessionEvents400", ListAgentSessionEvents400), + "401": decodeError("ListAgentSessionEvents401", ListAgentSessionEvents401), + "403": decodeError("ListAgentSessionEvents403", ListAgentSessionEvents403), + "404": decodeError("ListAgentSessionEvents404", ListAgentSessionEvents404), + "409": decodeError("ListAgentSessionEvents409", ListAgentSessionEvents409), + "500": decodeError("ListAgentSessionEvents500", ListAgentSessionEvents500), + "503": decodeError("ListAgentSessionEvents503", ListAgentSessionEvents503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionEventsSse": (sessionId) => + Stream.unwrap( + __makePathRequest(HttpClientRequest.get, [sessionId], () => + "/agents/sessions/" + __encodePathParam(sessionId) + "/events").pipe( + Effect.map((request) => + request.pipe( + sseRequest(ListAgentSessionEvents200Sse) + ) + ) + ) + ), + "createAgentSessionEvents": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/events" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setHeaders({ "Idempotency-Key": options.params?.["Idempotency-Key"] ?? undefined }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "400": decodeError("CreateAgentSessionEvents400", CreateAgentSessionEvents400), + "401": decodeError("CreateAgentSessionEvents401", CreateAgentSessionEvents401), + "403": decodeError("CreateAgentSessionEvents403", CreateAgentSessionEvents403), + "404": decodeError("CreateAgentSessionEvents404", CreateAgentSessionEvents404), + "409": decodeError("CreateAgentSessionEvents409", CreateAgentSessionEvents409), + "500": decodeError("CreateAgentSessionEvents500", CreateAgentSessionEvents500), + "503": decodeError("CreateAgentSessionEvents503", CreateAgentSessionEvents503), + "202": () => Effect.void, + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionItems": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionItems200), + "400": decodeError("ListAgentSessionItems400", ListAgentSessionItems400), + "401": decodeError("ListAgentSessionItems401", ListAgentSessionItems401), + "403": decodeError("ListAgentSessionItems403", ListAgentSessionItems403), + "404": decodeError("ListAgentSessionItems404", ListAgentSessionItems404), + "409": decodeError("ListAgentSessionItems409", ListAgentSessionItems409), + "500": decodeError("ListAgentSessionItems500", ListAgentSessionItems500), + "503": decodeError("ListAgentSessionItems503", ListAgentSessionItems503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionTraces": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/traces" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionTraces200), + "400": decodeError("ListAgentSessionTraces400", ListAgentSessionTraces400), + "401": decodeError("ListAgentSessionTraces401", ListAgentSessionTraces401), + "403": decodeError("ListAgentSessionTraces403", ListAgentSessionTraces403), + "404": decodeError("ListAgentSessionTraces404", ListAgentSessionTraces404), + "409": decodeError("ListAgentSessionTraces409", ListAgentSessionTraces409), + "500": decodeError("ListAgentSessionTraces500", ListAgentSessionTraces500), + "503": decodeError("ListAgentSessionTraces503", ListAgentSessionTraces503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionTurns": (sessionId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/turns" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionTurns200), + "400": decodeError("ListAgentSessionTurns400", ListAgentSessionTurns400), + "401": decodeError("ListAgentSessionTurns401", ListAgentSessionTurns401), + "403": decodeError("ListAgentSessionTurns403", ListAgentSessionTurns403), + "404": decodeError("ListAgentSessionTurns404", ListAgentSessionTurns404), + "409": decodeError("ListAgentSessionTurns409", ListAgentSessionTurns409), + "500": decodeError("ListAgentSessionTurns500", ListAgentSessionTurns500), + "503": decodeError("ListAgentSessionTurns503", ListAgentSessionTurns503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveAgentSessionTurn": (sessionId, turnId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, turnId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/turns/" + __encodePathParam(turnId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveAgentSessionTurn200), + "400": decodeError("RetrieveAgentSessionTurn400", RetrieveAgentSessionTurn400), + "401": decodeError("RetrieveAgentSessionTurn401", RetrieveAgentSessionTurn401), + "403": decodeError("RetrieveAgentSessionTurn403", RetrieveAgentSessionTurn403), + "404": decodeError("RetrieveAgentSessionTurn404", RetrieveAgentSessionTurn404), + "409": decodeError("RetrieveAgentSessionTurn409", RetrieveAgentSessionTurn409), + "500": decodeError("RetrieveAgentSessionTurn500", RetrieveAgentSessionTurn500), + "503": decodeError("RetrieveAgentSessionTurn503", RetrieveAgentSessionTurn503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listAgentSessionTurnItems": ( + sessionId, + turnId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [sessionId, turnId], + () => "/agents/sessions/" + __encodePathParam(sessionId) + "/turns/" + __encodePathParam(turnId) + "/items" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListAgentSessionTurnItems200), + "400": decodeError("ListAgentSessionTurnItems400", ListAgentSessionTurnItems400), + "401": decodeError("ListAgentSessionTurnItems401", ListAgentSessionTurnItems401), + "403": decodeError("ListAgentSessionTurnItems403", ListAgentSessionTurnItems403), + "404": decodeError("ListAgentSessionTurnItems404", ListAgentSessionTurnItems404), + "409": decodeError("ListAgentSessionTurnItems409", ListAgentSessionTurnItems409), + "500": decodeError("ListAgentSessionTurnItems500", ListAgentSessionTurnItems500), + "503": decodeError("ListAgentSessionTurnItems503", ListAgentSessionTurnItems503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listVaults": (options: Parameters[0]) => + HttpClientRequest.get("/vaults").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "metadata": options?.params?.["metadata"] as any, + "status": options?.params?.["status"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListVaults200), + "400": decodeError("ListVaults400", ListVaults400), + "401": decodeError("ListVaults401", ListVaults401), + "403": decodeError("ListVaults403", ListVaults403), + "404": decodeError("ListVaults404", ListVaults404), + "409": decodeError("ListVaults409", ListVaults409), + "500": decodeError("ListVaults500", ListVaults500), + "503": decodeError("ListVaults503", ListVaults503), + orElse: unexpectedStatus + })) + ), + "createVault": (options: Parameters[0]) => + HttpClientRequest.post("/vaults").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVault201), + "400": decodeError("CreateVault400", CreateVault400), + "401": decodeError("CreateVault401", CreateVault401), + "403": decodeError("CreateVault403", CreateVault403), + "404": decodeError("CreateVault404", CreateVault404), + "409": decodeError("CreateVault409", CreateVault409), + "500": decodeError("CreateVault500", CreateVault500), + "503": decodeError("CreateVault503", CreateVault503), + orElse: unexpectedStatus + })) + ), + "retrieveVault": (vaultId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.get, [vaultId], () => "/vaults/" + __encodePathParam(vaultId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveVault200), + "400": decodeError("RetrieveVault400", RetrieveVault400), + "401": decodeError("RetrieveVault401", RetrieveVault401), + "403": decodeError("RetrieveVault403", RetrieveVault403), + "404": decodeError("RetrieveVault404", RetrieveVault404), + "409": decodeError("RetrieveVault409", RetrieveVault409), + "500": decodeError("RetrieveVault500", RetrieveVault500), + "503": decodeError("RetrieveVault503", RetrieveVault503), + orElse: unexpectedStatus + })) + ) + ) + ), + "updateVault": (vaultId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.post, [vaultId], () => "/vaults/" + __encodePathParam(vaultId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateVault200), + "400": decodeError("UpdateVault400", UpdateVault400), + "401": decodeError("UpdateVault401", UpdateVault401), + "403": decodeError("UpdateVault403", UpdateVault403), + "404": decodeError("UpdateVault404", UpdateVault404), + "409": decodeError("UpdateVault409", UpdateVault409), + "500": decodeError("UpdateVault500", UpdateVault500), + "503": decodeError("UpdateVault503", UpdateVault503), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteVault": (vaultId, options: Parameters[1]) => + __makePathRequest(HttpClientRequest.delete, [vaultId], () => "/vaults/" + __encodePathParam(vaultId) + "").pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteVault200), + "400": decodeError("DeleteVault400", DeleteVault400), + "401": decodeError("DeleteVault401", DeleteVault401), + "403": decodeError("DeleteVault403", DeleteVault403), + "404": decodeError("DeleteVault404", DeleteVault404), + "409": decodeError("DeleteVault409", DeleteVault409), + "500": decodeError("DeleteVault500", DeleteVault500), + "503": decodeError("DeleteVault503", DeleteVault503), + orElse: unexpectedStatus + })) + ) + ) + ), + "listVaultCredentials": (vaultId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [vaultId], + () => "/vaults/" + __encodePathParam(vaultId) + "/credentials" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "metadata": options?.params?.["metadata"] as any, + "status": options?.params?.["status"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListVaultCredentials200), + "400": decodeError("ListVaultCredentials400", ListVaultCredentials400), + "401": decodeError("ListVaultCredentials401", ListVaultCredentials401), + "403": decodeError("ListVaultCredentials403", ListVaultCredentials403), + "404": decodeError("ListVaultCredentials404", ListVaultCredentials404), + "409": decodeError("ListVaultCredentials409", ListVaultCredentials409), + "500": decodeError("ListVaultCredentials500", ListVaultCredentials500), + "503": decodeError("ListVaultCredentials503", ListVaultCredentials503), + orElse: unexpectedStatus + })) + ) + ) + ), + "createVaultCredential": (vaultId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [vaultId], + () => "/vaults/" + __encodePathParam(vaultId) + "/credentials" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateVaultCredential201), + "400": decodeError("CreateVaultCredential400", CreateVaultCredential400), + "401": decodeError("CreateVaultCredential401", CreateVaultCredential401), + "403": decodeError("CreateVaultCredential403", CreateVaultCredential403), + "404": decodeError("CreateVaultCredential404", CreateVaultCredential404), + "409": decodeError("CreateVaultCredential409", CreateVaultCredential409), + "500": decodeError("CreateVaultCredential500", CreateVaultCredential500), + "503": decodeError("CreateVaultCredential503", CreateVaultCredential503), + orElse: unexpectedStatus + })) + ) + ) + ), + "retrieveVaultCredential": ( + vaultId, + credentialId, + options: Parameters[2] + ) => + __makePathRequest( + HttpClientRequest.get, + [vaultId, credentialId], + () => "/vaults/" + __encodePathParam(vaultId) + "/credentials/" + __encodePathParam(credentialId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveVaultCredential200), + "400": decodeError("RetrieveVaultCredential400", RetrieveVaultCredential400), + "401": decodeError("RetrieveVaultCredential401", RetrieveVaultCredential401), + "403": decodeError("RetrieveVaultCredential403", RetrieveVaultCredential403), + "404": decodeError("RetrieveVaultCredential404", RetrieveVaultCredential404), + "409": decodeError("RetrieveVaultCredential409", RetrieveVaultCredential409), + "500": decodeError("RetrieveVaultCredential500", RetrieveVaultCredential500), + "503": decodeError("RetrieveVaultCredential503", RetrieveVaultCredential503), + orElse: unexpectedStatus + })) + ) + ) + ), + "rotateVaultCredential": (vaultId, credentialId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.post, + [vaultId, credentialId], + () => "/vaults/" + __encodePathParam(vaultId) + "/credentials/" + __encodePathParam(credentialId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RotateVaultCredential200), + "400": decodeError("RotateVaultCredential400", RotateVaultCredential400), + "401": decodeError("RotateVaultCredential401", RotateVaultCredential401), + "403": decodeError("RotateVaultCredential403", RotateVaultCredential403), + "404": decodeError("RotateVaultCredential404", RotateVaultCredential404), + "409": decodeError("RotateVaultCredential409", RotateVaultCredential409), + "500": decodeError("RotateVaultCredential500", RotateVaultCredential500), + "503": decodeError("RotateVaultCredential503", RotateVaultCredential503), + orElse: unexpectedStatus + })) + ) + ) + ), + "deleteVaultCredential": (vaultId, credentialId, options: Parameters[2]) => + __makePathRequest( + HttpClientRequest.delete, + [vaultId, credentialId], + () => "/vaults/" + __encodePathParam(vaultId) + "/credentials/" + __encodePathParam(credentialId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteVaultCredential200), + "400": decodeError("DeleteVaultCredential400", DeleteVaultCredential400), + "401": decodeError("DeleteVaultCredential401", DeleteVaultCredential401), + "403": decodeError("DeleteVaultCredential403", DeleteVaultCredential403), + "404": decodeError("DeleteVaultCredential404", DeleteVaultCredential404), + "409": decodeError("DeleteVaultCredential409", DeleteVaultCredential409), + "500": decodeError("DeleteVaultCredential500", DeleteVaultCredential500), + "503": decodeError("DeleteVaultCredential503", DeleteVaultCredential503), + orElse: unexpectedStatus + })) + ) + ) + ), + "ListWebhookEndpoints": (options: Parameters[0]) => + HttpClientRequest.get("/webhook_endpoints").pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "after": options?.params?.["after"] as any + }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListWebhookEndpoints200), + "400": decodeError("ListWebhookEndpoints400", ListWebhookEndpoints400), + "404": decodeError("ListWebhookEndpoints404", ListWebhookEndpoints404), + orElse: unexpectedStatus + })) + ), + "CreateWebhookEndpoint": (options: Parameters[0]) => + HttpClientRequest.post("/webhook_endpoints").pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(CreateWebhookEndpoint200), + "400": decodeError("CreateWebhookEndpoint400", CreateWebhookEndpoint400), + orElse: unexpectedStatus + })) + ), + "RetrieveWebhookEndpoint": (webhookEndpointId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [webhookEndpointId], + () => "/webhook_endpoints/" + __encodePathParam(webhookEndpointId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RetrieveWebhookEndpoint200), + "400": decodeError("RetrieveWebhookEndpoint400", RetrieveWebhookEndpoint400), + "404": decodeError("RetrieveWebhookEndpoint404", RetrieveWebhookEndpoint404), + orElse: unexpectedStatus + })) + ) + ) + ), + "UpdateWebhookEndpoint": (webhookEndpointId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [webhookEndpointId], + () => "/webhook_endpoints/" + __encodePathParam(webhookEndpointId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(UpdateWebhookEndpoint200), + "400": decodeError("UpdateWebhookEndpoint400", UpdateWebhookEndpoint400), + "404": decodeError("UpdateWebhookEndpoint404", UpdateWebhookEndpoint404), + orElse: unexpectedStatus + })) + ) + ) + ), + "DeleteWebhookEndpoint": (webhookEndpointId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [webhookEndpointId], + () => "/webhook_endpoints/" + __encodePathParam(webhookEndpointId) + "" + ).pipe( + Effect.flatMap((request) => + request.pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(DeleteWebhookEndpoint200), + "400": decodeError("DeleteWebhookEndpoint400", DeleteWebhookEndpoint400), + "404": decodeError("DeleteWebhookEndpoint404", DeleteWebhookEndpoint404), + orElse: unexpectedStatus + })) + ) + ) + ), + "RotateWebhookEndpointSigningSecret": ( + webhookEndpointId, + options: Parameters[1] + ) => + __makePathRequest( + HttpClientRequest.post, + [webhookEndpointId], + () => "/webhook_endpoints/" + __encodePathParam(webhookEndpointId) + "/rotate_secret" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(RotateWebhookEndpointSigningSecret200), + "400": decodeError("RotateWebhookEndpointSigningSecret400", RotateWebhookEndpointSigningSecret400), + "404": decodeError("RotateWebhookEndpointSigningSecret404", RotateWebhookEndpointSigningSecret404), + orElse: unexpectedStatus + })) + ) + ) + ), + "TestWebhookEndpoint": (webhookEndpointId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [webhookEndpointId], + () => "/webhook_endpoints/" + __encodePathParam(webhookEndpointId) + "/test" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(TestWebhookEndpoint200), + "400": decodeError("TestWebhookEndpoint400", TestWebhookEndpoint400), + "404": decodeError("TestWebhookEndpoint404", TestWebhookEndpoint404), + "500": decodeError("TestWebhookEndpoint500", TestWebhookEndpoint500), + orElse: unexpectedStatus + })) + ) + ) + ), + "ListWebhookEventTypes": (options: Parameters[0]) => + HttpClientRequest.get("/webhook_event_types").pipe( + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(ListWebhookEventTypes200), + "401": decodeError("ListWebhookEventTypes401", ListWebhookEventTypes401), + orElse: unexpectedStatus + })) + ), + "betaCreateResponse": (options: Parameters[0]) => + HttpClientRequest.post("/responses?beta=true").pipe( + HttpClientRequest.setHeaders({ "openai-beta": options.params?.["openai-beta"] ?? undefined }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaCreateResponse200), + "400": decodeError("BetaCreateResponse400", BetaCreateResponse400), + "404": decodeError("BetaCreateResponse404", BetaCreateResponse404), + "429": decodeError("BetaCreateResponse429", BetaCreateResponse429), + "503": decodeError("BetaCreateResponse503", BetaCreateResponse503), + orElse: unexpectedStatus + })) + ), + "betaCreateResponseSse": (options) => + HttpClientRequest.post("/responses?beta=true").pipe( + HttpClientRequest.setHeaders({ "openai-beta": options.params?.["openai-beta"] ?? undefined }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + sseRequest(BetaCreateResponse200Sse) + ), + "betaGetResponse": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "?beta=true" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "include": options?.params?.["include"] as any, + "stream": options?.params?.["stream"] as any, + "starting_after": options?.params?.["starting_after"] as any, + "include_obfuscation": options?.params?.["include_obfuscation"] as any + }), + HttpClientRequest.setHeaders({ "openai-beta": options?.params?.["openai-beta"] ?? undefined }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaGetResponse200), + "404": decodeError("BetaGetResponse404", BetaGetResponse404), + "429": decodeError("BetaGetResponse429", BetaGetResponse429), + orElse: unexpectedStatus + })) + ) + ) + ), + "betaDeleteResponse": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.delete, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "?beta=true" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setHeaders({ "openai-beta": options?.params?.["openai-beta"] ?? undefined }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaDeleteResponse200), + "404": decodeError("BetaDeleteResponse404", BetaDeleteResponse404), + "429": decodeError("BetaDeleteResponse429", BetaDeleteResponse429), + orElse: unexpectedStatus + })) + ) + ) + ), + "betaCancelResponse": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.post, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "/cancel?beta=true" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setHeaders({ "openai-beta": options?.params?.["openai-beta"] ?? undefined }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaCancelResponse200), + "404": decodeError("BetaCancelResponse404", BetaCancelResponse404), + "429": decodeError("BetaCancelResponse429", BetaCancelResponse429), + orElse: unexpectedStatus + })) + ) + ) + ), + "betaCompactconversation": (options: Parameters[0]) => + HttpClientRequest.post("/responses/compact?beta=true").pipe( + HttpClientRequest.setHeaders({ "openai-beta": options.params?.["openai-beta"] ?? undefined }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaCompactconversation200), + "400": decodeError("BetaCompactconversation400", BetaCompactconversation400), + "429": decodeError("BetaCompactconversation429", BetaCompactconversation429), + "503": decodeError("BetaCompactconversation503", BetaCompactconversation503), + orElse: unexpectedStatus + })) + ), + "betaListInputItems": (responseId, options: Parameters[1]) => + __makePathRequest( + HttpClientRequest.get, + [responseId], + () => "/responses/" + __encodePathParam(responseId) + "/input_items?beta=true" + ).pipe( + Effect.flatMap((request) => + request.pipe( + HttpClientRequest.setUrlParams({ + "limit": options?.params?.["limit"] as any, + "order": options?.params?.["order"] as any, + "after": options?.params?.["after"] as any, + "include": options?.params?.["include"] as any + }), + HttpClientRequest.setHeaders({ "openai-beta": options?.params?.["openai-beta"] ?? undefined }), + withResponse(options?.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaListInputItems200), + "404": decodeError("BetaListInputItems404", BetaListInputItems404), + "429": decodeError("BetaListInputItems429", BetaListInputItems429), + orElse: unexpectedStatus + })) + ) + ) + ), + "betaGetinputtokencounts": (options: Parameters[0]) => + HttpClientRequest.post("/responses/input_tokens?beta=true").pipe( + HttpClientRequest.setHeaders({ "openai-beta": options.params?.["openai-beta"] ?? undefined }), + HttpClientRequest.bodyJsonUnsafe(options.payload), + withResponse(options.config)(HttpClientResponse.matchStatus({ + "2xx": decodeSuccess(BetaGetinputtokencounts200), + "400": decodeError("BetaGetinputtokencounts400", BetaGetinputtokencounts400), + "429": decodeError("BetaGetinputtokencounts429", BetaGetinputtokencounts429), + orElse: unexpectedStatus + })) + ) + } +} + +export interface OpenAiClient { + readonly httpClient: HttpClient.HttpClient + /** + * Returns a list of assistants. + */ + readonly "listAssistants": { + ( + options: { readonly params?: typeof ListAssistantsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListAssistants429", ListAssistants429> + > + ( + options: { + readonly params?: typeof ListAssistantsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListAssistants429", ListAssistants429> + > + } + /** + * Create an assistant with a model and instructions. + */ + readonly "createAssistant": { + ( + options: { readonly payload: typeof CreateAssistantRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateAssistant429", CreateAssistant429> + > + ( + options: { readonly payload: typeof CreateAssistantRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateAssistant429", CreateAssistant429> + > + } + /** + * Retrieves an assistant. + */ + readonly "getAssistant": { + ( + assistantId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetAssistant429", GetAssistant429> + > + ( + assistantId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetAssistant429", GetAssistant429> + > + } + /** + * Modifies an assistant. + */ + readonly "modifyAssistant": { + ( + assistantId: string, + options: { readonly payload: typeof ModifyAssistantRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyAssistant429", ModifyAssistant429> + > + ( + assistantId: string, + options: { readonly payload: typeof ModifyAssistantRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyAssistant429", ModifyAssistant429> + > + } + /** + * Delete an assistant. + */ + readonly "deleteAssistant": { + ( + assistantId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"DeleteAssistant429", DeleteAssistant429> + > + ( + assistantId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"DeleteAssistant429", DeleteAssistant429> + > + } + /** + * Generates audio from the input text. + * + * Returns the audio file content, or a stream of audio events. + */ + readonly "createSpeech": { + ( + options: { readonly payload: typeof CreateSpeechRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateSpeech400", CreateSpeech400> + | OpenAiClientError<"CreateSpeech401", CreateSpeech401> + | OpenAiClientError<"CreateSpeech429", CreateSpeech429> + | OpenAiClientError<"CreateSpeech500", CreateSpeech500> + | OpenAiClientError<"CreateSpeech503", CreateSpeech503> + > + ( + options: { readonly payload: typeof CreateSpeechRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateSpeech400", CreateSpeech400> + | OpenAiClientError<"CreateSpeech401", CreateSpeech401> + | OpenAiClientError<"CreateSpeech429", CreateSpeech429> + | OpenAiClientError<"CreateSpeech500", CreateSpeech500> + | OpenAiClientError<"CreateSpeech503", CreateSpeech503> + > + } + /** + * Generates audio from the input text. + * + * Returns the audio file content, or a stream of audio events. + */ + readonly "createSpeechSse": ( + options: { readonly payload: typeof CreateSpeechRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateSpeech200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateSpeech200Sse.DecodingServices + > + /** + * Generates audio from the input text. + * + * Returns the audio file content, or a stream of audio events. + */ + readonly "createSpeechStream": ( + options: { readonly payload: typeof CreateSpeechRequestJson.Encoded } + ) => Stream.Stream + /** + * Transcribes audio into the input language. + * + * Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + * format, plain text in `text`, `srt`, or `vtt` format, or a stream of + * transcript events. Supported formats depend on the model. + */ + readonly "createTranscription": { + ( + options: { readonly payload: typeof CreateTranscriptionRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateTranscription400", CreateTranscription400> + | OpenAiClientError<"CreateTranscription401", CreateTranscription401> + | OpenAiClientError<"CreateTranscription413", CreateTranscription413> + | OpenAiClientError<"CreateTranscription429", CreateTranscription429> + | OpenAiClientError<"CreateTranscription500", CreateTranscription500> + | OpenAiClientError<"CreateTranscription502", CreateTranscription502> + | OpenAiClientError<"CreateTranscription503", CreateTranscription503> + > + ( + options: { + readonly payload: typeof CreateTranscriptionRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateTranscription400", CreateTranscription400> + | OpenAiClientError<"CreateTranscription401", CreateTranscription401> + | OpenAiClientError<"CreateTranscription413", CreateTranscription413> + | OpenAiClientError<"CreateTranscription429", CreateTranscription429> + | OpenAiClientError<"CreateTranscription500", CreateTranscription500> + | OpenAiClientError<"CreateTranscription502", CreateTranscription502> + | OpenAiClientError<"CreateTranscription503", CreateTranscription503> + > + } + /** + * Transcribes audio into the input language. + * + * Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + * format, plain text in `text`, `srt`, or `vtt` format, or a stream of + * transcript events. Supported formats depend on the model. + */ + readonly "createTranscriptionSse": ( + options: { readonly payload: typeof CreateTranscriptionRequestFormData.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateTranscription200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateTranscription200Sse.DecodingServices + > + /** + * Translates audio into English. + */ + readonly "createTranslation": { + ( + options: { readonly payload: typeof CreateTranslationRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateTranslation400", CreateTranslation400> + | OpenAiClientError<"CreateTranslation401", CreateTranslation401> + | OpenAiClientError<"CreateTranslation413", CreateTranslation413> + | OpenAiClientError<"CreateTranslation429", CreateTranslation429> + | OpenAiClientError<"CreateTranslation500", CreateTranslation500> + | OpenAiClientError<"CreateTranslation502", CreateTranslation502> + | OpenAiClientError<"CreateTranslation503", CreateTranslation503> + > + ( + options: { + readonly payload: typeof CreateTranslationRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateTranslation400", CreateTranslation400> + | OpenAiClientError<"CreateTranslation401", CreateTranslation401> + | OpenAiClientError<"CreateTranslation413", CreateTranslation413> + | OpenAiClientError<"CreateTranslation429", CreateTranslation429> + | OpenAiClientError<"CreateTranslation500", CreateTranslation500> + | OpenAiClientError<"CreateTranslation502", CreateTranslation502> + | OpenAiClientError<"CreateTranslation503", CreateTranslation503> + > + } + /** + * Returns a list of voice consent recordings. + */ + readonly "listVoiceConsents": { + ( + options: { readonly params?: typeof ListVoiceConsentsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVoiceConsents400", ListVoiceConsents400> + | OpenAiClientError<"ListVoiceConsents404", ListVoiceConsents404> + > + ( + options: { + readonly params?: typeof ListVoiceConsentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVoiceConsents400", ListVoiceConsents400> + | OpenAiClientError<"ListVoiceConsents404", ListVoiceConsents404> + > + } + /** + * Upload a voice consent recording. + */ + readonly "createVoiceConsent": { + ( + options: { readonly payload: typeof CreateVoiceConsentRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVoiceConsent400", CreateVoiceConsent400> + | OpenAiClientError<"CreateVoiceConsent404", CreateVoiceConsent404> + > + ( + options: { + readonly payload: typeof CreateVoiceConsentRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVoiceConsent400", CreateVoiceConsent400> + | OpenAiClientError<"CreateVoiceConsent404", CreateVoiceConsent404> + > + } + /** + * Retrieves a voice consent recording. + */ + readonly "getVoiceConsent": { + ( + consentId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVoiceConsent400", GetVoiceConsent400> + | OpenAiClientError<"GetVoiceConsent404", GetVoiceConsent404> + > + ( + consentId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVoiceConsent400", GetVoiceConsent400> + | OpenAiClientError<"GetVoiceConsent404", GetVoiceConsent404> + > + } + /** + * Updates a voice consent recording (metadata only). + */ + readonly "updateVoiceConsent": { + ( + consentId: string, + options: { readonly payload: typeof UpdateVoiceConsentRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateVoiceConsent400", UpdateVoiceConsent400> + | OpenAiClientError<"UpdateVoiceConsent404", UpdateVoiceConsent404> + > + ( + consentId: string, + options: { readonly payload: typeof UpdateVoiceConsentRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateVoiceConsent400", UpdateVoiceConsent400> + | OpenAiClientError<"UpdateVoiceConsent404", UpdateVoiceConsent404> + > + } + /** + * Deletes a voice consent recording. + */ + readonly "deleteVoiceConsent": { + ( + consentId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVoiceConsent400", DeleteVoiceConsent400> + | OpenAiClientError<"DeleteVoiceConsent404", DeleteVoiceConsent404> + > + ( + consentId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVoiceConsent400", DeleteVoiceConsent400> + | OpenAiClientError<"DeleteVoiceConsent404", DeleteVoiceConsent404> + > + } + /** + * Create a custom voice you can use for audio output (for example, in Text-to-Speech and the Realtime API). This requires an audio sample and a previously uploaded consent recording. + * + * Send `name`, `audio_sample`, and the `consent` recording ID as multipart form data. The optional `type` defaults to `audio_sample`. + * + * Returns the saved voice's metadata. See the [custom voices guide](https://developers.openai.com/api/docs/guides/text-to-speech#custom-voices) for requirements and best practices. Custom voices are limited to eligible customers. + */ + readonly "createVoice": { + ( + options: { readonly payload: typeof CreateVoiceRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVoice400", CreateVoice400> + | OpenAiClientError<"CreateVoice404", CreateVoice404> + | OpenAiClientError<"CreateVoice500", CreateVoice500> + > + ( + options: { readonly payload: typeof CreateVoiceRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVoice400", CreateVoice400> + | OpenAiClientError<"CreateVoice404", CreateVoice404> + | OpenAiClientError<"CreateVoice500", CreateVoice500> + > + } + /** + * List your organization's batches. + */ + readonly "listBatches": { + ( + options: { readonly params?: typeof ListBatchesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListBatches400", ListBatches400> + | OpenAiClientError<"ListBatches404", ListBatches404> + | OpenAiClientError<"ListBatches429", ListBatches429> + > + ( + options: + | { readonly params?: typeof ListBatchesParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListBatches400", ListBatches400> + | OpenAiClientError<"ListBatches404", ListBatches404> + | OpenAiClientError<"ListBatches429", ListBatches429> + > + } + /** + * Creates and executes a batch from an uploaded file of requests + */ + readonly "createBatch": { + ( + options: { readonly payload: typeof CreateBatchRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateBatch400", CreateBatch400> + | OpenAiClientError<"CreateBatch429", CreateBatch429> + | OpenAiClientError<"CreateBatch500", CreateBatch500> + | OpenAiClientError<"CreateBatch503", CreateBatch503> + > + ( + options: { readonly payload: typeof CreateBatchRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateBatch400", CreateBatch400> + | OpenAiClientError<"CreateBatch429", CreateBatch429> + | OpenAiClientError<"CreateBatch500", CreateBatch500> + | OpenAiClientError<"CreateBatch503", CreateBatch503> + > + } + /** + * Retrieves a batch. + */ + readonly "retrieveBatch": { + ( + batchId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveBatch400", RetrieveBatch400> + | OpenAiClientError<"RetrieveBatch404", RetrieveBatch404> + | OpenAiClientError<"RetrieveBatch429", RetrieveBatch429> + > + ( + batchId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveBatch400", RetrieveBatch400> + | OpenAiClientError<"RetrieveBatch404", RetrieveBatch404> + | OpenAiClientError<"RetrieveBatch429", RetrieveBatch429> + > + } + /** + * Cancels an in-progress batch. The batch will be in status `cancelling` for up to 10 minutes, before changing to `cancelled`, where it will have partial results (if any) available in the output file. + */ + readonly "cancelBatch": { + ( + batchId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelBatch400", CancelBatch400> + | OpenAiClientError<"CancelBatch404", CancelBatch404> + | OpenAiClientError<"CancelBatch409", CancelBatch409> + | OpenAiClientError<"CancelBatch429", CancelBatch429> + > + ( + batchId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelBatch400", CancelBatch400> + | OpenAiClientError<"CancelBatch404", CancelBatch404> + | OpenAiClientError<"CancelBatch409", CancelBatch409> + | OpenAiClientError<"CancelBatch429", CancelBatch429> + > + } + /** + * List stored Chat Completions. Only Chat Completions that have been stored + * with the `store` parameter set to `true` will be returned. + */ + readonly "listChatCompletions": { + ( + options: { readonly params?: typeof ListChatCompletionsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListChatCompletions400", ListChatCompletions400> + | OpenAiClientError<"ListChatCompletions429", ListChatCompletions429> + | OpenAiClientError<"ListChatCompletions500", ListChatCompletions500> + > + ( + options: { + readonly params?: typeof ListChatCompletionsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListChatCompletions400", ListChatCompletions400> + | OpenAiClientError<"ListChatCompletions429", ListChatCompletions429> + | OpenAiClientError<"ListChatCompletions500", ListChatCompletions500> + > + } + /** + * **Starting a new project?** We recommend trying [Responses](https://developers.openai.com/api/reference/resources/responses) + * to take advantage of the latest OpenAI platform features. Compare + * [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + * + * --- + * + * Creates a model response for the given chat conversation. Learn more in the + * [text generation](https://developers.openai.com/api/docs/guides/text), [vision](https://developers.openai.com/api/docs/guides/images-vision), + * and [audio](https://developers.openai.com/api/docs/guides/audio) guides. + * + * Parameter support can differ depending on the model used to generate the + * response, particularly for newer reasoning models. Parameters that are only + * supported for reasoning models are noted below. For the current state of + * unsupported parameters in reasoning models, + * [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + * + * Returns a chat completion object, or a streamed sequence of chat completion + * chunk objects if the request is streamed. + */ + readonly "createChatCompletion": { + ( + options: { readonly payload: typeof CreateChatCompletionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateChatCompletion400", CreateChatCompletion400> + | OpenAiClientError<"CreateChatCompletion401", CreateChatCompletion401> + | OpenAiClientError<"CreateChatCompletion404", CreateChatCompletion404> + | OpenAiClientError<"CreateChatCompletion429", CreateChatCompletion429> + | OpenAiClientError<"CreateChatCompletion500", CreateChatCompletion500> + | OpenAiClientError<"CreateChatCompletion503", CreateChatCompletion503> + > + ( + options: { + readonly payload: typeof CreateChatCompletionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateChatCompletion400", CreateChatCompletion400> + | OpenAiClientError<"CreateChatCompletion401", CreateChatCompletion401> + | OpenAiClientError<"CreateChatCompletion404", CreateChatCompletion404> + | OpenAiClientError<"CreateChatCompletion429", CreateChatCompletion429> + | OpenAiClientError<"CreateChatCompletion500", CreateChatCompletion500> + | OpenAiClientError<"CreateChatCompletion503", CreateChatCompletion503> + > + } + /** + * **Starting a new project?** We recommend trying [Responses](https://developers.openai.com/api/reference/resources/responses) + * to take advantage of the latest OpenAI platform features. Compare + * [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses). + * + * --- + * + * Creates a model response for the given chat conversation. Learn more in the + * [text generation](https://developers.openai.com/api/docs/guides/text), [vision](https://developers.openai.com/api/docs/guides/images-vision), + * and [audio](https://developers.openai.com/api/docs/guides/audio) guides. + * + * Parameter support can differ depending on the model used to generate the + * response, particularly for newer reasoning models. Parameters that are only + * supported for reasoning models are noted below. For the current state of + * unsupported parameters in reasoning models, + * [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning). + * + * Returns a chat completion object, or a streamed sequence of chat completion + * chunk objects if the request is streamed. + */ + readonly "createChatCompletionSse": ( + options: { readonly payload: typeof CreateChatCompletionRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateChatCompletion200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateChatCompletion200Sse.DecodingServices + > + /** + * Get a stored chat completion. Only Chat Completions that have been created + * with the `store` parameter set to `true` will be returned. + */ + readonly "getChatCompletion": { + ( + completionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetChatCompletion404", GetChatCompletion404> + | OpenAiClientError<"GetChatCompletion429", GetChatCompletion429> + | OpenAiClientError<"GetChatCompletion500", GetChatCompletion500> + > + ( + completionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetChatCompletion404", GetChatCompletion404> + | OpenAiClientError<"GetChatCompletion429", GetChatCompletion429> + | OpenAiClientError<"GetChatCompletion500", GetChatCompletion500> + > + } + /** + * Modify a stored chat completion. Only Chat Completions that have been + * created with the `store` parameter set to `true` can be modified. Currently, + * the only supported modification is to update the `metadata` field. + */ + readonly "updateChatCompletion": { + ( + completionId: string, + options: { readonly payload: typeof UpdateChatCompletionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateChatCompletion400", UpdateChatCompletion400> + | OpenAiClientError<"UpdateChatCompletion404", UpdateChatCompletion404> + | OpenAiClientError<"UpdateChatCompletion429", UpdateChatCompletion429> + | OpenAiClientError<"UpdateChatCompletion500", UpdateChatCompletion500> + > + ( + completionId: string, + options: { + readonly payload: typeof UpdateChatCompletionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateChatCompletion400", UpdateChatCompletion400> + | OpenAiClientError<"UpdateChatCompletion404", UpdateChatCompletion404> + | OpenAiClientError<"UpdateChatCompletion429", UpdateChatCompletion429> + | OpenAiClientError<"UpdateChatCompletion500", UpdateChatCompletion500> + > + } + /** + * Delete a stored chat completion. Only Chat Completions that have been + * created with the `store` parameter set to `true` can be deleted. + */ + readonly "deleteChatCompletion": { + ( + completionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteChatCompletion404", DeleteChatCompletion404> + | OpenAiClientError<"DeleteChatCompletion429", DeleteChatCompletion429> + | OpenAiClientError<"DeleteChatCompletion500", DeleteChatCompletion500> + > + ( + completionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteChatCompletion404", DeleteChatCompletion404> + | OpenAiClientError<"DeleteChatCompletion429", DeleteChatCompletion429> + | OpenAiClientError<"DeleteChatCompletion500", DeleteChatCompletion500> + > + } + /** + * Get the messages in a stored chat completion. Only Chat Completions that + * have been created with the `store` parameter set to `true` will be + * returned. + */ + readonly "getChatCompletionMessages": { + ( + completionId: string, + options: { readonly params?: typeof GetChatCompletionMessagesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetChatCompletionMessages400", GetChatCompletionMessages400> + | OpenAiClientError<"GetChatCompletionMessages404", GetChatCompletionMessages404> + | OpenAiClientError<"GetChatCompletionMessages429", GetChatCompletionMessages429> + | OpenAiClientError<"GetChatCompletionMessages500", GetChatCompletionMessages500> + > + ( + completionId: string, + options: { + readonly params?: typeof GetChatCompletionMessagesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetChatCompletionMessages400", GetChatCompletionMessages400> + | OpenAiClientError<"GetChatCompletionMessages404", GetChatCompletionMessages404> + | OpenAiClientError<"GetChatCompletionMessages429", GetChatCompletionMessages429> + | OpenAiClientError<"GetChatCompletionMessages500", GetChatCompletionMessages500> + > + } + /** + * Creates a completion for the provided prompt and parameters. + * + * Returns a completion object, or a sequence of completion objects if the request is streamed. + */ + readonly "createCompletion": { + ( + options: { readonly payload: typeof CreateCompletionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateCompletion400", CreateCompletion400> + | OpenAiClientError<"CreateCompletion401", CreateCompletion401> + | OpenAiClientError<"CreateCompletion404", CreateCompletion404> + | OpenAiClientError<"CreateCompletion429", CreateCompletion429> + | OpenAiClientError<"CreateCompletion500", CreateCompletion500> + | OpenAiClientError<"CreateCompletion503", CreateCompletion503> + > + ( + options: { readonly payload: typeof CreateCompletionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateCompletion400", CreateCompletion400> + | OpenAiClientError<"CreateCompletion401", CreateCompletion401> + | OpenAiClientError<"CreateCompletion404", CreateCompletion404> + | OpenAiClientError<"CreateCompletion429", CreateCompletion429> + | OpenAiClientError<"CreateCompletion500", CreateCompletion500> + | OpenAiClientError<"CreateCompletion503", CreateCompletion503> + > + } + /** + * List Containers + */ + readonly "ListContainers": { + ( + options: { readonly params?: typeof ListContainersParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListContainers404", ListContainers404> + | OpenAiClientError<"ListContainers429", ListContainers429> + > + ( + options: { + readonly params?: typeof ListContainersParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListContainers404", ListContainers404> + | OpenAiClientError<"ListContainers429", ListContainers429> + > + } + /** + * Create Container + */ + readonly "CreateContainer": { + ( + options: { readonly payload: typeof CreateContainerRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateContainer400", CreateContainer400> + | OpenAiClientError<"CreateContainer429", CreateContainer429> + > + ( + options: { readonly payload: typeof CreateContainerRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateContainer400", CreateContainer400> + | OpenAiClientError<"CreateContainer429", CreateContainer429> + > + } + /** + * Retrieve Container + */ + readonly "RetrieveContainer": { + ( + containerId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveContainer404", RetrieveContainer404> + | OpenAiClientError<"RetrieveContainer429", RetrieveContainer429> + > + ( + containerId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveContainer404", RetrieveContainer404> + | OpenAiClientError<"RetrieveContainer429", RetrieveContainer429> + > + } + /** + * Delete Container + */ + readonly "DeleteContainer": { + ( + containerId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteContainer404", DeleteContainer404> + | OpenAiClientError<"DeleteContainer429", DeleteContainer429> + > + ( + containerId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteContainer404", DeleteContainer404> + | OpenAiClientError<"DeleteContainer429", DeleteContainer429> + > + } + /** + * List Container files + */ + readonly "ListContainerFiles": { + ( + containerId: string, + options: { readonly params?: typeof ListContainerFilesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListContainerFiles404", ListContainerFiles404> + | OpenAiClientError<"ListContainerFiles429", ListContainerFiles429> + > + ( + containerId: string, + options: { + readonly params?: typeof ListContainerFilesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListContainerFiles404", ListContainerFiles404> + | OpenAiClientError<"ListContainerFiles429", ListContainerFiles429> + > + } + /** + * Create a Container File + * + * You can send either a multipart/form-data request with the raw file content, or a JSON request with a file ID. + */ + readonly "CreateContainerFile": { + ( + containerId: string, + options: { readonly payload: typeof CreateContainerFileRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateContainerFile400", CreateContainerFile400> + | OpenAiClientError<"CreateContainerFile404", CreateContainerFile404> + | OpenAiClientError<"CreateContainerFile429", CreateContainerFile429> + > + ( + containerId: string, + options: { + readonly payload: typeof CreateContainerFileRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateContainerFile400", CreateContainerFile400> + | OpenAiClientError<"CreateContainerFile404", CreateContainerFile404> + | OpenAiClientError<"CreateContainerFile429", CreateContainerFile429> + > + } + /** + * Retrieve Container File + */ + readonly "RetrieveContainerFile": { + ( + containerId: string, + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveContainerFile404", RetrieveContainerFile404> + | OpenAiClientError<"RetrieveContainerFile429", RetrieveContainerFile429> + > + ( + containerId: string, + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveContainerFile404", RetrieveContainerFile404> + | OpenAiClientError<"RetrieveContainerFile429", RetrieveContainerFile429> + > + } + /** + * Delete Container File + */ + readonly "DeleteContainerFile": { + ( + containerId: string, + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteContainerFile404", DeleteContainerFile404> + | OpenAiClientError<"DeleteContainerFile429", DeleteContainerFile429> + > + ( + containerId: string, + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteContainerFile404", DeleteContainerFile404> + | OpenAiClientError<"DeleteContainerFile429", DeleteContainerFile429> + > + } + /** + * Retrieve Container File Content + */ + readonly "RetrieveContainerFileContent": { + ( + containerId: string, + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveContainerFileContent404", RetrieveContainerFileContent404> + | OpenAiClientError<"RetrieveContainerFileContent429", RetrieveContainerFileContent429> + > + ( + containerId: string, + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveContainerFileContent404", RetrieveContainerFileContent404> + | OpenAiClientError<"RetrieveContainerFileContent429", RetrieveContainerFileContent429> + > + } + /** + * Retrieve Container File Content + */ + readonly "RetrieveContainerFileContentStream": ( + containerId: string, + fileId: string + ) => Stream.Stream + /** + * List all items for a conversation with the given ID. + */ + readonly "listConversationItems": { + ( + conversationId: string, + options: { readonly params?: typeof ListConversationItemsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListConversationItems400", ListConversationItems400> + | OpenAiClientError<"ListConversationItems404", ListConversationItems404> + | OpenAiClientError<"ListConversationItems429", ListConversationItems429> + > + ( + conversationId: string, + options: { + readonly params?: typeof ListConversationItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListConversationItems400", ListConversationItems400> + | OpenAiClientError<"ListConversationItems404", ListConversationItems404> + | OpenAiClientError<"ListConversationItems429", ListConversationItems429> + > + } + /** + * Create items in a conversation with the given ID. + */ + readonly "createConversationItems": { + ( + conversationId: string, + options: { + readonly params?: typeof CreateConversationItemsParams.Encoded | undefined + readonly payload: typeof CreateConversationItemsRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateConversationItems400", CreateConversationItems400> + | OpenAiClientError<"CreateConversationItems404", CreateConversationItems404> + | OpenAiClientError<"CreateConversationItems429", CreateConversationItems429> + | OpenAiClientError<"CreateConversationItems500", CreateConversationItems500> + > + ( + conversationId: string, + options: { + readonly params?: typeof CreateConversationItemsParams.Encoded | undefined + readonly payload: typeof CreateConversationItemsRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateConversationItems400", CreateConversationItems400> + | OpenAiClientError<"CreateConversationItems404", CreateConversationItems404> + | OpenAiClientError<"CreateConversationItems429", CreateConversationItems429> + | OpenAiClientError<"CreateConversationItems500", CreateConversationItems500> + > + } + /** + * Get a single item from a conversation with the given IDs. + */ + readonly "getConversationItem": { + ( + conversationId: string, + itemId: string, + options: { readonly params?: typeof GetConversationItemParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetConversationItem404", GetConversationItem404> + | OpenAiClientError<"GetConversationItem429", GetConversationItem429> + > + ( + conversationId: string, + itemId: string, + options: { + readonly params?: typeof GetConversationItemParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetConversationItem404", GetConversationItem404> + | OpenAiClientError<"GetConversationItem429", GetConversationItem429> + > + } + /** + * Delete an item from a conversation with the given IDs. + */ + readonly "deleteConversationItem": { + ( + conversationId: string, + itemId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteConversationItem400", DeleteConversationItem400> + | OpenAiClientError<"DeleteConversationItem404", DeleteConversationItem404> + | OpenAiClientError<"DeleteConversationItem429", DeleteConversationItem429> + | OpenAiClientError<"DeleteConversationItem500", DeleteConversationItem500> + > + ( + conversationId: string, + itemId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteConversationItem400", DeleteConversationItem400> + | OpenAiClientError<"DeleteConversationItem404", DeleteConversationItem404> + | OpenAiClientError<"DeleteConversationItem429", DeleteConversationItem429> + | OpenAiClientError<"DeleteConversationItem500", DeleteConversationItem500> + > + } + /** + * Creates an embedding vector representing the input text. + */ + readonly "createEmbedding": { + ( + options: { readonly payload: typeof CreateEmbeddingRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateEmbedding400", CreateEmbedding400> + | OpenAiClientError<"CreateEmbedding401", CreateEmbedding401> + | OpenAiClientError<"CreateEmbedding429", CreateEmbedding429> + | OpenAiClientError<"CreateEmbedding500", CreateEmbedding500> + | OpenAiClientError<"CreateEmbedding503", CreateEmbedding503> + > + ( + options: { readonly payload: typeof CreateEmbeddingRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateEmbedding400", CreateEmbedding400> + | OpenAiClientError<"CreateEmbedding401", CreateEmbedding401> + | OpenAiClientError<"CreateEmbedding429", CreateEmbedding429> + | OpenAiClientError<"CreateEmbedding500", CreateEmbedding500> + | OpenAiClientError<"CreateEmbedding503", CreateEmbedding503> + > + } + /** + * List evaluations for a project. + */ + readonly "listEvals": { + ( + options: { readonly params?: typeof ListEvalsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListEvals429", ListEvals429> + > + ( + options: + | { readonly params?: typeof ListEvalsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListEvals429", ListEvals429> + > + } + /** + * Create the structure of an evaluation that can be used to test a model's performance. + * An evaluation is a set of testing criteria and the config for a data source, which dictates the schema of the data used in the evaluation. After creating an evaluation, you can run it on different models and model parameters. We support several types of graders and datasources. + * For more information, see the [Evals guide](https://developers.openai.com/api/docs/guides/evals). + */ + readonly "createEval": { + ( + options: { readonly payload: typeof CreateEvalRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateEval400", CreateEval400> + | OpenAiClientError<"CreateEval429", CreateEval429> + > + ( + options: { readonly payload: typeof CreateEvalRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateEval400", CreateEval400> + | OpenAiClientError<"CreateEval429", CreateEval429> + > + } + /** + * Get an evaluation by ID. + */ + readonly "getEval": { + ( + evalId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEval404", GetEval404> + | OpenAiClientError<"GetEval429", GetEval429> + > + ( + evalId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEval404", GetEval404> + | OpenAiClientError<"GetEval429", GetEval429> + > + } + /** + * Update certain properties of an evaluation. + */ + readonly "updateEval": { + ( + evalId: string, + options: { readonly payload: typeof UpdateEvalRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateEval400", UpdateEval400> + | OpenAiClientError<"UpdateEval404", UpdateEval404> + | OpenAiClientError<"UpdateEval429", UpdateEval429> + > + ( + evalId: string, + options: { readonly payload: typeof UpdateEvalRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateEval400", UpdateEval400> + | OpenAiClientError<"UpdateEval404", UpdateEval404> + | OpenAiClientError<"UpdateEval429", UpdateEval429> + > + } + /** + * Delete an evaluation. + */ + readonly "deleteEval": { + ( + evalId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteEval400", DeleteEval400> + | OpenAiClientError<"DeleteEval404", DeleteEval404> + | OpenAiClientError<"DeleteEval429", DeleteEval429> + > + ( + evalId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteEval400", DeleteEval400> + | OpenAiClientError<"DeleteEval404", DeleteEval404> + | OpenAiClientError<"DeleteEval429", DeleteEval429> + > + } + /** + * Get a list of runs for an evaluation. + */ + readonly "getEvalRuns": { + ( + evalId: string, + options: { readonly params?: typeof GetEvalRunsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRuns400", GetEvalRuns400> + | OpenAiClientError<"GetEvalRuns404", GetEvalRuns404> + | OpenAiClientError<"GetEvalRuns429", GetEvalRuns429> + > + ( + evalId: string, + options: + | { readonly params?: typeof GetEvalRunsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRuns400", GetEvalRuns400> + | OpenAiClientError<"GetEvalRuns404", GetEvalRuns404> + | OpenAiClientError<"GetEvalRuns429", GetEvalRuns429> + > + } + /** + * Kicks off a new run for a given evaluation, specifying the data source, and what model configuration to use to test. The datasource will be validated against the schema specified in the config of the evaluation. + */ + readonly "createEvalRun": { + ( + evalId: string, + options: { readonly payload: typeof CreateEvalRunRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateEvalRun400", CreateEvalRun400> + | OpenAiClientError<"CreateEvalRun404", CreateEvalRun404> + | OpenAiClientError<"CreateEvalRun429", CreateEvalRun429> + | OpenAiClientError<"CreateEvalRun500", CreateEvalRun500> + > + ( + evalId: string, + options: { readonly payload: typeof CreateEvalRunRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateEvalRun400", CreateEvalRun400> + | OpenAiClientError<"CreateEvalRun404", CreateEvalRun404> + | OpenAiClientError<"CreateEvalRun429", CreateEvalRun429> + | OpenAiClientError<"CreateEvalRun500", CreateEvalRun500> + > + } + /** + * Get an evaluation run by ID. + */ + readonly "getEvalRun": { + ( + evalId: string, + runId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRun400", GetEvalRun400> + | OpenAiClientError<"GetEvalRun404", GetEvalRun404> + | OpenAiClientError<"GetEvalRun429", GetEvalRun429> + > + ( + evalId: string, + runId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRun400", GetEvalRun400> + | OpenAiClientError<"GetEvalRun404", GetEvalRun404> + | OpenAiClientError<"GetEvalRun429", GetEvalRun429> + > + } + /** + * Delete an eval run. + */ + readonly "deleteEvalRun": { + ( + evalId: string, + runId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteEvalRun400", DeleteEvalRun400> + | OpenAiClientError<"DeleteEvalRun404", DeleteEvalRun404> + | OpenAiClientError<"DeleteEvalRun429", DeleteEvalRun429> + > + ( + evalId: string, + runId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteEvalRun400", DeleteEvalRun400> + | OpenAiClientError<"DeleteEvalRun404", DeleteEvalRun404> + | OpenAiClientError<"DeleteEvalRun429", DeleteEvalRun429> + > + } + /** + * Cancel an ongoing evaluation run. + */ + readonly "cancelEvalRun": { + ( + evalId: string, + runId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CancelEvalRun429", CancelEvalRun429> + > + ( + evalId: string, + runId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CancelEvalRun429", CancelEvalRun429> + > + } + /** + * Get a list of output items for an evaluation run. + */ + readonly "getEvalRunOutputItems": { + ( + evalId: string, + runId: string, + options: { readonly params?: typeof GetEvalRunOutputItemsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRunOutputItems400", GetEvalRunOutputItems400> + | OpenAiClientError<"GetEvalRunOutputItems404", GetEvalRunOutputItems404> + | OpenAiClientError<"GetEvalRunOutputItems429", GetEvalRunOutputItems429> + > + ( + evalId: string, + runId: string, + options: { + readonly params?: typeof GetEvalRunOutputItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRunOutputItems400", GetEvalRunOutputItems400> + | OpenAiClientError<"GetEvalRunOutputItems404", GetEvalRunOutputItems404> + | OpenAiClientError<"GetEvalRunOutputItems429", GetEvalRunOutputItems429> + > + } + /** + * Get an evaluation run output item by ID. + */ + readonly "getEvalRunOutputItem": { + ( + evalId: string, + runId: string, + outputItemId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRunOutputItem400", GetEvalRunOutputItem400> + | OpenAiClientError<"GetEvalRunOutputItem404", GetEvalRunOutputItem404> + | OpenAiClientError<"GetEvalRunOutputItem429", GetEvalRunOutputItem429> + > + ( + evalId: string, + runId: string, + outputItemId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetEvalRunOutputItem400", GetEvalRunOutputItem400> + | OpenAiClientError<"GetEvalRunOutputItem404", GetEvalRunOutputItem404> + | OpenAiClientError<"GetEvalRunOutputItem429", GetEvalRunOutputItem429> + > + } + /** + * Returns a list of files. + */ + readonly "listFiles": { + ( + options: { readonly params?: typeof ListFilesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFiles400", ListFiles400> + | OpenAiClientError<"ListFiles404", ListFiles404> + | OpenAiClientError<"ListFiles429", ListFiles429> + > + ( + options: + | { readonly params?: typeof ListFilesParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFiles400", ListFiles400> + | OpenAiClientError<"ListFiles404", ListFiles404> + | OpenAiClientError<"ListFiles429", ListFiles429> + > + } + /** + * Upload a file that can be used across various endpoints. Individual files + * can be up to 512 MB, and each project can store up to 2.5 TB of files in + * total. There is no organization-wide storage limit. Uploads to this + * endpoint are rate-limited to 1,000 requests per minute per authenticated + * user. + * + * - The Assistants API supports files up to 2 million tokens and of specific + * file types. See the [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools) for + * details. + * - The Fine-tuning API only supports `.jsonl` files. The input also has + * certain required formats for fine-tuning + * [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) or + * [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) models. + * - The Batch API only supports `.jsonl` files up to 200 MB in size. The input + * also has a specific required + * [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file). + * - For Retrieval or `file_search` ingestion, upload files here first. If + * you need to attach multiple uploaded files to the same vector store, use + * [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) + * instead of attaching them one by one. Vector store attachment has separate + * limits from file upload, including 2,000 attached files per minute per + * organization. + * + * Please [contact us](https://help.openai.com/) if you need to increase these + * storage limits. + */ + readonly "createFile": { + ( + options: { readonly payload: typeof CreateFileRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateFile400", CreateFile400> + | OpenAiClientError<"CreateFile429", CreateFile429> + | OpenAiClientError<"CreateFile500", CreateFile500> + > + ( + options: { readonly payload: typeof CreateFileRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateFile400", CreateFile400> + | OpenAiClientError<"CreateFile429", CreateFile429> + | OpenAiClientError<"CreateFile500", CreateFile500> + > + } + /** + * Returns information about a specific file. + */ + readonly "retrieveFile": { + ( + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveFile404", RetrieveFile404> + | OpenAiClientError<"RetrieveFile429", RetrieveFile429> + > + ( + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveFile404", RetrieveFile404> + | OpenAiClientError<"RetrieveFile429", RetrieveFile429> + > + } + /** + * Delete a file and remove it from all vector stores. + */ + readonly "deleteFile": { + ( + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteFile404", DeleteFile404> + | OpenAiClientError<"DeleteFile429", DeleteFile429> + > + ( + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteFile404", DeleteFile404> + | OpenAiClientError<"DeleteFile429", DeleteFile429> + > + } + /** + * Returns a response containing the contents of the specified file. + */ + readonly "downloadFile": { + ( + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DownloadFile400", DownloadFile400> + | OpenAiClientError<"DownloadFile404", DownloadFile404> + | OpenAiClientError<"DownloadFile429", DownloadFile429> + | OpenAiClientError<"DownloadFile500", DownloadFile500> + > + ( + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DownloadFile400", DownloadFile400> + | OpenAiClientError<"DownloadFile404", DownloadFile404> + | OpenAiClientError<"DownloadFile429", DownloadFile429> + | OpenAiClientError<"DownloadFile500", DownloadFile500> + > + } + /** + * Returns a response containing the contents of the specified file. + */ + readonly "downloadFileStream": (fileId: string) => Stream.Stream + /** + * Run a grader. + */ + readonly "runGrader": { + ( + options: { readonly payload: typeof RunGraderRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RunGrader400", RunGrader400> + | OpenAiClientError<"RunGrader429", RunGrader429> + > + ( + options: { readonly payload: typeof RunGraderRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RunGrader400", RunGrader400> + | OpenAiClientError<"RunGrader429", RunGrader429> + > + } + /** + * Validate a grader. + */ + readonly "validateGrader": { + ( + options: { readonly payload: typeof ValidateGraderRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ValidateGrader400", ValidateGrader400> + | OpenAiClientError<"ValidateGrader429", ValidateGrader429> + > + ( + options: { readonly payload: typeof ValidateGraderRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ValidateGrader400", ValidateGrader400> + | OpenAiClientError<"ValidateGrader429", ValidateGrader429> + > + } + /** + * **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + * + * Organization owners can use this endpoint to view all permissions for a fine-tuned model checkpoint. + */ + readonly "listFineTuningCheckpointPermissions": { + ( + fineTunedModelCheckpoint: string, + options: { + readonly params?: typeof ListFineTuningCheckpointPermissionsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFineTuningCheckpointPermissions400", ListFineTuningCheckpointPermissions400> + | OpenAiClientError<"ListFineTuningCheckpointPermissions429", ListFineTuningCheckpointPermissions429> + > + ( + fineTunedModelCheckpoint: string, + options: { + readonly params?: typeof ListFineTuningCheckpointPermissionsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFineTuningCheckpointPermissions400", ListFineTuningCheckpointPermissions400> + | OpenAiClientError<"ListFineTuningCheckpointPermissions429", ListFineTuningCheckpointPermissions429> + > + } + /** + * **NOTE:** Calling this endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + * + * This enables organization owners to share fine-tuned models with other projects in their organization. + */ + readonly "createFineTuningCheckpointPermission": { + ( + fineTunedModelCheckpoint: string, + options: { + readonly payload: typeof CreateFineTuningCheckpointPermissionRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateFineTuningCheckpointPermission400", CreateFineTuningCheckpointPermission400> + | OpenAiClientError<"CreateFineTuningCheckpointPermission404", CreateFineTuningCheckpointPermission404> + | OpenAiClientError<"CreateFineTuningCheckpointPermission429", CreateFineTuningCheckpointPermission429> + > + ( + fineTunedModelCheckpoint: string, + options: { + readonly payload: typeof CreateFineTuningCheckpointPermissionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateFineTuningCheckpointPermission400", CreateFineTuningCheckpointPermission400> + | OpenAiClientError<"CreateFineTuningCheckpointPermission404", CreateFineTuningCheckpointPermission404> + | OpenAiClientError<"CreateFineTuningCheckpointPermission429", CreateFineTuningCheckpointPermission429> + > + } + /** + * **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys). + * + * Organization owners can use this endpoint to delete a permission for a fine-tuned model checkpoint. + */ + readonly "deleteFineTuningCheckpointPermission": { + ( + fineTunedModelCheckpoint: string, + permissionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteFineTuningCheckpointPermission400", DeleteFineTuningCheckpointPermission400> + | OpenAiClientError<"DeleteFineTuningCheckpointPermission404", DeleteFineTuningCheckpointPermission404> + | OpenAiClientError<"DeleteFineTuningCheckpointPermission429", DeleteFineTuningCheckpointPermission429> + > + ( + fineTunedModelCheckpoint: string, + permissionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteFineTuningCheckpointPermission400", DeleteFineTuningCheckpointPermission400> + | OpenAiClientError<"DeleteFineTuningCheckpointPermission404", DeleteFineTuningCheckpointPermission404> + | OpenAiClientError<"DeleteFineTuningCheckpointPermission429", DeleteFineTuningCheckpointPermission429> + > + } + /** + * List your organization's fine-tuning jobs + */ + readonly "listPaginatedFineTuningJobs": { + ( + options: { + readonly params?: typeof ListPaginatedFineTuningJobsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListPaginatedFineTuningJobs400", ListPaginatedFineTuningJobs400> + | OpenAiClientError<"ListPaginatedFineTuningJobs429", ListPaginatedFineTuningJobs429> + > + ( + options: { + readonly params?: typeof ListPaginatedFineTuningJobsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListPaginatedFineTuningJobs400", ListPaginatedFineTuningJobs400> + | OpenAiClientError<"ListPaginatedFineTuningJobs429", ListPaginatedFineTuningJobs429> + > + } + /** + * Creates a fine-tuning job which begins the process of creating a new model from a given dataset. + * + * Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. + * + * [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) + */ + readonly "createFineTuningJob": { + ( + options: { readonly payload: typeof CreateFineTuningJobRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateFineTuningJob400", CreateFineTuningJob400> + | OpenAiClientError<"CreateFineTuningJob429", CreateFineTuningJob429> + > + ( + options: { readonly payload: typeof CreateFineTuningJobRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateFineTuningJob400", CreateFineTuningJob400> + | OpenAiClientError<"CreateFineTuningJob429", CreateFineTuningJob429> + > + } + /** + * Get info about a fine-tuning job. + * + * [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) + */ + readonly "retrieveFineTuningJob": { + ( + fineTuningJobId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveFineTuningJob404", RetrieveFineTuningJob404> + | OpenAiClientError<"RetrieveFineTuningJob429", RetrieveFineTuningJob429> + > + ( + fineTuningJobId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveFineTuningJob404", RetrieveFineTuningJob404> + | OpenAiClientError<"RetrieveFineTuningJob429", RetrieveFineTuningJob429> + > + } + /** + * Immediately cancel a fine-tune job. + */ + readonly "cancelFineTuningJob": { + ( + fineTuningJobId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelFineTuningJob400", CancelFineTuningJob400> + | OpenAiClientError<"CancelFineTuningJob404", CancelFineTuningJob404> + | OpenAiClientError<"CancelFineTuningJob429", CancelFineTuningJob429> + > + ( + fineTuningJobId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelFineTuningJob400", CancelFineTuningJob400> + | OpenAiClientError<"CancelFineTuningJob404", CancelFineTuningJob404> + | OpenAiClientError<"CancelFineTuningJob429", CancelFineTuningJob429> + > + } + /** + * List checkpoints for a fine-tuning job. + */ + readonly "listFineTuningJobCheckpoints": { + ( + fineTuningJobId: string, + options: { + readonly params?: typeof ListFineTuningJobCheckpointsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFineTuningJobCheckpoints400", ListFineTuningJobCheckpoints400> + | OpenAiClientError<"ListFineTuningJobCheckpoints404", ListFineTuningJobCheckpoints404> + | OpenAiClientError<"ListFineTuningJobCheckpoints429", ListFineTuningJobCheckpoints429> + > + ( + fineTuningJobId: string, + options: { + readonly params?: typeof ListFineTuningJobCheckpointsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFineTuningJobCheckpoints400", ListFineTuningJobCheckpoints400> + | OpenAiClientError<"ListFineTuningJobCheckpoints404", ListFineTuningJobCheckpoints404> + | OpenAiClientError<"ListFineTuningJobCheckpoints429", ListFineTuningJobCheckpoints429> + > + } + /** + * Get status updates for a fine-tuning job. + */ + readonly "listFineTuningEvents": { + ( + fineTuningJobId: string, + options: { readonly params?: typeof ListFineTuningEventsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFineTuningEvents400", ListFineTuningEvents400> + | OpenAiClientError<"ListFineTuningEvents404", ListFineTuningEvents404> + | OpenAiClientError<"ListFineTuningEvents429", ListFineTuningEvents429> + > + ( + fineTuningJobId: string, + options: { + readonly params?: typeof ListFineTuningEventsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFineTuningEvents400", ListFineTuningEvents400> + | OpenAiClientError<"ListFineTuningEvents404", ListFineTuningEvents404> + | OpenAiClientError<"ListFineTuningEvents429", ListFineTuningEvents429> + > + } + /** + * Pause a fine-tune job. + */ + readonly "pauseFineTuningJob": { + ( + fineTuningJobId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"PauseFineTuningJob400", PauseFineTuningJob400> + | OpenAiClientError<"PauseFineTuningJob404", PauseFineTuningJob404> + | OpenAiClientError<"PauseFineTuningJob429", PauseFineTuningJob429> + > + ( + fineTuningJobId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"PauseFineTuningJob400", PauseFineTuningJob400> + | OpenAiClientError<"PauseFineTuningJob404", PauseFineTuningJob404> + | OpenAiClientError<"PauseFineTuningJob429", PauseFineTuningJob429> + > + } + /** + * Resume a fine-tune job. + */ + readonly "resumeFineTuningJob": { + ( + fineTuningJobId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ResumeFineTuningJob400", ResumeFineTuningJob400> + | OpenAiClientError<"ResumeFineTuningJob404", ResumeFineTuningJob404> + | OpenAiClientError<"ResumeFineTuningJob429", ResumeFineTuningJob429> + > + ( + fineTuningJobId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ResumeFineTuningJob400", ResumeFineTuningJob400> + | OpenAiClientError<"ResumeFineTuningJob404", ResumeFineTuningJob404> + | OpenAiClientError<"ResumeFineTuningJob429", ResumeFineTuningJob429> + > + } + /** + * Creates an edited or extended image given one or more source images and a prompt. This endpoint supports GPT Image models. + */ + readonly "createImageEdit": { + ( + options: { readonly payload: typeof CreateImageEditRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateImageEdit400", CreateImageEdit400> + | OpenAiClientError<"CreateImageEdit403", CreateImageEdit403> + | OpenAiClientError<"CreateImageEdit410", CreateImageEdit410> + | OpenAiClientError<"CreateImageEdit429", CreateImageEdit429> + | OpenAiClientError<"CreateImageEdit500", CreateImageEdit500> + | OpenAiClientError<"CreateImageEdit503", CreateImageEdit503> + > + ( + options: { readonly payload: typeof CreateImageEditRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateImageEdit400", CreateImageEdit400> + | OpenAiClientError<"CreateImageEdit403", CreateImageEdit403> + | OpenAiClientError<"CreateImageEdit410", CreateImageEdit410> + | OpenAiClientError<"CreateImageEdit429", CreateImageEdit429> + | OpenAiClientError<"CreateImageEdit500", CreateImageEdit500> + | OpenAiClientError<"CreateImageEdit503", CreateImageEdit503> + > + } + /** + * Creates an edited or extended image given one or more source images and a prompt. This endpoint supports GPT Image models. + */ + readonly "createImageEditSse": ( + options: { readonly payload: typeof CreateImageEditRequestFormData.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateImageEdit200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateImageEdit200Sse.DecodingServices + > + /** + * Creates an image given a prompt using a GPT Image model. [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + */ + readonly "createImage": { + ( + options: { readonly payload: typeof CreateImageRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateImage400", CreateImage400> + | OpenAiClientError<"CreateImage403", CreateImage403> + | OpenAiClientError<"CreateImage410", CreateImage410> + | OpenAiClientError<"CreateImage429", CreateImage429> + | OpenAiClientError<"CreateImage500", CreateImage500> + | OpenAiClientError<"CreateImage503", CreateImage503> + > + ( + options: { readonly payload: typeof CreateImageRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateImage400", CreateImage400> + | OpenAiClientError<"CreateImage403", CreateImage403> + | OpenAiClientError<"CreateImage410", CreateImage410> + | OpenAiClientError<"CreateImage429", CreateImage429> + | OpenAiClientError<"CreateImage500", CreateImage500> + | OpenAiClientError<"CreateImage503", CreateImage503> + > + } + /** + * Creates an image given a prompt using a GPT Image model. [Learn more](https://developers.openai.com/api/docs/guides/images-vision). + */ + readonly "createImageSse": ( + options: { readonly payload: typeof CreateImageRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateImage200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateImage200Sse.DecodingServices + > + /** + * This endpoint is retired and no longer available. Use the image edits endpoint with a GPT Image model and a prompt to create a variation of an image. The request and response schemas below describe the legacy contract. + */ + readonly "createImageVariation": { + ( + options: { readonly payload: typeof CreateImageVariationRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateImageVariation400", CreateImageVariation400> + | OpenAiClientError<"CreateImageVariation403", CreateImageVariation403> + | OpenAiClientError<"CreateImageVariation410", CreateImageVariation410> + | OpenAiClientError<"CreateImageVariation429", CreateImageVariation429> + | OpenAiClientError<"CreateImageVariation500", CreateImageVariation500> + > + ( + options: { + readonly payload: typeof CreateImageVariationRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateImageVariation400", CreateImageVariation400> + | OpenAiClientError<"CreateImageVariation403", CreateImageVariation403> + | OpenAiClientError<"CreateImageVariation410", CreateImageVariation410> + | OpenAiClientError<"CreateImageVariation429", CreateImageVariation429> + | OpenAiClientError<"CreateImageVariation500", CreateImageVariation500> + > + } + /** + * Create a Live WebRTC session or place an outbound SIP call. Start with the + * [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) for session configuration + * and [Telephony and SIP](https://developers.openai.com/api/docs/guides/voice-sip?api=live#place-an-outbound-call) + * for trunk setup and call monitoring. + * + * Set transport.type to `webrtc` and supply an SDP offer, or set it to `sip` + * and supply an E.164 destination and trunk credentials. Outbound SIP calling + * must be enabled for your organization. + * Ringing is limited to 3 minutes and connected calls to 2 hours; these limits + * are not configurable in the request. + * + * Returns `201 Created` after session initialization. WebRTC responses include an + * SDP answer. SIP responses do not wait for the callee to answer. Attach a + * sideband connection using session.id to monitor SIP call progress. + * + * Each SIP request creates a new call. If a request times out or the connection + * fails, retry with caution: the original request may have succeeded, and a + * retry can place another call. + */ + readonly "createLive": { + ( + options: { readonly payload: typeof CreateLiveRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateLive400", CreateLive400> + | OpenAiClientError<"CreateLive413", CreateLive413> + | OpenAiClientError<"CreateLive429", CreateLive429> + | OpenAiClientError<"CreateLive503", CreateLive503> + | OpenAiClientError<"CreateLive504", CreateLive504> + > + ( + options: { readonly payload: typeof CreateLiveRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateLive400", CreateLive400> + | OpenAiClientError<"CreateLive413", CreateLive413> + | OpenAiClientError<"CreateLive429", CreateLive429> + | OpenAiClientError<"CreateLive503", CreateLive503> + | OpenAiClientError<"CreateLive504", CreateLive504> + > + } + /** + * Accept an incoming SIP call. Supply session with type live, the model, and startup configuration. Before accepting calls, follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) to write frontend conversation instructions and a separate backend prompt. SIP media format is negotiated; omit audio.format. + */ + readonly "acceptLiveSession": { + ( + sessionId: string, + options: { readonly payload: typeof AcceptLiveSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AcceptLiveSession400", AcceptLiveSession400> + | OpenAiClientError<"AcceptLiveSession404", AcceptLiveSession404> + | OpenAiClientError<"AcceptLiveSession409", AcceptLiveSession409> + | OpenAiClientError<"AcceptLiveSession429", AcceptLiveSession429> + | OpenAiClientError<"AcceptLiveSession503", AcceptLiveSession503> + > + ( + sessionId: string, + options: { readonly payload: typeof AcceptLiveSessionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AcceptLiveSession400", AcceptLiveSession400> + | OpenAiClientError<"AcceptLiveSession404", AcceptLiveSession404> + | OpenAiClientError<"AcceptLiveSession409", AcceptLiveSession409> + | OpenAiClientError<"AcceptLiveSession429", AcceptLiveSession429> + | OpenAiClientError<"AcceptLiveSession503", AcceptLiveSession503> + > + } + /** + * Fork a stored Live session onto a new WebRTC connection. + */ + readonly "forkLiveSession": { + ( + sessionId: string, + options: { readonly payload: typeof ForkLiveSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ForkLiveSession400", ForkLiveSession400> + | OpenAiClientError<"ForkLiveSession413", ForkLiveSession413> + | OpenAiClientError<"ForkLiveSession429", ForkLiveSession429> + | OpenAiClientError<"ForkLiveSession503", ForkLiveSession503> + > + ( + sessionId: string, + options: { readonly payload: typeof ForkLiveSessionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ForkLiveSession400", ForkLiveSession400> + | OpenAiClientError<"ForkLiveSession413", ForkLiveSession413> + | OpenAiClientError<"ForkLiveSession429", ForkLiveSession429> + | OpenAiClientError<"ForkLiveSession503", ForkLiveSession503> + > + } + /** + * End a SIP call identified by session_id. + */ + readonly "hangupLiveSession": { + ( + sessionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"HangupLiveSession404", HangupLiveSession404> + > + ( + sessionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"HangupLiveSession404", HangupLiveSession404> + > + } + /** + * Transfer a SIP call to another destination. Supply a nonblank target_uri for the SIP Refer-To header. + */ + readonly "referLiveSession": { + ( + sessionId: string, + options: { readonly payload: typeof ReferLiveSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ReferLiveSession400", ReferLiveSession400> + | OpenAiClientError<"ReferLiveSession404", ReferLiveSession404> + > + ( + sessionId: string, + options: { readonly payload: typeof ReferLiveSessionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ReferLiveSession400", ReferLiveSession400> + | OpenAiClientError<"ReferLiveSession404", ReferLiveSession404> + > + } + /** + * Reject an incoming SIP call. Send a required SIP rejection status_code between 300 and 699. + */ + readonly "rejectLiveSession": { + ( + sessionId: string, + options: { readonly payload: typeof RejectLiveSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RejectLiveSession400", RejectLiveSession400> + | OpenAiClientError<"RejectLiveSession404", RejectLiveSession404> + | OpenAiClientError<"RejectLiveSession409", RejectLiveSession409> + > + ( + sessionId: string, + options: { readonly payload: typeof RejectLiveSessionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RejectLiveSession400", RejectLiveSession400> + | OpenAiClientError<"RejectLiveSession404", RejectLiveSession404> + | OpenAiClientError<"RejectLiveSession409", RejectLiveSession409> + > + } + /** + * Lists the currently available models, and provides basic information about each one such as the owner and availability. + */ + readonly "listModels": { + ( + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListModels403", ListModels403> + > + ( + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListModels403", ListModels403> + > + } + /** + * Retrieves a model instance, providing basic information about the model such as the owner and permissioning. + */ + readonly "retrieveModel": { + ( + model: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveModel403", RetrieveModel403> + | OpenAiClientError<"RetrieveModel404", RetrieveModel404> + > + ( + model: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveModel403", RetrieveModel403> + | OpenAiClientError<"RetrieveModel404", RetrieveModel404> + > + } + /** + * Delete a fine-tuned model. You must have the Owner role in your organization to delete a model. + */ + readonly "deleteModel": { + ( + model: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteModel403", DeleteModel403> + | OpenAiClientError<"DeleteModel404", DeleteModel404> + > + ( + model: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteModel403", DeleteModel403> + | OpenAiClientError<"DeleteModel404", DeleteModel404> + > + } + /** + * Classifies if text and/or image inputs are potentially harmful. Learn + * more in the [moderation guide](https://developers.openai.com/api/docs/guides/moderation). + */ + readonly "createModeration": { + ( + options: { readonly payload: typeof CreateModerationRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateModeration400", CreateModeration400> + | OpenAiClientError<"CreateModeration403", CreateModeration403> + | OpenAiClientError<"CreateModeration429", CreateModeration429> + | OpenAiClientError<"CreateModeration500", CreateModeration500> + | OpenAiClientError<"CreateModeration503", CreateModeration503> + > + ( + options: { readonly payload: typeof CreateModerationRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateModeration400", CreateModeration400> + | OpenAiClientError<"CreateModeration403", CreateModeration403> + | OpenAiClientError<"CreateModeration429", CreateModeration429> + | OpenAiClientError<"CreateModeration500", CreateModeration500> + | OpenAiClientError<"CreateModeration503", CreateModeration503> + > + } + /** + * List organization API keys + */ + readonly "adminApiKeysList": { + ( + options: { readonly params?: typeof AdminApiKeysListParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysList400", AdminApiKeysList400> + | OpenAiClientError<"AdminApiKeysList403", AdminApiKeysList403> + > + ( + options: { + readonly params?: typeof AdminApiKeysListParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysList400", AdminApiKeysList400> + | OpenAiClientError<"AdminApiKeysList403", AdminApiKeysList403> + > + } + /** + * Create an organization admin API key + */ + readonly "adminApiKeysCreate": { + ( + options: { readonly payload: typeof AdminApiKeysCreateRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysCreate400", AdminApiKeysCreate400> + | OpenAiClientError<"AdminApiKeysCreate403", AdminApiKeysCreate403> + | OpenAiClientError<"AdminApiKeysCreate404", AdminApiKeysCreate404> + > + ( + options: { readonly payload: typeof AdminApiKeysCreateRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysCreate400", AdminApiKeysCreate400> + | OpenAiClientError<"AdminApiKeysCreate403", AdminApiKeysCreate403> + | OpenAiClientError<"AdminApiKeysCreate404", AdminApiKeysCreate404> + > + } + /** + * Retrieve a single organization API key + */ + readonly "adminApiKeysGet": { + ( + keyId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysGet403", AdminApiKeysGet403> + | OpenAiClientError<"AdminApiKeysGet404", AdminApiKeysGet404> + > + ( + keyId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysGet403", AdminApiKeysGet403> + | OpenAiClientError<"AdminApiKeysGet404", AdminApiKeysGet404> + > + } + /** + * Delete an organization admin API key + */ + readonly "adminApiKeysDelete": { + ( + keyId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysDelete403", AdminApiKeysDelete403> + | OpenAiClientError<"AdminApiKeysDelete404", AdminApiKeysDelete404> + > + ( + keyId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AdminApiKeysDelete403", AdminApiKeysDelete403> + | OpenAiClientError<"AdminApiKeysDelete404", AdminApiKeysDelete404> + > + } + /** + * List user actions and configuration changes within this organization. + */ + readonly "listAuditLogs": { + ( + options: { readonly params?: typeof ListAuditLogsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAuditLogs400", ListAuditLogs400> + | OpenAiClientError<"ListAuditLogs404", ListAuditLogs404> + | OpenAiClientError<"ListAuditLogs429", ListAuditLogs429> + > + ( + options: { + readonly params?: typeof ListAuditLogsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAuditLogs400", ListAuditLogs400> + | OpenAiClientError<"ListAuditLogs404", ListAuditLogs404> + | OpenAiClientError<"ListAuditLogs429", ListAuditLogs429> + > + } + /** + * List uploaded certificates for this organization. + */ + readonly "listOrganizationCertificates": { + ( + options: { + readonly params?: typeof ListOrganizationCertificatesParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListOrganizationCertificates400", ListOrganizationCertificates400> + | OpenAiClientError<"ListOrganizationCertificates403", ListOrganizationCertificates403> + | OpenAiClientError<"ListOrganizationCertificates429", ListOrganizationCertificates429> + > + ( + options: { + readonly params?: typeof ListOrganizationCertificatesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListOrganizationCertificates400", ListOrganizationCertificates400> + | OpenAiClientError<"ListOrganizationCertificates403", ListOrganizationCertificates403> + | OpenAiClientError<"ListOrganizationCertificates429", ListOrganizationCertificates429> + > + } + /** + * Upload a certificate to the organization. This does **not** automatically activate the certificate. + * + * Organizations can upload up to 50 certificates. + */ + readonly "uploadCertificate": { + ( + options: { readonly payload: typeof UploadCertificateRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UploadCertificate400", UploadCertificate400> + | OpenAiClientError<"UploadCertificate403", UploadCertificate403> + | OpenAiClientError<"UploadCertificate429", UploadCertificate429> + > + ( + options: { readonly payload: typeof UploadCertificateRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UploadCertificate400", UploadCertificate400> + | OpenAiClientError<"UploadCertificate403", UploadCertificate403> + | OpenAiClientError<"UploadCertificate429", UploadCertificate429> + > + } + /** + * Activate certificates at the organization level. + * + * You can atomically and idempotently activate up to 10 certificates at a time. + */ + readonly "activateOrganizationCertificates": { + ( + options: { readonly payload: typeof ActivateOrganizationCertificatesRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ActivateOrganizationCertificates400", ActivateOrganizationCertificates400> + | OpenAiClientError<"ActivateOrganizationCertificates403", ActivateOrganizationCertificates403> + | OpenAiClientError<"ActivateOrganizationCertificates404", ActivateOrganizationCertificates404> + | OpenAiClientError<"ActivateOrganizationCertificates429", ActivateOrganizationCertificates429> + | OpenAiClientError<"ActivateOrganizationCertificates500", ActivateOrganizationCertificates500> + > + ( + options: { + readonly payload: typeof ActivateOrganizationCertificatesRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ActivateOrganizationCertificates400", ActivateOrganizationCertificates400> + | OpenAiClientError<"ActivateOrganizationCertificates403", ActivateOrganizationCertificates403> + | OpenAiClientError<"ActivateOrganizationCertificates404", ActivateOrganizationCertificates404> + | OpenAiClientError<"ActivateOrganizationCertificates429", ActivateOrganizationCertificates429> + | OpenAiClientError<"ActivateOrganizationCertificates500", ActivateOrganizationCertificates500> + > + } + /** + * Deactivate certificates at the organization level. + * + * You can atomically and idempotently deactivate up to 10 certificates at a time. + */ + readonly "deactivateOrganizationCertificates": { + ( + options: { + readonly payload: typeof DeactivateOrganizationCertificatesRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeactivateOrganizationCertificates400", DeactivateOrganizationCertificates400> + | OpenAiClientError<"DeactivateOrganizationCertificates403", DeactivateOrganizationCertificates403> + | OpenAiClientError<"DeactivateOrganizationCertificates404", DeactivateOrganizationCertificates404> + | OpenAiClientError<"DeactivateOrganizationCertificates429", DeactivateOrganizationCertificates429> + | OpenAiClientError<"DeactivateOrganizationCertificates500", DeactivateOrganizationCertificates500> + > + ( + options: { + readonly payload: typeof DeactivateOrganizationCertificatesRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeactivateOrganizationCertificates400", DeactivateOrganizationCertificates400> + | OpenAiClientError<"DeactivateOrganizationCertificates403", DeactivateOrganizationCertificates403> + | OpenAiClientError<"DeactivateOrganizationCertificates404", DeactivateOrganizationCertificates404> + | OpenAiClientError<"DeactivateOrganizationCertificates429", DeactivateOrganizationCertificates429> + | OpenAiClientError<"DeactivateOrganizationCertificates500", DeactivateOrganizationCertificates500> + > + } + /** + * Get a certificate that has been uploaded to the organization. + * + * You can get a certificate regardless of whether it is active or not. + */ + readonly "getCertificate": { + ( + certificateId: string, + options: { readonly params?: typeof GetCertificateParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetCertificate400", GetCertificate400> + | OpenAiClientError<"GetCertificate403", GetCertificate403> + | OpenAiClientError<"GetCertificate404", GetCertificate404> + | OpenAiClientError<"GetCertificate429", GetCertificate429> + > + ( + certificateId: string, + options: { + readonly params?: typeof GetCertificateParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetCertificate400", GetCertificate400> + | OpenAiClientError<"GetCertificate403", GetCertificate403> + | OpenAiClientError<"GetCertificate404", GetCertificate404> + | OpenAiClientError<"GetCertificate429", GetCertificate429> + > + } + /** + * Modify a certificate. Note that only the name can be modified. + */ + readonly "modifyCertificate": { + ( + certificateId: string, + options: { readonly payload: typeof ModifyCertificateRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyCertificate400", ModifyCertificate400> + | OpenAiClientError<"ModifyCertificate403", ModifyCertificate403> + | OpenAiClientError<"ModifyCertificate404", ModifyCertificate404> + | OpenAiClientError<"ModifyCertificate429", ModifyCertificate429> + | OpenAiClientError<"ModifyCertificate500", ModifyCertificate500> + > + ( + certificateId: string, + options: { readonly payload: typeof ModifyCertificateRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyCertificate400", ModifyCertificate400> + | OpenAiClientError<"ModifyCertificate403", ModifyCertificate403> + | OpenAiClientError<"ModifyCertificate404", ModifyCertificate404> + | OpenAiClientError<"ModifyCertificate429", ModifyCertificate429> + | OpenAiClientError<"ModifyCertificate500", ModifyCertificate500> + > + } + /** + * Delete a certificate from the organization. + * + * The certificate must be inactive for the organization and all projects. + */ + readonly "deleteCertificate": { + ( + certificateId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteCertificate400", DeleteCertificate400> + | OpenAiClientError<"DeleteCertificate403", DeleteCertificate403> + | OpenAiClientError<"DeleteCertificate404", DeleteCertificate404> + | OpenAiClientError<"DeleteCertificate429", DeleteCertificate429> + > + ( + certificateId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteCertificate400", DeleteCertificate400> + | OpenAiClientError<"DeleteCertificate403", DeleteCertificate403> + | OpenAiClientError<"DeleteCertificate404", DeleteCertificate404> + | OpenAiClientError<"DeleteCertificate429", DeleteCertificate429> + > + } + /** + * Get costs details for the organization. + */ + readonly "usageCosts": { + ( + options: { readonly params: typeof UsageCostsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageCosts400", UsageCosts400> + | OpenAiClientError<"UsageCosts403", UsageCosts403> + | OpenAiClientError<"UsageCosts429", UsageCosts429> + > + ( + options: { readonly params: typeof UsageCostsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageCosts400", UsageCosts400> + | OpenAiClientError<"UsageCosts403", UsageCosts403> + | OpenAiClientError<"UsageCosts429", UsageCosts429> + > + } + /** + * Retrieves organization data retention controls. + */ + readonly "retrieveOrganizationDataRetention": { + ( + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveOrganizationDataRetention403", RetrieveOrganizationDataRetention403> + | OpenAiClientError<"RetrieveOrganizationDataRetention404", RetrieveOrganizationDataRetention404> + > + ( + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveOrganizationDataRetention403", RetrieveOrganizationDataRetention403> + | OpenAiClientError<"RetrieveOrganizationDataRetention404", RetrieveOrganizationDataRetention404> + > + } + /** + * Updates organization data retention controls. + */ + readonly "updateOrganizationDataRetention": { + ( + options: { readonly payload: typeof UpdateOrganizationDataRetentionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateOrganizationDataRetention400", UpdateOrganizationDataRetention400> + | OpenAiClientError<"UpdateOrganizationDataRetention403", UpdateOrganizationDataRetention403> + | OpenAiClientError<"UpdateOrganizationDataRetention404", UpdateOrganizationDataRetention404> + > + ( + options: { + readonly payload: typeof UpdateOrganizationDataRetentionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateOrganizationDataRetention400", UpdateOrganizationDataRetention400> + | OpenAiClientError<"UpdateOrganizationDataRetention403", UpdateOrganizationDataRetention403> + | OpenAiClientError<"UpdateOrganizationDataRetention404", UpdateOrganizationDataRetention404> + > + } + /** + * Lists all groups in the organization. + */ + readonly "listGroups": { + ( + options: { readonly params?: typeof ListGroupsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListGroups400", ListGroups400> + | OpenAiClientError<"ListGroups403", ListGroups403> + | OpenAiClientError<"ListGroups404", ListGroups404> + | OpenAiClientError<"ListGroups429", ListGroups429> + > + ( + options: + | { readonly params?: typeof ListGroupsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListGroups400", ListGroups400> + | OpenAiClientError<"ListGroups403", ListGroups403> + | OpenAiClientError<"ListGroups404", ListGroups404> + | OpenAiClientError<"ListGroups429", ListGroups429> + > + } + /** + * Creates a new group in the organization. + */ + readonly "createGroup": { + ( + options: { readonly payload: typeof CreateGroupRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateGroup400", CreateGroup400> + | OpenAiClientError<"CreateGroup403", CreateGroup403> + | OpenAiClientError<"CreateGroup404", CreateGroup404> + | OpenAiClientError<"CreateGroup422", CreateGroup422> + | OpenAiClientError<"CreateGroup429", CreateGroup429> + | OpenAiClientError<"CreateGroup500", CreateGroup500> + > + ( + options: { readonly payload: typeof CreateGroupRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateGroup400", CreateGroup400> + | OpenAiClientError<"CreateGroup403", CreateGroup403> + | OpenAiClientError<"CreateGroup404", CreateGroup404> + | OpenAiClientError<"CreateGroup422", CreateGroup422> + | OpenAiClientError<"CreateGroup429", CreateGroup429> + | OpenAiClientError<"CreateGroup500", CreateGroup500> + > + } + /** + * Retrieves a group. + */ + readonly "retrieveGroup": { + ( + groupId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveGroup400", RetrieveGroup400> + | OpenAiClientError<"RetrieveGroup403", RetrieveGroup403> + | OpenAiClientError<"RetrieveGroup404", RetrieveGroup404> + | OpenAiClientError<"RetrieveGroup429", RetrieveGroup429> + | OpenAiClientError<"RetrieveGroup500", RetrieveGroup500> + > + ( + groupId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveGroup400", RetrieveGroup400> + | OpenAiClientError<"RetrieveGroup403", RetrieveGroup403> + | OpenAiClientError<"RetrieveGroup404", RetrieveGroup404> + | OpenAiClientError<"RetrieveGroup429", RetrieveGroup429> + | OpenAiClientError<"RetrieveGroup500", RetrieveGroup500> + > + } + /** + * Updates a group's information. + */ + readonly "updateGroup": { + ( + groupId: string, + options: { readonly payload: typeof UpdateGroupRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateGroup400", UpdateGroup400> + | OpenAiClientError<"UpdateGroup403", UpdateGroup403> + | OpenAiClientError<"UpdateGroup404", UpdateGroup404> + | OpenAiClientError<"UpdateGroup429", UpdateGroup429> + | OpenAiClientError<"UpdateGroup500", UpdateGroup500> + > + ( + groupId: string, + options: { readonly payload: typeof UpdateGroupRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateGroup400", UpdateGroup400> + | OpenAiClientError<"UpdateGroup403", UpdateGroup403> + | OpenAiClientError<"UpdateGroup404", UpdateGroup404> + | OpenAiClientError<"UpdateGroup429", UpdateGroup429> + | OpenAiClientError<"UpdateGroup500", UpdateGroup500> + > + } + /** + * Deletes a group from the organization. + */ + readonly "deleteGroup": { + ( + groupId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteGroup400", DeleteGroup400> + | OpenAiClientError<"DeleteGroup403", DeleteGroup403> + | OpenAiClientError<"DeleteGroup404", DeleteGroup404> + | OpenAiClientError<"DeleteGroup429", DeleteGroup429> + > + ( + groupId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteGroup400", DeleteGroup400> + | OpenAiClientError<"DeleteGroup403", DeleteGroup403> + | OpenAiClientError<"DeleteGroup404", DeleteGroup404> + | OpenAiClientError<"DeleteGroup429", DeleteGroup429> + > + } + /** + * Lists the organization roles assigned to a group within the organization. + */ + readonly "listGroupRoleAssignments": { + ( + groupId: string, + options: { readonly params?: typeof ListGroupRoleAssignmentsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListGroupRoleAssignments400", ListGroupRoleAssignments400> + | OpenAiClientError<"ListGroupRoleAssignments403", ListGroupRoleAssignments403> + | OpenAiClientError<"ListGroupRoleAssignments404", ListGroupRoleAssignments404> + | OpenAiClientError<"ListGroupRoleAssignments429", ListGroupRoleAssignments429> + > + ( + groupId: string, + options: { + readonly params?: typeof ListGroupRoleAssignmentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListGroupRoleAssignments400", ListGroupRoleAssignments400> + | OpenAiClientError<"ListGroupRoleAssignments403", ListGroupRoleAssignments403> + | OpenAiClientError<"ListGroupRoleAssignments404", ListGroupRoleAssignments404> + | OpenAiClientError<"ListGroupRoleAssignments429", ListGroupRoleAssignments429> + > + } + /** + * Assigns an organization role to a group within the organization. + */ + readonly "assignGroupRole": { + ( + groupId: string, + options: { readonly payload: typeof AssignGroupRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignGroupRole400", AssignGroupRole400> + | OpenAiClientError<"AssignGroupRole403", AssignGroupRole403> + | OpenAiClientError<"AssignGroupRole404", AssignGroupRole404> + | OpenAiClientError<"AssignGroupRole429", AssignGroupRole429> + > + ( + groupId: string, + options: { readonly payload: typeof AssignGroupRoleRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignGroupRole400", AssignGroupRole400> + | OpenAiClientError<"AssignGroupRole403", AssignGroupRole403> + | OpenAiClientError<"AssignGroupRole404", AssignGroupRole404> + | OpenAiClientError<"AssignGroupRole429", AssignGroupRole429> + > + } + /** + * Retrieves an organization role assigned to a group. + */ + readonly "retrieveGroupRole": { + ( + groupId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveGroupRole400", RetrieveGroupRole400> + | OpenAiClientError<"RetrieveGroupRole403", RetrieveGroupRole403> + | OpenAiClientError<"RetrieveGroupRole404", RetrieveGroupRole404> + | OpenAiClientError<"RetrieveGroupRole429", RetrieveGroupRole429> + > + ( + groupId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveGroupRole400", RetrieveGroupRole400> + | OpenAiClientError<"RetrieveGroupRole403", RetrieveGroupRole403> + | OpenAiClientError<"RetrieveGroupRole404", RetrieveGroupRole404> + | OpenAiClientError<"RetrieveGroupRole429", RetrieveGroupRole429> + > + } + /** + * Unassigns an organization role from a group within the organization. + */ + readonly "unassignGroupRole": { + ( + groupId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignGroupRole400", UnassignGroupRole400> + | OpenAiClientError<"UnassignGroupRole403", UnassignGroupRole403> + | OpenAiClientError<"UnassignGroupRole404", UnassignGroupRole404> + | OpenAiClientError<"UnassignGroupRole429", UnassignGroupRole429> + > + ( + groupId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignGroupRole400", UnassignGroupRole400> + | OpenAiClientError<"UnassignGroupRole403", UnassignGroupRole403> + | OpenAiClientError<"UnassignGroupRole404", UnassignGroupRole404> + | OpenAiClientError<"UnassignGroupRole429", UnassignGroupRole429> + > + } + /** + * Lists the users assigned to a group. + */ + readonly "listGroupUsers": { + ( + groupId: string, + options: { readonly params?: typeof ListGroupUsersParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListGroupUsers400", ListGroupUsers400> + | OpenAiClientError<"ListGroupUsers403", ListGroupUsers403> + | OpenAiClientError<"ListGroupUsers404", ListGroupUsers404> + | OpenAiClientError<"ListGroupUsers429", ListGroupUsers429> + | OpenAiClientError<"ListGroupUsers502", ListGroupUsers502> + > + ( + groupId: string, + options: { + readonly params?: typeof ListGroupUsersParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListGroupUsers400", ListGroupUsers400> + | OpenAiClientError<"ListGroupUsers403", ListGroupUsers403> + | OpenAiClientError<"ListGroupUsers404", ListGroupUsers404> + | OpenAiClientError<"ListGroupUsers429", ListGroupUsers429> + | OpenAiClientError<"ListGroupUsers502", ListGroupUsers502> + > + } + /** + * Adds a user to a group. + */ + readonly "addGroupUser": { + ( + groupId: string, + options: { readonly payload: typeof AddGroupUserRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AddGroupUser400", AddGroupUser400> + | OpenAiClientError<"AddGroupUser403", AddGroupUser403> + | OpenAiClientError<"AddGroupUser404", AddGroupUser404> + | OpenAiClientError<"AddGroupUser409", AddGroupUser409> + | OpenAiClientError<"AddGroupUser429", AddGroupUser429> + > + ( + groupId: string, + options: { readonly payload: typeof AddGroupUserRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AddGroupUser400", AddGroupUser400> + | OpenAiClientError<"AddGroupUser403", AddGroupUser403> + | OpenAiClientError<"AddGroupUser404", AddGroupUser404> + | OpenAiClientError<"AddGroupUser409", AddGroupUser409> + | OpenAiClientError<"AddGroupUser429", AddGroupUser429> + > + } + /** + * Retrieves a user in a group. + */ + readonly "retrieveGroupUser": { + ( + groupId: string, + userId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveGroupUser400", RetrieveGroupUser400> + | OpenAiClientError<"RetrieveGroupUser403", RetrieveGroupUser403> + | OpenAiClientError<"RetrieveGroupUser404", RetrieveGroupUser404> + | OpenAiClientError<"RetrieveGroupUser429", RetrieveGroupUser429> + > + ( + groupId: string, + userId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveGroupUser400", RetrieveGroupUser400> + | OpenAiClientError<"RetrieveGroupUser403", RetrieveGroupUser403> + | OpenAiClientError<"RetrieveGroupUser404", RetrieveGroupUser404> + | OpenAiClientError<"RetrieveGroupUser429", RetrieveGroupUser429> + > + } + /** + * Removes a user from a group. + */ + readonly "removeGroupUser": { + ( + groupId: string, + userId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RemoveGroupUser400", RemoveGroupUser400> + | OpenAiClientError<"RemoveGroupUser403", RemoveGroupUser403> + | OpenAiClientError<"RemoveGroupUser404", RemoveGroupUser404> + | OpenAiClientError<"RemoveGroupUser409", RemoveGroupUser409> + | OpenAiClientError<"RemoveGroupUser429", RemoveGroupUser429> + > + ( + groupId: string, + userId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RemoveGroupUser400", RemoveGroupUser400> + | OpenAiClientError<"RemoveGroupUser403", RemoveGroupUser403> + | OpenAiClientError<"RemoveGroupUser404", RemoveGroupUser404> + | OpenAiClientError<"RemoveGroupUser409", RemoveGroupUser409> + | OpenAiClientError<"RemoveGroupUser429", RemoveGroupUser429> + > + } + /** + * Returns a list of invites in the organization. + */ + readonly "listInvites": { + ( + options: { readonly params?: typeof ListInvitesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListInvites400", ListInvites400> + | OpenAiClientError<"ListInvites403", ListInvites403> + | OpenAiClientError<"ListInvites404", ListInvites404> + | OpenAiClientError<"ListInvites429", ListInvites429> + > + ( + options: + | { readonly params?: typeof ListInvitesParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListInvites400", ListInvites400> + | OpenAiClientError<"ListInvites403", ListInvites403> + | OpenAiClientError<"ListInvites404", ListInvites404> + | OpenAiClientError<"ListInvites429", ListInvites429> + > + } + /** + * Create an invite for a user to the organization. The invite must be accepted by the user before they have access to the organization. + */ + readonly "inviteUser": { + ( + options: { readonly payload: typeof InviteUserRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"InviteUser400", InviteUser400> + | OpenAiClientError<"InviteUser403", InviteUser403> + | OpenAiClientError<"InviteUser429", InviteUser429> + > + ( + options: { readonly payload: typeof InviteUserRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"InviteUser400", InviteUser400> + | OpenAiClientError<"InviteUser403", InviteUser403> + | OpenAiClientError<"InviteUser429", InviteUser429> + > + } + /** + * Retrieves an invite. + */ + readonly "retrieveInvite": { + ( + inviteId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveInvite400", RetrieveInvite400> + | OpenAiClientError<"RetrieveInvite403", RetrieveInvite403> + | OpenAiClientError<"RetrieveInvite404", RetrieveInvite404> + | OpenAiClientError<"RetrieveInvite429", RetrieveInvite429> + | OpenAiClientError<"RetrieveInvite500", RetrieveInvite500> + > + ( + inviteId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveInvite400", RetrieveInvite400> + | OpenAiClientError<"RetrieveInvite403", RetrieveInvite403> + | OpenAiClientError<"RetrieveInvite404", RetrieveInvite404> + | OpenAiClientError<"RetrieveInvite429", RetrieveInvite429> + | OpenAiClientError<"RetrieveInvite500", RetrieveInvite500> + > + } + /** + * Delete an invite. If the invite has already been accepted, it cannot be deleted. + */ + readonly "deleteInvite": { + ( + inviteId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteInvite400", DeleteInvite400> + | OpenAiClientError<"DeleteInvite403", DeleteInvite403> + | OpenAiClientError<"DeleteInvite404", DeleteInvite404> + | OpenAiClientError<"DeleteInvite429", DeleteInvite429> + > + ( + inviteId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteInvite400", DeleteInvite400> + | OpenAiClientError<"DeleteInvite403", DeleteInvite403> + | OpenAiClientError<"DeleteInvite404", DeleteInvite404> + | OpenAiClientError<"DeleteInvite429", DeleteInvite429> + > + } + /** + * Returns a list of projects. + */ + readonly "listProjects": { + ( + options: { readonly params?: typeof ListProjectsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjects400", ListProjects400> + | OpenAiClientError<"ListProjects403", ListProjects403> + | OpenAiClientError<"ListProjects404", ListProjects404> + | OpenAiClientError<"ListProjects429", ListProjects429> + > + ( + options: + | { readonly params?: typeof ListProjectsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjects400", ListProjects400> + | OpenAiClientError<"ListProjects403", ListProjects403> + | OpenAiClientError<"ListProjects404", ListProjects404> + | OpenAiClientError<"ListProjects429", ListProjects429> + > + } + /** + * Create a new project in the organization. Projects can be created and archived, but cannot be deleted. + */ + readonly "createProject": { + ( + options: { readonly payload: typeof CreateProjectRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProject400", CreateProject400> + | OpenAiClientError<"CreateProject403", CreateProject403> + | OpenAiClientError<"CreateProject429", CreateProject429> + > + ( + options: { readonly payload: typeof CreateProjectRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProject400", CreateProject400> + | OpenAiClientError<"CreateProject403", CreateProject403> + | OpenAiClientError<"CreateProject429", CreateProject429> + > + } + /** + * Retrieves a project. + */ + readonly "retrieveProject": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProject403", RetrieveProject403> + | OpenAiClientError<"RetrieveProject404", RetrieveProject404> + | OpenAiClientError<"RetrieveProject429", RetrieveProject429> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProject403", RetrieveProject403> + | OpenAiClientError<"RetrieveProject404", RetrieveProject404> + | OpenAiClientError<"RetrieveProject429", RetrieveProject429> + > + } + /** + * Modifies a project in the organization. + */ + readonly "modifyProject": { + ( + projectId: string, + options: { readonly payload: typeof ModifyProjectRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyProject400", ModifyProject400> + | OpenAiClientError<"ModifyProject403", ModifyProject403> + | OpenAiClientError<"ModifyProject404", ModifyProject404> + | OpenAiClientError<"ModifyProject429", ModifyProject429> + > + ( + projectId: string, + options: { readonly payload: typeof ModifyProjectRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyProject400", ModifyProject400> + | OpenAiClientError<"ModifyProject403", ModifyProject403> + | OpenAiClientError<"ModifyProject404", ModifyProject404> + | OpenAiClientError<"ModifyProject429", ModifyProject429> + > + } + /** + * Returns a list of API keys in the project. + */ + readonly "listProjectApiKeys": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectApiKeysParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectApiKeys400", ListProjectApiKeys400> + | OpenAiClientError<"ListProjectApiKeys403", ListProjectApiKeys403> + | OpenAiClientError<"ListProjectApiKeys404", ListProjectApiKeys404> + | OpenAiClientError<"ListProjectApiKeys429", ListProjectApiKeys429> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectApiKeysParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectApiKeys400", ListProjectApiKeys400> + | OpenAiClientError<"ListProjectApiKeys403", ListProjectApiKeys403> + | OpenAiClientError<"ListProjectApiKeys404", ListProjectApiKeys404> + | OpenAiClientError<"ListProjectApiKeys429", ListProjectApiKeys429> + > + } + /** + * Retrieves an API key in the project. + */ + readonly "retrieveProjectApiKey": { + ( + projectId: string, + apiKeyId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectApiKey400", RetrieveProjectApiKey400> + | OpenAiClientError<"RetrieveProjectApiKey403", RetrieveProjectApiKey403> + | OpenAiClientError<"RetrieveProjectApiKey404", RetrieveProjectApiKey404> + | OpenAiClientError<"RetrieveProjectApiKey429", RetrieveProjectApiKey429> + > + ( + projectId: string, + apiKeyId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectApiKey400", RetrieveProjectApiKey400> + | OpenAiClientError<"RetrieveProjectApiKey403", RetrieveProjectApiKey403> + | OpenAiClientError<"RetrieveProjectApiKey404", RetrieveProjectApiKey404> + | OpenAiClientError<"RetrieveProjectApiKey429", RetrieveProjectApiKey429> + > + } + /** + * Deletes an API key from the project. + * + * Returns confirmation of the key deletion, or an error if the key belonged to + * a service account. + */ + readonly "deleteProjectApiKey": { + ( + projectId: string, + apiKeyId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectApiKey400", DeleteProjectApiKey400> + | OpenAiClientError<"DeleteProjectApiKey403", DeleteProjectApiKey403> + | OpenAiClientError<"DeleteProjectApiKey404", DeleteProjectApiKey404> + | OpenAiClientError<"DeleteProjectApiKey429", DeleteProjectApiKey429> + > + ( + projectId: string, + apiKeyId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectApiKey400", DeleteProjectApiKey400> + | OpenAiClientError<"DeleteProjectApiKey403", DeleteProjectApiKey403> + | OpenAiClientError<"DeleteProjectApiKey404", DeleteProjectApiKey404> + | OpenAiClientError<"DeleteProjectApiKey429", DeleteProjectApiKey429> + > + } + /** + * Archives a project in the organization. Archived projects cannot be used or updated. + */ + readonly "archiveProject": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ArchiveProject400", ArchiveProject400> + | OpenAiClientError<"ArchiveProject403", ArchiveProject403> + | OpenAiClientError<"ArchiveProject404", ArchiveProject404> + | OpenAiClientError<"ArchiveProject429", ArchiveProject429> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ArchiveProject400", ArchiveProject400> + | OpenAiClientError<"ArchiveProject403", ArchiveProject403> + | OpenAiClientError<"ArchiveProject404", ArchiveProject404> + | OpenAiClientError<"ArchiveProject429", ArchiveProject429> + > + } + /** + * List certificates for this project. + */ + readonly "listProjectCertificates": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectCertificatesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectCertificates400", ListProjectCertificates400> + | OpenAiClientError<"ListProjectCertificates403", ListProjectCertificates403> + | OpenAiClientError<"ListProjectCertificates429", ListProjectCertificates429> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectCertificatesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectCertificates400", ListProjectCertificates400> + | OpenAiClientError<"ListProjectCertificates403", ListProjectCertificates403> + | OpenAiClientError<"ListProjectCertificates429", ListProjectCertificates429> + > + } + /** + * Activate certificates at the project level. + * + * You can atomically and idempotently activate up to 10 certificates at a time. + */ + readonly "activateProjectCertificates": { + ( + projectId: string, + options: { readonly payload: typeof ActivateProjectCertificatesRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ActivateProjectCertificates400", ActivateProjectCertificates400> + | OpenAiClientError<"ActivateProjectCertificates403", ActivateProjectCertificates403> + | OpenAiClientError<"ActivateProjectCertificates404", ActivateProjectCertificates404> + | OpenAiClientError<"ActivateProjectCertificates429", ActivateProjectCertificates429> + | OpenAiClientError<"ActivateProjectCertificates500", ActivateProjectCertificates500> + > + ( + projectId: string, + options: { + readonly payload: typeof ActivateProjectCertificatesRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ActivateProjectCertificates400", ActivateProjectCertificates400> + | OpenAiClientError<"ActivateProjectCertificates403", ActivateProjectCertificates403> + | OpenAiClientError<"ActivateProjectCertificates404", ActivateProjectCertificates404> + | OpenAiClientError<"ActivateProjectCertificates429", ActivateProjectCertificates429> + | OpenAiClientError<"ActivateProjectCertificates500", ActivateProjectCertificates500> + > + } + /** + * Deactivate certificates at the project level. You can atomically and + * idempotently deactivate up to 10 certificates at a time. + */ + readonly "deactivateProjectCertificates": { + ( + projectId: string, + options: { readonly payload: typeof DeactivateProjectCertificatesRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeactivateProjectCertificates400", DeactivateProjectCertificates400> + | OpenAiClientError<"DeactivateProjectCertificates403", DeactivateProjectCertificates403> + | OpenAiClientError<"DeactivateProjectCertificates404", DeactivateProjectCertificates404> + | OpenAiClientError<"DeactivateProjectCertificates429", DeactivateProjectCertificates429> + | OpenAiClientError<"DeactivateProjectCertificates500", DeactivateProjectCertificates500> + > + ( + projectId: string, + options: { + readonly payload: typeof DeactivateProjectCertificatesRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeactivateProjectCertificates400", DeactivateProjectCertificates400> + | OpenAiClientError<"DeactivateProjectCertificates403", DeactivateProjectCertificates403> + | OpenAiClientError<"DeactivateProjectCertificates404", DeactivateProjectCertificates404> + | OpenAiClientError<"DeactivateProjectCertificates429", DeactivateProjectCertificates429> + | OpenAiClientError<"DeactivateProjectCertificates500", DeactivateProjectCertificates500> + > + } + /** + * Retrieves project data retention controls. + */ + readonly "retrieveProjectDataRetention": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectDataRetention400", RetrieveProjectDataRetention400> + | OpenAiClientError<"RetrieveProjectDataRetention403", RetrieveProjectDataRetention403> + | OpenAiClientError<"RetrieveProjectDataRetention404", RetrieveProjectDataRetention404> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectDataRetention400", RetrieveProjectDataRetention400> + | OpenAiClientError<"RetrieveProjectDataRetention403", RetrieveProjectDataRetention403> + | OpenAiClientError<"RetrieveProjectDataRetention404", RetrieveProjectDataRetention404> + > + } + /** + * Updates project data retention controls. + */ + readonly "updateProjectDataRetention": { + ( + projectId: string, + options: { readonly payload: typeof UpdateProjectDataRetentionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectDataRetention400", UpdateProjectDataRetention400> + | OpenAiClientError<"UpdateProjectDataRetention403", UpdateProjectDataRetention403> + | OpenAiClientError<"UpdateProjectDataRetention404", UpdateProjectDataRetention404> + > + ( + projectId: string, + options: { + readonly payload: typeof UpdateProjectDataRetentionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectDataRetention400", UpdateProjectDataRetention400> + | OpenAiClientError<"UpdateProjectDataRetention403", UpdateProjectDataRetention403> + | OpenAiClientError<"UpdateProjectDataRetention404", UpdateProjectDataRetention404> + > + } + /** + * Lists the groups that have access to a project. + */ + readonly "listProjectGroups": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectGroupsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectGroups400", ListProjectGroups400> + | OpenAiClientError<"ListProjectGroups403", ListProjectGroups403> + | OpenAiClientError<"ListProjectGroups404", ListProjectGroups404> + | OpenAiClientError<"ListProjectGroups429", ListProjectGroups429> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectGroupsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectGroups400", ListProjectGroups400> + | OpenAiClientError<"ListProjectGroups403", ListProjectGroups403> + | OpenAiClientError<"ListProjectGroups404", ListProjectGroups404> + | OpenAiClientError<"ListProjectGroups429", ListProjectGroups429> + > + } + /** + * Grants a group access to a project. + */ + readonly "addProjectGroup": { + ( + projectId: string, + options: { readonly payload: typeof AddProjectGroupRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AddProjectGroup400", AddProjectGroup400> + | OpenAiClientError<"AddProjectGroup403", AddProjectGroup403> + | OpenAiClientError<"AddProjectGroup404", AddProjectGroup404> + | OpenAiClientError<"AddProjectGroup429", AddProjectGroup429> + | OpenAiClientError<"AddProjectGroup500", AddProjectGroup500> + > + ( + projectId: string, + options: { readonly payload: typeof AddProjectGroupRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AddProjectGroup400", AddProjectGroup400> + | OpenAiClientError<"AddProjectGroup403", AddProjectGroup403> + | OpenAiClientError<"AddProjectGroup404", AddProjectGroup404> + | OpenAiClientError<"AddProjectGroup429", AddProjectGroup429> + | OpenAiClientError<"AddProjectGroup500", AddProjectGroup500> + > + } + /** + * Retrieves a project's group. + */ + readonly "retrieveProjectGroup": { + ( + projectId: string, + groupId: string, + options: { readonly params?: typeof RetrieveProjectGroupParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectGroup400", RetrieveProjectGroup400> + | OpenAiClientError<"RetrieveProjectGroup403", RetrieveProjectGroup403> + | OpenAiClientError<"RetrieveProjectGroup404", RetrieveProjectGroup404> + | OpenAiClientError<"RetrieveProjectGroup429", RetrieveProjectGroup429> + > + ( + projectId: string, + groupId: string, + options: { + readonly params?: typeof RetrieveProjectGroupParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectGroup400", RetrieveProjectGroup400> + | OpenAiClientError<"RetrieveProjectGroup403", RetrieveProjectGroup403> + | OpenAiClientError<"RetrieveProjectGroup404", RetrieveProjectGroup404> + | OpenAiClientError<"RetrieveProjectGroup429", RetrieveProjectGroup429> + > + } + /** + * Revokes a group's access to a project. + */ + readonly "removeProjectGroup": { + ( + projectId: string, + groupId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RemoveProjectGroup400", RemoveProjectGroup400> + | OpenAiClientError<"RemoveProjectGroup403", RemoveProjectGroup403> + | OpenAiClientError<"RemoveProjectGroup404", RemoveProjectGroup404> + | OpenAiClientError<"RemoveProjectGroup429", RemoveProjectGroup429> + > + ( + projectId: string, + groupId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RemoveProjectGroup400", RemoveProjectGroup400> + | OpenAiClientError<"RemoveProjectGroup403", RemoveProjectGroup403> + | OpenAiClientError<"RemoveProjectGroup404", RemoveProjectGroup404> + | OpenAiClientError<"RemoveProjectGroup429", RemoveProjectGroup429> + > + } + /** + * Returns hosted tool permissions for a project. + */ + readonly "retrieveProjectHostedToolPermissions": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectHostedToolPermissions400", RetrieveProjectHostedToolPermissions400> + | OpenAiClientError<"RetrieveProjectHostedToolPermissions403", RetrieveProjectHostedToolPermissions403> + | OpenAiClientError<"RetrieveProjectHostedToolPermissions404", RetrieveProjectHostedToolPermissions404> + | OpenAiClientError<"RetrieveProjectHostedToolPermissions429", RetrieveProjectHostedToolPermissions429> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectHostedToolPermissions400", RetrieveProjectHostedToolPermissions400> + | OpenAiClientError<"RetrieveProjectHostedToolPermissions403", RetrieveProjectHostedToolPermissions403> + | OpenAiClientError<"RetrieveProjectHostedToolPermissions404", RetrieveProjectHostedToolPermissions404> + | OpenAiClientError<"RetrieveProjectHostedToolPermissions429", RetrieveProjectHostedToolPermissions429> + > + } + /** + * Updates hosted tool permissions for a project. + */ + readonly "updateProjectHostedToolPermissions": { + ( + projectId: string, + options: { + readonly payload: typeof UpdateProjectHostedToolPermissionsRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectHostedToolPermissions400", UpdateProjectHostedToolPermissions400> + | OpenAiClientError<"UpdateProjectHostedToolPermissions403", UpdateProjectHostedToolPermissions403> + | OpenAiClientError<"UpdateProjectHostedToolPermissions404", UpdateProjectHostedToolPermissions404> + | OpenAiClientError<"UpdateProjectHostedToolPermissions409", UpdateProjectHostedToolPermissions409> + | OpenAiClientError<"UpdateProjectHostedToolPermissions429", UpdateProjectHostedToolPermissions429> + > + ( + projectId: string, + options: { + readonly payload: typeof UpdateProjectHostedToolPermissionsRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectHostedToolPermissions400", UpdateProjectHostedToolPermissions400> + | OpenAiClientError<"UpdateProjectHostedToolPermissions403", UpdateProjectHostedToolPermissions403> + | OpenAiClientError<"UpdateProjectHostedToolPermissions404", UpdateProjectHostedToolPermissions404> + | OpenAiClientError<"UpdateProjectHostedToolPermissions409", UpdateProjectHostedToolPermissions409> + | OpenAiClientError<"UpdateProjectHostedToolPermissions429", UpdateProjectHostedToolPermissions429> + > + } + /** + * Returns model permissions for a project. + */ + readonly "retrieveProjectModelPermissions": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectModelPermissions400", RetrieveProjectModelPermissions400> + | OpenAiClientError<"RetrieveProjectModelPermissions403", RetrieveProjectModelPermissions403> + | OpenAiClientError<"RetrieveProjectModelPermissions404", RetrieveProjectModelPermissions404> + | OpenAiClientError<"RetrieveProjectModelPermissions429", RetrieveProjectModelPermissions429> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectModelPermissions400", RetrieveProjectModelPermissions400> + | OpenAiClientError<"RetrieveProjectModelPermissions403", RetrieveProjectModelPermissions403> + | OpenAiClientError<"RetrieveProjectModelPermissions404", RetrieveProjectModelPermissions404> + | OpenAiClientError<"RetrieveProjectModelPermissions429", RetrieveProjectModelPermissions429> + > + } + /** + * Updates model permissions for a project. + */ + readonly "updateProjectModelPermissions": { + ( + projectId: string, + options: { readonly payload: typeof UpdateProjectModelPermissionsRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectModelPermissions400", UpdateProjectModelPermissions400> + | OpenAiClientError<"UpdateProjectModelPermissions403", UpdateProjectModelPermissions403> + | OpenAiClientError<"UpdateProjectModelPermissions404", UpdateProjectModelPermissions404> + | OpenAiClientError<"UpdateProjectModelPermissions429", UpdateProjectModelPermissions429> + > + ( + projectId: string, + options: { + readonly payload: typeof UpdateProjectModelPermissionsRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectModelPermissions400", UpdateProjectModelPermissions400> + | OpenAiClientError<"UpdateProjectModelPermissions403", UpdateProjectModelPermissions403> + | OpenAiClientError<"UpdateProjectModelPermissions404", UpdateProjectModelPermissions404> + | OpenAiClientError<"UpdateProjectModelPermissions429", UpdateProjectModelPermissions429> + > + } + /** + * Deletes model permissions for a project. + */ + readonly "deleteProjectModelPermissions": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectModelPermissions400", DeleteProjectModelPermissions400> + | OpenAiClientError<"DeleteProjectModelPermissions403", DeleteProjectModelPermissions403> + | OpenAiClientError<"DeleteProjectModelPermissions404", DeleteProjectModelPermissions404> + | OpenAiClientError<"DeleteProjectModelPermissions429", DeleteProjectModelPermissions429> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectModelPermissions400", DeleteProjectModelPermissions400> + | OpenAiClientError<"DeleteProjectModelPermissions403", DeleteProjectModelPermissions403> + | OpenAiClientError<"DeleteProjectModelPermissions404", DeleteProjectModelPermissions404> + | OpenAiClientError<"DeleteProjectModelPermissions429", DeleteProjectModelPermissions429> + > + } + /** + * Returns the rate limits per model for a project. + */ + readonly "listProjectRateLimits": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectRateLimitsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectRateLimits400", ListProjectRateLimits400> + | OpenAiClientError<"ListProjectRateLimits403", ListProjectRateLimits403> + | OpenAiClientError<"ListProjectRateLimits404", ListProjectRateLimits404> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectRateLimitsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectRateLimits400", ListProjectRateLimits400> + | OpenAiClientError<"ListProjectRateLimits403", ListProjectRateLimits403> + | OpenAiClientError<"ListProjectRateLimits404", ListProjectRateLimits404> + > + } + /** + * Updates a project rate limit. + */ + readonly "updateProjectRateLimits": { + ( + projectId: string, + rateLimitId: string, + options: { readonly payload: typeof UpdateProjectRateLimitsRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectRateLimits400", UpdateProjectRateLimits400> + | OpenAiClientError<"UpdateProjectRateLimits403", UpdateProjectRateLimits403> + | OpenAiClientError<"UpdateProjectRateLimits404", UpdateProjectRateLimits404> + > + ( + projectId: string, + rateLimitId: string, + options: { + readonly payload: typeof UpdateProjectRateLimitsRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectRateLimits400", UpdateProjectRateLimits400> + | OpenAiClientError<"UpdateProjectRateLimits403", UpdateProjectRateLimits403> + | OpenAiClientError<"UpdateProjectRateLimits404", UpdateProjectRateLimits404> + > + } + /** + * Returns a list of service accounts in the project. + */ + readonly "listProjectServiceAccounts": { + ( + projectId: string, + options: { + readonly params?: typeof ListProjectServiceAccountsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectServiceAccounts400", ListProjectServiceAccounts400> + | OpenAiClientError<"ListProjectServiceAccounts403", ListProjectServiceAccounts403> + | OpenAiClientError<"ListProjectServiceAccounts404", ListProjectServiceAccounts404> + | OpenAiClientError<"ListProjectServiceAccounts429", ListProjectServiceAccounts429> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectServiceAccountsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectServiceAccounts400", ListProjectServiceAccounts400> + | OpenAiClientError<"ListProjectServiceAccounts403", ListProjectServiceAccounts403> + | OpenAiClientError<"ListProjectServiceAccounts404", ListProjectServiceAccounts404> + | OpenAiClientError<"ListProjectServiceAccounts429", ListProjectServiceAccounts429> + > + } + /** + * Creates a new service account in the project. By default, this also returns an unredacted API key for the service account. + */ + readonly "createProjectServiceAccount": { + ( + projectId: string, + options: { readonly payload: typeof CreateProjectServiceAccountRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectServiceAccount400", CreateProjectServiceAccount400> + | OpenAiClientError<"CreateProjectServiceAccount403", CreateProjectServiceAccount403> + | OpenAiClientError<"CreateProjectServiceAccount404", CreateProjectServiceAccount404> + | OpenAiClientError<"CreateProjectServiceAccount429", CreateProjectServiceAccount429> + > + ( + projectId: string, + options: { + readonly payload: typeof CreateProjectServiceAccountRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectServiceAccount400", CreateProjectServiceAccount400> + | OpenAiClientError<"CreateProjectServiceAccount403", CreateProjectServiceAccount403> + | OpenAiClientError<"CreateProjectServiceAccount404", CreateProjectServiceAccount404> + | OpenAiClientError<"CreateProjectServiceAccount429", CreateProjectServiceAccount429> + > + } + /** + * Retrieves a service account in the project. + */ + readonly "retrieveProjectServiceAccount": { + ( + projectId: string, + serviceAccountId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectServiceAccount400", RetrieveProjectServiceAccount400> + | OpenAiClientError<"RetrieveProjectServiceAccount403", RetrieveProjectServiceAccount403> + | OpenAiClientError<"RetrieveProjectServiceAccount404", RetrieveProjectServiceAccount404> + | OpenAiClientError<"RetrieveProjectServiceAccount429", RetrieveProjectServiceAccount429> + > + ( + projectId: string, + serviceAccountId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectServiceAccount400", RetrieveProjectServiceAccount400> + | OpenAiClientError<"RetrieveProjectServiceAccount403", RetrieveProjectServiceAccount403> + | OpenAiClientError<"RetrieveProjectServiceAccount404", RetrieveProjectServiceAccount404> + | OpenAiClientError<"RetrieveProjectServiceAccount429", RetrieveProjectServiceAccount429> + > + } + /** + * Updates a service account in the project. + */ + readonly "updateProjectServiceAccount": { + ( + projectId: string, + serviceAccountId: string, + options: { readonly payload: typeof UpdateProjectServiceAccountRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectServiceAccount400", UpdateProjectServiceAccount400> + | OpenAiClientError<"UpdateProjectServiceAccount403", UpdateProjectServiceAccount403> + | OpenAiClientError<"UpdateProjectServiceAccount404", UpdateProjectServiceAccount404> + | OpenAiClientError<"UpdateProjectServiceAccount429", UpdateProjectServiceAccount429> + > + ( + projectId: string, + serviceAccountId: string, + options: { + readonly payload: typeof UpdateProjectServiceAccountRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectServiceAccount400", UpdateProjectServiceAccount400> + | OpenAiClientError<"UpdateProjectServiceAccount403", UpdateProjectServiceAccount403> + | OpenAiClientError<"UpdateProjectServiceAccount404", UpdateProjectServiceAccount404> + | OpenAiClientError<"UpdateProjectServiceAccount429", UpdateProjectServiceAccount429> + > + } + /** + * Deletes a service account from the project. + * + * Returns confirmation of service account deletion, or an error if the project + * is archived (archived projects have no service accounts). + */ + readonly "deleteProjectServiceAccount": { + ( + projectId: string, + serviceAccountId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectServiceAccount400", DeleteProjectServiceAccount400> + | OpenAiClientError<"DeleteProjectServiceAccount403", DeleteProjectServiceAccount403> + | OpenAiClientError<"DeleteProjectServiceAccount404", DeleteProjectServiceAccount404> + | OpenAiClientError<"DeleteProjectServiceAccount429", DeleteProjectServiceAccount429> + > + ( + projectId: string, + serviceAccountId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectServiceAccount400", DeleteProjectServiceAccount400> + | OpenAiClientError<"DeleteProjectServiceAccount403", DeleteProjectServiceAccount403> + | OpenAiClientError<"DeleteProjectServiceAccount404", DeleteProjectServiceAccount404> + | OpenAiClientError<"DeleteProjectServiceAccount429", DeleteProjectServiceAccount429> + > + } + /** + * Lists project spend alerts. + */ + readonly "listProjectSpendAlerts": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectSpendAlertsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectSpendAlerts400", ListProjectSpendAlerts400> + | OpenAiClientError<"ListProjectSpendAlerts403", ListProjectSpendAlerts403> + | OpenAiClientError<"ListProjectSpendAlerts404", ListProjectSpendAlerts404> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectSpendAlertsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectSpendAlerts400", ListProjectSpendAlerts400> + | OpenAiClientError<"ListProjectSpendAlerts403", ListProjectSpendAlerts403> + | OpenAiClientError<"ListProjectSpendAlerts404", ListProjectSpendAlerts404> + > + } + /** + * Creates a project spend alert. + */ + readonly "createProjectSpendAlert": { + ( + projectId: string, + options: { readonly payload: typeof CreateProjectSpendAlertRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectSpendAlert400", CreateProjectSpendAlert400> + | OpenAiClientError<"CreateProjectSpendAlert403", CreateProjectSpendAlert403> + | OpenAiClientError<"CreateProjectSpendAlert404", CreateProjectSpendAlert404> + > + ( + projectId: string, + options: { + readonly payload: typeof CreateProjectSpendAlertRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectSpendAlert400", CreateProjectSpendAlert400> + | OpenAiClientError<"CreateProjectSpendAlert403", CreateProjectSpendAlert403> + | OpenAiClientError<"CreateProjectSpendAlert404", CreateProjectSpendAlert404> + > + } + /** + * Retrieves a project spend alert. + */ + readonly "retrieveProjectSpendAlert": { + ( + projectId: string, + alertId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectSpendAlert400", RetrieveProjectSpendAlert400> + | OpenAiClientError<"RetrieveProjectSpendAlert403", RetrieveProjectSpendAlert403> + | OpenAiClientError<"RetrieveProjectSpendAlert404", RetrieveProjectSpendAlert404> + > + ( + projectId: string, + alertId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectSpendAlert400", RetrieveProjectSpendAlert400> + | OpenAiClientError<"RetrieveProjectSpendAlert403", RetrieveProjectSpendAlert403> + | OpenAiClientError<"RetrieveProjectSpendAlert404", RetrieveProjectSpendAlert404> + > + } + /** + * Updates a project spend alert. + */ + readonly "updateProjectSpendAlert": { + ( + projectId: string, + alertId: string, + options: { readonly payload: typeof UpdateProjectSpendAlertRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectSpendAlert400", UpdateProjectSpendAlert400> + | OpenAiClientError<"UpdateProjectSpendAlert403", UpdateProjectSpendAlert403> + | OpenAiClientError<"UpdateProjectSpendAlert404", UpdateProjectSpendAlert404> + > + ( + projectId: string, + alertId: string, + options: { + readonly payload: typeof UpdateProjectSpendAlertRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectSpendAlert400", UpdateProjectSpendAlert400> + | OpenAiClientError<"UpdateProjectSpendAlert403", UpdateProjectSpendAlert403> + | OpenAiClientError<"UpdateProjectSpendAlert404", UpdateProjectSpendAlert404> + > + } + /** + * Deletes a project spend alert. + */ + readonly "deleteProjectSpendAlert": { + ( + projectId: string, + alertId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectSpendAlert400", DeleteProjectSpendAlert400> + | OpenAiClientError<"DeleteProjectSpendAlert403", DeleteProjectSpendAlert403> + | OpenAiClientError<"DeleteProjectSpendAlert404", DeleteProjectSpendAlert404> + > + ( + projectId: string, + alertId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectSpendAlert400", DeleteProjectSpendAlert400> + | OpenAiClientError<"DeleteProjectSpendAlert403", DeleteProjectSpendAlert403> + | OpenAiClientError<"DeleteProjectSpendAlert404", DeleteProjectSpendAlert404> + > + } + /** + * Returns a list of users in the project. + */ + readonly "listProjectUsers": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectUsersParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectUsers400", ListProjectUsers400> + | OpenAiClientError<"ListProjectUsers403", ListProjectUsers403> + | OpenAiClientError<"ListProjectUsers404", ListProjectUsers404> + | OpenAiClientError<"ListProjectUsers429", ListProjectUsers429> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectUsersParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectUsers400", ListProjectUsers400> + | OpenAiClientError<"ListProjectUsers403", ListProjectUsers403> + | OpenAiClientError<"ListProjectUsers404", ListProjectUsers404> + | OpenAiClientError<"ListProjectUsers429", ListProjectUsers429> + > + } + /** + * Adds a user to the project. Users must already be members of the organization to be added to a project. + */ + readonly "createProjectUser": { + ( + projectId: string, + options: { readonly payload: typeof CreateProjectUserRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectUser400", CreateProjectUser400> + | OpenAiClientError<"CreateProjectUser403", CreateProjectUser403> + | OpenAiClientError<"CreateProjectUser404", CreateProjectUser404> + | OpenAiClientError<"CreateProjectUser409", CreateProjectUser409> + | OpenAiClientError<"CreateProjectUser429", CreateProjectUser429> + > + ( + projectId: string, + options: { readonly payload: typeof CreateProjectUserRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectUser400", CreateProjectUser400> + | OpenAiClientError<"CreateProjectUser403", CreateProjectUser403> + | OpenAiClientError<"CreateProjectUser404", CreateProjectUser404> + | OpenAiClientError<"CreateProjectUser409", CreateProjectUser409> + | OpenAiClientError<"CreateProjectUser429", CreateProjectUser429> + > + } + /** + * Retrieves a user in the project. + */ + readonly "retrieveProjectUser": { + ( + projectId: string, + userId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectUser400", RetrieveProjectUser400> + | OpenAiClientError<"RetrieveProjectUser403", RetrieveProjectUser403> + | OpenAiClientError<"RetrieveProjectUser404", RetrieveProjectUser404> + | OpenAiClientError<"RetrieveProjectUser429", RetrieveProjectUser429> + > + ( + projectId: string, + userId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectUser400", RetrieveProjectUser400> + | OpenAiClientError<"RetrieveProjectUser403", RetrieveProjectUser403> + | OpenAiClientError<"RetrieveProjectUser404", RetrieveProjectUser404> + | OpenAiClientError<"RetrieveProjectUser429", RetrieveProjectUser429> + > + } + /** + * Modifies a user's role in the project. + */ + readonly "modifyProjectUser": { + ( + projectId: string, + userId: string, + options: { readonly payload: typeof ModifyProjectUserRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyProjectUser400", ModifyProjectUser400> + | OpenAiClientError<"ModifyProjectUser403", ModifyProjectUser403> + | OpenAiClientError<"ModifyProjectUser404", ModifyProjectUser404> + | OpenAiClientError<"ModifyProjectUser429", ModifyProjectUser429> + > + ( + projectId: string, + userId: string, + options: { readonly payload: typeof ModifyProjectUserRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyProjectUser400", ModifyProjectUser400> + | OpenAiClientError<"ModifyProjectUser403", ModifyProjectUser403> + | OpenAiClientError<"ModifyProjectUser404", ModifyProjectUser404> + | OpenAiClientError<"ModifyProjectUser429", ModifyProjectUser429> + > + } + /** + * Deletes a user from the project. + * + * Returns confirmation of project user deletion, or an error if the project is + * archived (archived projects have no users). + */ + readonly "deleteProjectUser": { + ( + projectId: string, + userId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectUser400", DeleteProjectUser400> + | OpenAiClientError<"DeleteProjectUser403", DeleteProjectUser403> + | OpenAiClientError<"DeleteProjectUser404", DeleteProjectUser404> + | OpenAiClientError<"DeleteProjectUser429", DeleteProjectUser429> + > + ( + projectId: string, + userId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectUser400", DeleteProjectUser400> + | OpenAiClientError<"DeleteProjectUser403", DeleteProjectUser403> + | OpenAiClientError<"DeleteProjectUser404", DeleteProjectUser404> + | OpenAiClientError<"DeleteProjectUser429", DeleteProjectUser429> + > + } + /** + * Lists the roles configured for the organization. + */ + readonly "listRoles": { + ( + options: { readonly params?: typeof ListRolesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListRoles400", ListRoles400> + | OpenAiClientError<"ListRoles403", ListRoles403> + | OpenAiClientError<"ListRoles404", ListRoles404> + | OpenAiClientError<"ListRoles429", ListRoles429> + > + ( + options: + | { readonly params?: typeof ListRolesParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListRoles400", ListRoles400> + | OpenAiClientError<"ListRoles403", ListRoles403> + | OpenAiClientError<"ListRoles404", ListRoles404> + | OpenAiClientError<"ListRoles429", ListRoles429> + > + } + /** + * Creates a custom role for the organization. + */ + readonly "createRole": { + ( + options: { readonly payload: typeof CreateRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateRole400", CreateRole400> + | OpenAiClientError<"CreateRole403", CreateRole403> + | OpenAiClientError<"CreateRole404", CreateRole404> + | OpenAiClientError<"CreateRole429", CreateRole429> + > + ( + options: { readonly payload: typeof CreateRoleRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateRole400", CreateRole400> + | OpenAiClientError<"CreateRole403", CreateRole403> + | OpenAiClientError<"CreateRole404", CreateRole404> + | OpenAiClientError<"CreateRole429", CreateRole429> + > + } + /** + * Retrieves an organization role. + */ + readonly "retrieveRole": { + ( + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveRole403", RetrieveRole403> + | OpenAiClientError<"RetrieveRole404", RetrieveRole404> + | OpenAiClientError<"RetrieveRole429", RetrieveRole429> + > + ( + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveRole403", RetrieveRole403> + | OpenAiClientError<"RetrieveRole404", RetrieveRole404> + | OpenAiClientError<"RetrieveRole429", RetrieveRole429> + > + } + /** + * Updates an existing organization role. + */ + readonly "updateRole": { + ( + roleId: string, + options: { readonly payload: typeof UpdateRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateRole400", UpdateRole400> + | OpenAiClientError<"UpdateRole403", UpdateRole403> + | OpenAiClientError<"UpdateRole404", UpdateRole404> + | OpenAiClientError<"UpdateRole429", UpdateRole429> + > + ( + roleId: string, + options: { readonly payload: typeof UpdateRoleRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateRole400", UpdateRole400> + | OpenAiClientError<"UpdateRole403", UpdateRole403> + | OpenAiClientError<"UpdateRole404", UpdateRole404> + | OpenAiClientError<"UpdateRole429", UpdateRole429> + > + } + /** + * Deletes a custom role from the organization. + */ + readonly "deleteRole": { + ( + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteRole400", DeleteRole400> + | OpenAiClientError<"DeleteRole403", DeleteRole403> + | OpenAiClientError<"DeleteRole404", DeleteRole404> + | OpenAiClientError<"DeleteRole429", DeleteRole429> + > + ( + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteRole400", DeleteRole400> + | OpenAiClientError<"DeleteRole403", DeleteRole403> + | OpenAiClientError<"DeleteRole404", DeleteRole404> + | OpenAiClientError<"DeleteRole429", DeleteRole429> + > + } + /** + * Lists organization spend alerts. + */ + readonly "listOrganizationSpendAlerts": { + ( + options: { + readonly params?: typeof ListOrganizationSpendAlertsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListOrganizationSpendAlerts400", ListOrganizationSpendAlerts400> + | OpenAiClientError<"ListOrganizationSpendAlerts403", ListOrganizationSpendAlerts403> + | OpenAiClientError<"ListOrganizationSpendAlerts404", ListOrganizationSpendAlerts404> + > + ( + options: { + readonly params?: typeof ListOrganizationSpendAlertsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListOrganizationSpendAlerts400", ListOrganizationSpendAlerts400> + | OpenAiClientError<"ListOrganizationSpendAlerts403", ListOrganizationSpendAlerts403> + | OpenAiClientError<"ListOrganizationSpendAlerts404", ListOrganizationSpendAlerts404> + > + } + /** + * Creates an organization spend alert. + */ + readonly "createOrganizationSpendAlert": { + ( + options: { readonly payload: typeof CreateOrganizationSpendAlertRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateOrganizationSpendAlert400", CreateOrganizationSpendAlert400> + | OpenAiClientError<"CreateOrganizationSpendAlert403", CreateOrganizationSpendAlert403> + > + ( + options: { + readonly payload: typeof CreateOrganizationSpendAlertRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateOrganizationSpendAlert400", CreateOrganizationSpendAlert400> + | OpenAiClientError<"CreateOrganizationSpendAlert403", CreateOrganizationSpendAlert403> + > + } + /** + * Retrieves an organization spend alert. + */ + readonly "retrieveOrganizationSpendAlert": { + ( + alertId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveOrganizationSpendAlert403", RetrieveOrganizationSpendAlert403> + | OpenAiClientError<"RetrieveOrganizationSpendAlert404", RetrieveOrganizationSpendAlert404> + > + ( + alertId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveOrganizationSpendAlert403", RetrieveOrganizationSpendAlert403> + | OpenAiClientError<"RetrieveOrganizationSpendAlert404", RetrieveOrganizationSpendAlert404> + > + } + /** + * Updates an organization spend alert. + */ + readonly "updateOrganizationSpendAlert": { + ( + alertId: string, + options: { readonly payload: typeof UpdateOrganizationSpendAlertRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateOrganizationSpendAlert400", UpdateOrganizationSpendAlert400> + | OpenAiClientError<"UpdateOrganizationSpendAlert403", UpdateOrganizationSpendAlert403> + | OpenAiClientError<"UpdateOrganizationSpendAlert404", UpdateOrganizationSpendAlert404> + > + ( + alertId: string, + options: { + readonly payload: typeof UpdateOrganizationSpendAlertRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateOrganizationSpendAlert400", UpdateOrganizationSpendAlert400> + | OpenAiClientError<"UpdateOrganizationSpendAlert403", UpdateOrganizationSpendAlert403> + | OpenAiClientError<"UpdateOrganizationSpendAlert404", UpdateOrganizationSpendAlert404> + > + } + /** + * Deletes an organization spend alert. + */ + readonly "deleteOrganizationSpendAlert": { + ( + alertId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteOrganizationSpendAlert403", DeleteOrganizationSpendAlert403> + | OpenAiClientError<"DeleteOrganizationSpendAlert404", DeleteOrganizationSpendAlert404> + > + ( + alertId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteOrganizationSpendAlert403", DeleteOrganizationSpendAlert403> + | OpenAiClientError<"DeleteOrganizationSpendAlert404", DeleteOrganizationSpendAlert404> + > + } + /** + * Get audio speeches usage details for the organization. + */ + readonly "usageAudioSpeeches": { + ( + options: { readonly params: typeof UsageAudioSpeechesParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageAudioSpeeches400", UsageAudioSpeeches400> + | OpenAiClientError<"UsageAudioSpeeches403", UsageAudioSpeeches403> + | OpenAiClientError<"UsageAudioSpeeches429", UsageAudioSpeeches429> + > + ( + options: { readonly params: typeof UsageAudioSpeechesParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageAudioSpeeches400", UsageAudioSpeeches400> + | OpenAiClientError<"UsageAudioSpeeches403", UsageAudioSpeeches403> + | OpenAiClientError<"UsageAudioSpeeches429", UsageAudioSpeeches429> + > + } + /** + * Get audio transcriptions usage details for the organization. + */ + readonly "usageAudioTranscriptions": { + ( + options: { readonly params: typeof UsageAudioTranscriptionsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageAudioTranscriptions400", UsageAudioTranscriptions400> + | OpenAiClientError<"UsageAudioTranscriptions403", UsageAudioTranscriptions403> + | OpenAiClientError<"UsageAudioTranscriptions429", UsageAudioTranscriptions429> + > + ( + options: { readonly params: typeof UsageAudioTranscriptionsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageAudioTranscriptions400", UsageAudioTranscriptions400> + | OpenAiClientError<"UsageAudioTranscriptions403", UsageAudioTranscriptions403> + | OpenAiClientError<"UsageAudioTranscriptions429", UsageAudioTranscriptions429> + > + } + /** + * Get code interpreter sessions usage details for the organization. + */ + readonly "usageCodeInterpreterSessions": { + ( + options: { readonly params: typeof UsageCodeInterpreterSessionsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageCodeInterpreterSessions400", UsageCodeInterpreterSessions400> + | OpenAiClientError<"UsageCodeInterpreterSessions403", UsageCodeInterpreterSessions403> + | OpenAiClientError<"UsageCodeInterpreterSessions429", UsageCodeInterpreterSessions429> + > + ( + options: { + readonly params: typeof UsageCodeInterpreterSessionsParams.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageCodeInterpreterSessions400", UsageCodeInterpreterSessions400> + | OpenAiClientError<"UsageCodeInterpreterSessions403", UsageCodeInterpreterSessions403> + | OpenAiClientError<"UsageCodeInterpreterSessions429", UsageCodeInterpreterSessions429> + > + } + /** + * Get completions usage details for the organization. + */ + readonly "usageCompletions": { + ( + options: { readonly params: typeof UsageCompletionsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageCompletions400", UsageCompletions400> + | OpenAiClientError<"UsageCompletions403", UsageCompletions403> + | OpenAiClientError<"UsageCompletions429", UsageCompletions429> + > + ( + options: { readonly params: typeof UsageCompletionsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageCompletions400", UsageCompletions400> + | OpenAiClientError<"UsageCompletions403", UsageCompletions403> + | OpenAiClientError<"UsageCompletions429", UsageCompletions429> + > + } + /** + * Get embeddings usage details for the organization. + */ + readonly "usageEmbeddings": { + ( + options: { readonly params: typeof UsageEmbeddingsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageEmbeddings400", UsageEmbeddings400> + | OpenAiClientError<"UsageEmbeddings403", UsageEmbeddings403> + | OpenAiClientError<"UsageEmbeddings429", UsageEmbeddings429> + > + ( + options: { readonly params: typeof UsageEmbeddingsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageEmbeddings400", UsageEmbeddings400> + | OpenAiClientError<"UsageEmbeddings403", UsageEmbeddings403> + | OpenAiClientError<"UsageEmbeddings429", UsageEmbeddings429> + > + } + /** + * Get file search calls usage details for the organization. + */ + readonly "usageFileSearchCalls": { + ( + options: { readonly params: typeof UsageFileSearchCallsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageFileSearchCalls400", UsageFileSearchCalls400> + | OpenAiClientError<"UsageFileSearchCalls403", UsageFileSearchCalls403> + | OpenAiClientError<"UsageFileSearchCalls429", UsageFileSearchCalls429> + > + ( + options: { readonly params: typeof UsageFileSearchCallsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageFileSearchCalls400", UsageFileSearchCalls400> + | OpenAiClientError<"UsageFileSearchCalls403", UsageFileSearchCalls403> + | OpenAiClientError<"UsageFileSearchCalls429", UsageFileSearchCalls429> + > + } + /** + * Get images usage details for the organization. + */ + readonly "usageImages": { + ( + options: { readonly params: typeof UsageImagesParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageImages400", UsageImages400> + | OpenAiClientError<"UsageImages403", UsageImages403> + | OpenAiClientError<"UsageImages429", UsageImages429> + > + ( + options: { readonly params: typeof UsageImagesParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageImages400", UsageImages400> + | OpenAiClientError<"UsageImages403", UsageImages403> + | OpenAiClientError<"UsageImages429", UsageImages429> + > + } + /** + * Get moderations usage details for the organization. + */ + readonly "usageModerations": { + ( + options: { readonly params: typeof UsageModerationsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageModerations400", UsageModerations400> + | OpenAiClientError<"UsageModerations403", UsageModerations403> + | OpenAiClientError<"UsageModerations429", UsageModerations429> + > + ( + options: { readonly params: typeof UsageModerationsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageModerations400", UsageModerations400> + | OpenAiClientError<"UsageModerations403", UsageModerations403> + | OpenAiClientError<"UsageModerations429", UsageModerations429> + > + } + /** + * Get vector stores usage details for the organization. + */ + readonly "usageVectorStores": { + ( + options: { readonly params: typeof UsageVectorStoresParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageVectorStores400", UsageVectorStores400> + | OpenAiClientError<"UsageVectorStores403", UsageVectorStores403> + | OpenAiClientError<"UsageVectorStores429", UsageVectorStores429> + > + ( + options: { readonly params: typeof UsageVectorStoresParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageVectorStores400", UsageVectorStores400> + | OpenAiClientError<"UsageVectorStores403", UsageVectorStores403> + | OpenAiClientError<"UsageVectorStores429", UsageVectorStores429> + > + } + /** + * Get web search calls usage details for the organization. + */ + readonly "usageWebSearchCalls": { + ( + options: { readonly params: typeof UsageWebSearchCallsParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageWebSearchCalls400", UsageWebSearchCalls400> + | OpenAiClientError<"UsageWebSearchCalls403", UsageWebSearchCalls403> + | OpenAiClientError<"UsageWebSearchCalls429", UsageWebSearchCalls429> + > + ( + options: { readonly params: typeof UsageWebSearchCallsParams.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UsageWebSearchCalls400", UsageWebSearchCalls400> + | OpenAiClientError<"UsageWebSearchCalls403", UsageWebSearchCalls403> + | OpenAiClientError<"UsageWebSearchCalls429", UsageWebSearchCalls429> + > + } + /** + * Lists all of the users in the organization. + */ + readonly "listUsers": { + ( + options: { readonly params?: typeof ListUsersParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListUsers400", ListUsers400> + | OpenAiClientError<"ListUsers403", ListUsers403> + | OpenAiClientError<"ListUsers429", ListUsers429> + > + ( + options: + | { readonly params?: typeof ListUsersParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListUsers400", ListUsers400> + | OpenAiClientError<"ListUsers403", ListUsers403> + | OpenAiClientError<"ListUsers429", ListUsers429> + > + } + /** + * Retrieves a user by their identifier. + */ + readonly "retrieveUser": { + ( + userId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveUser403", RetrieveUser403> + | OpenAiClientError<"RetrieveUser404", RetrieveUser404> + | OpenAiClientError<"RetrieveUser429", RetrieveUser429> + > + ( + userId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveUser403", RetrieveUser403> + | OpenAiClientError<"RetrieveUser404", RetrieveUser404> + | OpenAiClientError<"RetrieveUser429", RetrieveUser429> + > + } + /** + * Modifies a user's role in the organization. + */ + readonly "modifyUser": { + ( + userId: string, + options: { readonly payload: typeof ModifyUserRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyUser400", ModifyUser400> + | OpenAiClientError<"ModifyUser403", ModifyUser403> + | OpenAiClientError<"ModifyUser404", ModifyUser404> + | OpenAiClientError<"ModifyUser429", ModifyUser429> + > + ( + userId: string, + options: { readonly payload: typeof ModifyUserRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyUser400", ModifyUser400> + | OpenAiClientError<"ModifyUser403", ModifyUser403> + | OpenAiClientError<"ModifyUser404", ModifyUser404> + | OpenAiClientError<"ModifyUser429", ModifyUser429> + > + } + /** + * Deletes a user from the organization. + */ + readonly "deleteUser": { + ( + userId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteUser400", DeleteUser400> + | OpenAiClientError<"DeleteUser403", DeleteUser403> + | OpenAiClientError<"DeleteUser404", DeleteUser404> + | OpenAiClientError<"DeleteUser429", DeleteUser429> + > + ( + userId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteUser400", DeleteUser400> + | OpenAiClientError<"DeleteUser403", DeleteUser403> + | OpenAiClientError<"DeleteUser404", DeleteUser404> + | OpenAiClientError<"DeleteUser429", DeleteUser429> + > + } + /** + * Lists the organization roles assigned to a user within the organization. + */ + readonly "listUserRoleAssignments": { + ( + userId: string, + options: { readonly params?: typeof ListUserRoleAssignmentsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListUserRoleAssignments400", ListUserRoleAssignments400> + | OpenAiClientError<"ListUserRoleAssignments403", ListUserRoleAssignments403> + | OpenAiClientError<"ListUserRoleAssignments404", ListUserRoleAssignments404> + | OpenAiClientError<"ListUserRoleAssignments429", ListUserRoleAssignments429> + > + ( + userId: string, + options: { + readonly params?: typeof ListUserRoleAssignmentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListUserRoleAssignments400", ListUserRoleAssignments400> + | OpenAiClientError<"ListUserRoleAssignments403", ListUserRoleAssignments403> + | OpenAiClientError<"ListUserRoleAssignments404", ListUserRoleAssignments404> + | OpenAiClientError<"ListUserRoleAssignments429", ListUserRoleAssignments429> + > + } + /** + * Assigns an organization role to a user within the organization. + */ + readonly "assignUserRole": { + ( + userId: string, + options: { readonly payload: typeof AssignUserRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignUserRole400", AssignUserRole400> + | OpenAiClientError<"AssignUserRole403", AssignUserRole403> + | OpenAiClientError<"AssignUserRole404", AssignUserRole404> + | OpenAiClientError<"AssignUserRole429", AssignUserRole429> + > + ( + userId: string, + options: { readonly payload: typeof AssignUserRoleRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignUserRole400", AssignUserRole400> + | OpenAiClientError<"AssignUserRole403", AssignUserRole403> + | OpenAiClientError<"AssignUserRole404", AssignUserRole404> + | OpenAiClientError<"AssignUserRole429", AssignUserRole429> + > + } + /** + * Retrieves an organization role assigned to a user. + */ + readonly "retrieveUserRole": { + ( + userId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveUserRole400", RetrieveUserRole400> + | OpenAiClientError<"RetrieveUserRole403", RetrieveUserRole403> + | OpenAiClientError<"RetrieveUserRole404", RetrieveUserRole404> + | OpenAiClientError<"RetrieveUserRole429", RetrieveUserRole429> + > + ( + userId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveUserRole400", RetrieveUserRole400> + | OpenAiClientError<"RetrieveUserRole403", RetrieveUserRole403> + | OpenAiClientError<"RetrieveUserRole404", RetrieveUserRole404> + | OpenAiClientError<"RetrieveUserRole429", RetrieveUserRole429> + > + } + /** + * Unassigns an organization role from a user within the organization. + */ + readonly "unassignUserRole": { + ( + userId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignUserRole400", UnassignUserRole400> + | OpenAiClientError<"UnassignUserRole403", UnassignUserRole403> + | OpenAiClientError<"UnassignUserRole404", UnassignUserRole404> + | OpenAiClientError<"UnassignUserRole429", UnassignUserRole429> + > + ( + userId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignUserRole400", UnassignUserRole400> + | OpenAiClientError<"UnassignUserRole403", UnassignUserRole403> + | OpenAiClientError<"UnassignUserRole404", UnassignUserRole404> + | OpenAiClientError<"UnassignUserRole429", UnassignUserRole429> + > + } + /** + * Lists the project roles assigned to a group within a project. + */ + readonly "listProjectGroupRoleAssignments": { + ( + projectId: string, + groupId: string, + options: { + readonly params?: typeof ListProjectGroupRoleAssignmentsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectGroupRoleAssignments400", ListProjectGroupRoleAssignments400> + | OpenAiClientError<"ListProjectGroupRoleAssignments403", ListProjectGroupRoleAssignments403> + | OpenAiClientError<"ListProjectGroupRoleAssignments404", ListProjectGroupRoleAssignments404> + | OpenAiClientError<"ListProjectGroupRoleAssignments429", ListProjectGroupRoleAssignments429> + > + ( + projectId: string, + groupId: string, + options: { + readonly params?: typeof ListProjectGroupRoleAssignmentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectGroupRoleAssignments400", ListProjectGroupRoleAssignments400> + | OpenAiClientError<"ListProjectGroupRoleAssignments403", ListProjectGroupRoleAssignments403> + | OpenAiClientError<"ListProjectGroupRoleAssignments404", ListProjectGroupRoleAssignments404> + | OpenAiClientError<"ListProjectGroupRoleAssignments429", ListProjectGroupRoleAssignments429> + > + } + /** + * Assigns a project role to a group within a project. + */ + readonly "assignProjectGroupRole": { + ( + projectId: string, + groupId: string, + options: { readonly payload: typeof AssignProjectGroupRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignProjectGroupRole400", AssignProjectGroupRole400> + | OpenAiClientError<"AssignProjectGroupRole403", AssignProjectGroupRole403> + | OpenAiClientError<"AssignProjectGroupRole404", AssignProjectGroupRole404> + | OpenAiClientError<"AssignProjectGroupRole429", AssignProjectGroupRole429> + > + ( + projectId: string, + groupId: string, + options: { + readonly payload: typeof AssignProjectGroupRoleRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignProjectGroupRole400", AssignProjectGroupRole400> + | OpenAiClientError<"AssignProjectGroupRole403", AssignProjectGroupRole403> + | OpenAiClientError<"AssignProjectGroupRole404", AssignProjectGroupRole404> + | OpenAiClientError<"AssignProjectGroupRole429", AssignProjectGroupRole429> + > + } + /** + * Retrieves a project role assigned to a group. + */ + readonly "retrieveProjectGroupRole": { + ( + projectId: string, + groupId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectGroupRole400", RetrieveProjectGroupRole400> + | OpenAiClientError<"RetrieveProjectGroupRole403", RetrieveProjectGroupRole403> + | OpenAiClientError<"RetrieveProjectGroupRole404", RetrieveProjectGroupRole404> + | OpenAiClientError<"RetrieveProjectGroupRole429", RetrieveProjectGroupRole429> + > + ( + projectId: string, + groupId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectGroupRole400", RetrieveProjectGroupRole400> + | OpenAiClientError<"RetrieveProjectGroupRole403", RetrieveProjectGroupRole403> + | OpenAiClientError<"RetrieveProjectGroupRole404", RetrieveProjectGroupRole404> + | OpenAiClientError<"RetrieveProjectGroupRole429", RetrieveProjectGroupRole429> + > + } + /** + * Unassigns a project role from a group within a project. + */ + readonly "unassignProjectGroupRole": { + ( + projectId: string, + groupId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignProjectGroupRole400", UnassignProjectGroupRole400> + | OpenAiClientError<"UnassignProjectGroupRole403", UnassignProjectGroupRole403> + | OpenAiClientError<"UnassignProjectGroupRole404", UnassignProjectGroupRole404> + | OpenAiClientError<"UnassignProjectGroupRole429", UnassignProjectGroupRole429> + > + ( + projectId: string, + groupId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignProjectGroupRole400", UnassignProjectGroupRole400> + | OpenAiClientError<"UnassignProjectGroupRole403", UnassignProjectGroupRole403> + | OpenAiClientError<"UnassignProjectGroupRole404", UnassignProjectGroupRole404> + | OpenAiClientError<"UnassignProjectGroupRole429", UnassignProjectGroupRole429> + > + } + /** + * Lists the roles configured for a project. + */ + readonly "listProjectRoles": { + ( + projectId: string, + options: { readonly params?: typeof ListProjectRolesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectRoles400", ListProjectRoles400> + | OpenAiClientError<"ListProjectRoles403", ListProjectRoles403> + | OpenAiClientError<"ListProjectRoles404", ListProjectRoles404> + | OpenAiClientError<"ListProjectRoles429", ListProjectRoles429> + > + ( + projectId: string, + options: { + readonly params?: typeof ListProjectRolesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectRoles400", ListProjectRoles400> + | OpenAiClientError<"ListProjectRoles403", ListProjectRoles403> + | OpenAiClientError<"ListProjectRoles404", ListProjectRoles404> + | OpenAiClientError<"ListProjectRoles429", ListProjectRoles429> + > + } + /** + * Creates a custom role for a project. + */ + readonly "createProjectRole": { + ( + projectId: string, + options: { readonly payload: typeof CreateProjectRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectRole400", CreateProjectRole400> + | OpenAiClientError<"CreateProjectRole403", CreateProjectRole403> + | OpenAiClientError<"CreateProjectRole404", CreateProjectRole404> + | OpenAiClientError<"CreateProjectRole429", CreateProjectRole429> + > + ( + projectId: string, + options: { readonly payload: typeof CreateProjectRoleRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateProjectRole400", CreateProjectRole400> + | OpenAiClientError<"CreateProjectRole403", CreateProjectRole403> + | OpenAiClientError<"CreateProjectRole404", CreateProjectRole404> + | OpenAiClientError<"CreateProjectRole429", CreateProjectRole429> + > + } + /** + * Retrieves a project role. + */ + readonly "retrieveProjectRole": { + ( + projectId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectRole403", RetrieveProjectRole403> + | OpenAiClientError<"RetrieveProjectRole404", RetrieveProjectRole404> + | OpenAiClientError<"RetrieveProjectRole429", RetrieveProjectRole429> + > + ( + projectId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectRole403", RetrieveProjectRole403> + | OpenAiClientError<"RetrieveProjectRole404", RetrieveProjectRole404> + | OpenAiClientError<"RetrieveProjectRole429", RetrieveProjectRole429> + > + } + /** + * Updates an existing project role. + */ + readonly "updateProjectRole": { + ( + projectId: string, + roleId: string, + options: { readonly payload: typeof UpdateProjectRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectRole400", UpdateProjectRole400> + | OpenAiClientError<"UpdateProjectRole403", UpdateProjectRole403> + | OpenAiClientError<"UpdateProjectRole404", UpdateProjectRole404> + | OpenAiClientError<"UpdateProjectRole429", UpdateProjectRole429> + > + ( + projectId: string, + roleId: string, + options: { readonly payload: typeof UpdateProjectRoleRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateProjectRole400", UpdateProjectRole400> + | OpenAiClientError<"UpdateProjectRole403", UpdateProjectRole403> + | OpenAiClientError<"UpdateProjectRole404", UpdateProjectRole404> + | OpenAiClientError<"UpdateProjectRole429", UpdateProjectRole429> + > + } + /** + * Deletes a custom role from a project. + */ + readonly "deleteProjectRole": { + ( + projectId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectRole400", DeleteProjectRole400> + | OpenAiClientError<"DeleteProjectRole403", DeleteProjectRole403> + | OpenAiClientError<"DeleteProjectRole404", DeleteProjectRole404> + | OpenAiClientError<"DeleteProjectRole429", DeleteProjectRole429> + > + ( + projectId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteProjectRole400", DeleteProjectRole400> + | OpenAiClientError<"DeleteProjectRole403", DeleteProjectRole403> + | OpenAiClientError<"DeleteProjectRole404", DeleteProjectRole404> + | OpenAiClientError<"DeleteProjectRole429", DeleteProjectRole429> + > + } + /** + * Lists the project roles assigned to a user within a project. + */ + readonly "listProjectUserRoleAssignments": { + ( + projectId: string, + userId: string, + options: { + readonly params?: typeof ListProjectUserRoleAssignmentsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectUserRoleAssignments400", ListProjectUserRoleAssignments400> + | OpenAiClientError<"ListProjectUserRoleAssignments403", ListProjectUserRoleAssignments403> + | OpenAiClientError<"ListProjectUserRoleAssignments404", ListProjectUserRoleAssignments404> + | OpenAiClientError<"ListProjectUserRoleAssignments429", ListProjectUserRoleAssignments429> + > + ( + projectId: string, + userId: string, + options: { + readonly params?: typeof ListProjectUserRoleAssignmentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListProjectUserRoleAssignments400", ListProjectUserRoleAssignments400> + | OpenAiClientError<"ListProjectUserRoleAssignments403", ListProjectUserRoleAssignments403> + | OpenAiClientError<"ListProjectUserRoleAssignments404", ListProjectUserRoleAssignments404> + | OpenAiClientError<"ListProjectUserRoleAssignments429", ListProjectUserRoleAssignments429> + > + } + /** + * Assigns a project role to a user within a project. + */ + readonly "assignProjectUserRole": { + ( + projectId: string, + userId: string, + options: { readonly payload: typeof AssignProjectUserRoleRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignProjectUserRole400", AssignProjectUserRole400> + | OpenAiClientError<"AssignProjectUserRole403", AssignProjectUserRole403> + | OpenAiClientError<"AssignProjectUserRole404", AssignProjectUserRole404> + | OpenAiClientError<"AssignProjectUserRole429", AssignProjectUserRole429> + > + ( + projectId: string, + userId: string, + options: { + readonly payload: typeof AssignProjectUserRoleRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AssignProjectUserRole400", AssignProjectUserRole400> + | OpenAiClientError<"AssignProjectUserRole403", AssignProjectUserRole403> + | OpenAiClientError<"AssignProjectUserRole404", AssignProjectUserRole404> + | OpenAiClientError<"AssignProjectUserRole429", AssignProjectUserRole429> + > + } + /** + * Retrieves a project role assigned to a user. + */ + readonly "retrieveProjectUserRole": { + ( + projectId: string, + userId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectUserRole400", RetrieveProjectUserRole400> + | OpenAiClientError<"RetrieveProjectUserRole403", RetrieveProjectUserRole403> + | OpenAiClientError<"RetrieveProjectUserRole404", RetrieveProjectUserRole404> + | OpenAiClientError<"RetrieveProjectUserRole429", RetrieveProjectUserRole429> + > + ( + projectId: string, + userId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveProjectUserRole400", RetrieveProjectUserRole400> + | OpenAiClientError<"RetrieveProjectUserRole403", RetrieveProjectUserRole403> + | OpenAiClientError<"RetrieveProjectUserRole404", RetrieveProjectUserRole404> + | OpenAiClientError<"RetrieveProjectUserRole429", RetrieveProjectUserRole429> + > + } + /** + * Unassigns a project role from a user within a project. + */ + readonly "unassignProjectUserRole": { + ( + projectId: string, + userId: string, + roleId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignProjectUserRole400", UnassignProjectUserRole400> + | OpenAiClientError<"UnassignProjectUserRole403", UnassignProjectUserRole403> + | OpenAiClientError<"UnassignProjectUserRole404", UnassignProjectUserRole404> + | OpenAiClientError<"UnassignProjectUserRole429", UnassignProjectUserRole429> + > + ( + projectId: string, + userId: string, + roleId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UnassignProjectUserRole400", UnassignProjectUserRole400> + | OpenAiClientError<"UnassignProjectUserRole403", UnassignProjectUserRole403> + | OpenAiClientError<"UnassignProjectUserRole404", UnassignProjectUserRole404> + | OpenAiClientError<"UnassignProjectUserRole429", UnassignProjectUserRole429> + > + } + /** + * Create a new Realtime API call over WebRTC and receive the SDP answer needed + * to complete the peer connection. + */ + readonly "createRealtimeCall": { + ( + options: { readonly payload: typeof CreateRealtimeCallRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect, HttpClientError.HttpClientError | SchemaError> + ( + options: { + readonly payload: typeof CreateRealtimeCallRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect, HttpClientError.HttpClientError | SchemaError> + } + /** + * Accept an incoming SIP call and configure the realtime session that will + * handle it. + */ + readonly "acceptRealtimeCall": { + ( + callId: string, + options: { readonly payload: typeof AcceptRealtimeCallRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AcceptRealtimeCall404", AcceptRealtimeCall404> + | OpenAiClientError<"AcceptRealtimeCall409", AcceptRealtimeCall409> + > + ( + callId: string, + options: { readonly payload: typeof AcceptRealtimeCallRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AcceptRealtimeCall404", AcceptRealtimeCall404> + | OpenAiClientError<"AcceptRealtimeCall409", AcceptRealtimeCall409> + > + } + /** + * End an active Realtime API call, whether it was initiated over SIP or + * WebRTC. + */ + readonly "hangupRealtimeCall": { + ( + callId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"HangupRealtimeCall404", HangupRealtimeCall404> + > + ( + callId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"HangupRealtimeCall404", HangupRealtimeCall404> + > + } + /** + * Transfer an active SIP call to a new destination using the SIP REFER verb. + */ + readonly "referRealtimeCall": { + ( + callId: string, + options: { readonly payload: typeof ReferRealtimeCallRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ReferRealtimeCall404", ReferRealtimeCall404> + > + ( + callId: string, + options: { readonly payload: typeof ReferRealtimeCallRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ReferRealtimeCall404", ReferRealtimeCall404> + > + } + /** + * Decline an incoming SIP call by returning a SIP status code to the caller. + */ + readonly "rejectRealtimeCall": { + ( + callId: string, + options: { readonly payload: typeof RejectRealtimeCallRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RejectRealtimeCall404", RejectRealtimeCall404> + | OpenAiClientError<"RejectRealtimeCall409", RejectRealtimeCall409> + > + ( + callId: string, + options: { readonly payload: typeof RejectRealtimeCallRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RejectRealtimeCall404", RejectRealtimeCall404> + | OpenAiClientError<"RejectRealtimeCall409", RejectRealtimeCall409> + > + } + /** + * Create a Realtime client secret with an associated session configuration. + * + * Client secrets are short-lived tokens that can be passed to a client app, + * such as a web frontend or mobile client, which grants access to the Realtime API without + * leaking your main API key. You can configure a custom TTL for each client secret. + * + * You can also attach session configuration options to the client secret, which will be + * applied to any sessions created using that client secret, but these can also be overridden + * by the client connection. + * + * [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc). + * + * Returns the created client secret and the effective session object. The client secret is a string that looks like `ek_1234`. + */ + readonly "createRealtimeClientSecret": { + ( + options: { readonly payload: typeof CreateRealtimeClientSecretRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateRealtimeClientSecret400", CreateRealtimeClientSecret400> + | OpenAiClientError<"CreateRealtimeClientSecret404", CreateRealtimeClientSecret404> + | OpenAiClientError<"CreateRealtimeClientSecret500", CreateRealtimeClientSecret500> + > + ( + options: { + readonly payload: typeof CreateRealtimeClientSecretRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateRealtimeClientSecret400", CreateRealtimeClientSecret400> + | OpenAiClientError<"CreateRealtimeClientSecret404", CreateRealtimeClientSecret404> + | OpenAiClientError<"CreateRealtimeClientSecret500", CreateRealtimeClientSecret500> + > + } + /** + * Create an ephemeral API token for use in client-side applications with the + * Realtime API. Can be configured with the same session parameters as the + * `session.update` client event. + * + * It responds with a session object, plus a `client_secret` key which contains + * a usable ephemeral API token that can be used to authenticate browser clients + * for the Realtime API. + * + * Returns the created Realtime session object, plus an ephemeral key. + */ + readonly "createRealtimeSession": { + ( + options: { readonly payload: typeof CreateRealtimeSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError + > + ( + options: { + readonly payload: typeof CreateRealtimeSessionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError + > + } + /** + * Create an ephemeral API token for use in client-side applications with the + * Realtime API specifically for realtime transcriptions. + * Can be configured with the same session parameters as the `transcription_session.update` client event. + * + * It responds with a session object, plus a `client_secret` key which contains + * a usable ephemeral API token that can be used to authenticate browser clients + * for the Realtime API. + * + * Returns the created Realtime transcription session object, plus an ephemeral key. + */ + readonly "createRealtimeTranscriptionSession": { + ( + options: { + readonly payload: typeof CreateRealtimeTranscriptionSessionRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError + > + ( + options: { + readonly payload: typeof CreateRealtimeTranscriptionSessionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError + > + } + /** + * Create a Realtime translation client secret with an associated translation session configuration. + * + * Client secrets are short-lived tokens that can be passed to a client app, + * such as a web frontend or mobile client, which grants access to the Realtime + * Translation API without leaking your main API key. You can configure a custom + * TTL for each client secret. + * + * Returns the created client secret and the effective translation session object. + * The client secret is a string that looks like `ek_1234`. + */ + readonly "createRealtimeTranslationClientSecret": { + ( + options: { + readonly payload: typeof CreateRealtimeTranslationClientSecretRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateRealtimeTranslationClientSecret400", CreateRealtimeTranslationClientSecret400> + | OpenAiClientError<"CreateRealtimeTranslationClientSecret404", CreateRealtimeTranslationClientSecret404> + | OpenAiClientError<"CreateRealtimeTranslationClientSecret500", CreateRealtimeTranslationClientSecret500> + > + ( + options: { + readonly payload: typeof CreateRealtimeTranslationClientSecretRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateRealtimeTranslationClientSecret400", CreateRealtimeTranslationClientSecret400> + | OpenAiClientError<"CreateRealtimeTranslationClientSecret404", CreateRealtimeTranslationClientSecret404> + | OpenAiClientError<"CreateRealtimeTranslationClientSecret500", CreateRealtimeTranslationClientSecret500> + > + } + /** + * Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or + * [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text) + * or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call + * your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in + * [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + * or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data + * as input for the model's response. + */ + readonly "createResponse": { + ( + options: { readonly payload: typeof CreateResponseRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateResponse400", CreateResponse400> + | OpenAiClientError<"CreateResponse404", CreateResponse404> + | OpenAiClientError<"CreateResponse429", CreateResponse429> + | OpenAiClientError<"CreateResponse503", CreateResponse503> + > + ( + options: { readonly payload: typeof CreateResponseRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateResponse400", CreateResponse400> + | OpenAiClientError<"CreateResponse404", CreateResponse404> + | OpenAiClientError<"CreateResponse429", CreateResponse429> + | OpenAiClientError<"CreateResponse503", CreateResponse503> + > + } + /** + * Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or + * [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text) + * or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call + * your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in + * [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + * or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data + * as input for the model's response. + */ + readonly "createResponseSse": ( + options: { readonly payload: typeof CreateResponseRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateResponse200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateResponse200Sse.DecodingServices + > + /** + * Retrieves a model response with the given ID. + */ + readonly "getResponse": { + ( + responseId: string, + options: { readonly params?: typeof GetResponseParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetResponse404", GetResponse404> + | OpenAiClientError<"GetResponse429", GetResponse429> + > + ( + responseId: string, + options: + | { readonly params?: typeof GetResponseParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetResponse404", GetResponse404> + | OpenAiClientError<"GetResponse429", GetResponse429> + > + } + /** + * Deletes a model response with the given ID. + */ + readonly "deleteResponse": { + ( + responseId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteResponse404", DeleteResponse404> + | OpenAiClientError<"DeleteResponse429", DeleteResponse429> + > + ( + responseId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteResponse404", DeleteResponse404> + | OpenAiClientError<"DeleteResponse429", DeleteResponse429> + > + } + /** + * Cancels a model response with the given ID. Only responses created with + * the `background` parameter set to `true` can be cancelled. + * [Learn more](https://developers.openai.com/api/docs/guides/background). + */ + readonly "cancelResponse": { + ( + responseId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelResponse404", CancelResponse404> + | OpenAiClientError<"CancelResponse429", CancelResponse429> + > + ( + responseId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelResponse404", CancelResponse404> + | OpenAiClientError<"CancelResponse429", CancelResponse429> + > + } + /** + * Returns a list of input items for a given response. + */ + readonly "listInputItems": { + ( + responseId: string, + options: { readonly params?: typeof ListInputItemsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListInputItems404", ListInputItems404> + | OpenAiClientError<"ListInputItems429", ListInputItems429> + > + ( + responseId: string, + options: { + readonly params?: typeof ListInputItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListInputItems404", ListInputItems404> + | OpenAiClientError<"ListInputItems429", ListInputItems429> + > + } + /** + * Create a thread. + */ + readonly "createThread": { + ( + options: { readonly payload: typeof CreateThreadRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateThread429", CreateThread429> + > + ( + options: { readonly payload: typeof CreateThreadRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateThread429", CreateThread429> + > + } + /** + * Create a thread and run it in one request. + */ + readonly "createThreadAndRun": { + ( + options: { readonly payload: typeof CreateThreadAndRunRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateThreadAndRun429", CreateThreadAndRun429> + > + ( + options: { readonly payload: typeof CreateThreadAndRunRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateThreadAndRun429", CreateThreadAndRun429> + > + } + /** + * Create a thread and run it in one request. + */ + readonly "createThreadAndRunSse": ( + options: { readonly payload: typeof CreateThreadAndRunRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateThreadAndRun200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateThreadAndRun200Sse.DecodingServices + > + /** + * Retrieves a thread. + */ + readonly "getThread": { + ( + threadId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetThread429", GetThread429> + > + ( + threadId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetThread429", GetThread429> + > + } + /** + * Modifies a thread. + */ + readonly "modifyThread": { + ( + threadId: string, + options: { readonly payload: typeof ModifyThreadRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyThread429", ModifyThread429> + > + ( + threadId: string, + options: { readonly payload: typeof ModifyThreadRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyThread429", ModifyThread429> + > + } + /** + * Delete a thread. + */ + readonly "deleteThread": { + ( + threadId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"DeleteThread429", DeleteThread429> + > + ( + threadId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"DeleteThread429", DeleteThread429> + > + } + /** + * Returns a list of messages for a given thread. + */ + readonly "listMessages": { + ( + threadId: string, + options: { readonly params?: typeof ListMessagesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListMessages429", ListMessages429> + > + ( + threadId: string, + options: + | { readonly params?: typeof ListMessagesParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListMessages429", ListMessages429> + > + } + /** + * Create a message. + */ + readonly "createMessage": { + ( + threadId: string, + options: { readonly payload: typeof CreateMessageRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateMessage429", CreateMessage429> + > + ( + threadId: string, + options: { readonly payload: typeof CreateMessageRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateMessage429", CreateMessage429> + > + } + /** + * Retrieve a message. + */ + readonly "getMessage": { + ( + threadId: string, + messageId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetMessage429", GetMessage429> + > + ( + threadId: string, + messageId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetMessage429", GetMessage429> + > + } + /** + * Modifies a message. + */ + readonly "modifyMessage": { + ( + threadId: string, + messageId: string, + options: { readonly payload: typeof ModifyMessageRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyMessage429", ModifyMessage429> + > + ( + threadId: string, + messageId: string, + options: { readonly payload: typeof ModifyMessageRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyMessage429", ModifyMessage429> + > + } + /** + * Deletes a message. + */ + readonly "deleteMessage": { + ( + threadId: string, + messageId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"DeleteMessage429", DeleteMessage429> + > + ( + threadId: string, + messageId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"DeleteMessage429", DeleteMessage429> + > + } + /** + * Returns a list of runs belonging to a thread. + */ + readonly "listRuns": { + ( + threadId: string, + options: { readonly params?: typeof ListRunsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListRuns429", ListRuns429> + > + ( + threadId: string, + options: + | { readonly params?: typeof ListRunsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListRuns429", ListRuns429> + > + } + /** + * Create a run. + */ + readonly "createRun": { + ( + threadId: string, + options: { + readonly params?: typeof CreateRunParams.Encoded | undefined + readonly payload: typeof CreateRunRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateRun429", CreateRun429> + > + ( + threadId: string, + options: { + readonly params?: typeof CreateRunParams.Encoded | undefined + readonly payload: typeof CreateRunRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CreateRun429", CreateRun429> + > + } + /** + * Create a run. + */ + readonly "createRunSse": ( + threadId: string, + options: { + readonly params?: typeof CreateRunParams.Encoded | undefined + readonly payload: typeof CreateRunRequestJson.Encoded + } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateRun200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateRun200Sse.DecodingServices + > + /** + * Retrieves a run. + */ + readonly "getRun": { + ( + threadId: string, + runId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetRun429", GetRun429> + > + ( + threadId: string, + runId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetRun429", GetRun429> + > + } + /** + * Modifies a run. + */ + readonly "modifyRun": { + ( + threadId: string, + runId: string, + options: { readonly payload: typeof ModifyRunRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyRun429", ModifyRun429> + > + ( + threadId: string, + runId: string, + options: { readonly payload: typeof ModifyRunRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ModifyRun429", ModifyRun429> + > + } + /** + * Cancels a run that is `in_progress`. + */ + readonly "cancelRun": { + ( + threadId: string, + runId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CancelRun429", CancelRun429> + > + ( + threadId: string, + runId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"CancelRun429", CancelRun429> + > + } + /** + * Returns a list of run steps belonging to a run. + */ + readonly "listRunSteps": { + ( + threadId: string, + runId: string, + options: { readonly params?: typeof ListRunStepsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListRunSteps429", ListRunSteps429> + > + ( + threadId: string, + runId: string, + options: + | { readonly params?: typeof ListRunStepsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"ListRunSteps429", ListRunSteps429> + > + } + /** + * Retrieves a run step. + */ + readonly "getRunStep": { + ( + threadId: string, + runId: string, + stepId: string, + options: { readonly params?: typeof GetRunStepParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetRunStep429", GetRunStep429> + > + ( + threadId: string, + runId: string, + stepId: string, + options: + | { readonly params?: typeof GetRunStepParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError | OpenAiClientError<"GetRunStep429", GetRunStep429> + > + } + /** + * When a run has the `status: "requires_action"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request. + */ + readonly "submitToolOuputsToRun": { + ( + threadId: string, + runId: string, + options: { readonly payload: typeof SubmitToolOuputsToRunRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"SubmitToolOuputsToRun429", SubmitToolOuputsToRun429> + > + ( + threadId: string, + runId: string, + options: { + readonly payload: typeof SubmitToolOuputsToRunRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"SubmitToolOuputsToRun429", SubmitToolOuputsToRun429> + > + } + /** + * When a run has the `status: "requires_action"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request. + */ + readonly "submitToolOuputsToRunSse": ( + threadId: string, + runId: string, + options: { readonly payload: typeof SubmitToolOuputsToRunRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: SubmitToolOuputsToRun200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof SubmitToolOuputsToRun200Sse.DecodingServices + > + /** + * Creates an intermediate [Upload](https://developers.openai.com/api/reference/resources/uploads) object + * that you can add [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to. + * Currently, an Upload can accept at most 8 GB in total and expires after an + * hour after you create it. + * + * Once you complete the Upload, we will create a + * [File](https://developers.openai.com/api/reference/resources/files) object that contains all the parts + * you uploaded. This File is usable in the rest of our platform as a regular + * File object. + * + * For certain `purpose` values, the correct `mime_type` must be specified. + * Please refer to documentation for the + * [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files). + * + * For guidance on the proper filename extensions for each purpose, please + * follow the documentation on [creating a + * File](https://developers.openai.com/api/reference/resources/files/methods/create). + * + * Returns the Upload object with status `pending`. + */ + readonly "createUpload": { + ( + options: { readonly payload: typeof CreateUploadRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateUpload400", CreateUpload400> + | OpenAiClientError<"CreateUpload429", CreateUpload429> + > + ( + options: { readonly payload: typeof CreateUploadRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateUpload400", CreateUpload400> + | OpenAiClientError<"CreateUpload429", CreateUpload429> + > + } + /** + * Cancels the Upload. No Parts may be added after an Upload is cancelled. + * + * Returns the Upload object with status `cancelled`. + */ + readonly "cancelUpload": { + ( + uploadId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelUpload400", CancelUpload400> + | OpenAiClientError<"CancelUpload404", CancelUpload404> + | OpenAiClientError<"CancelUpload410", CancelUpload410> + | OpenAiClientError<"CancelUpload429", CancelUpload429> + > + ( + uploadId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelUpload400", CancelUpload400> + | OpenAiClientError<"CancelUpload404", CancelUpload404> + | OpenAiClientError<"CancelUpload410", CancelUpload410> + | OpenAiClientError<"CancelUpload429", CancelUpload429> + > + } + /** + * Completes the [Upload](https://developers.openai.com/api/reference/resources/uploads). + * + * Within the returned Upload object, there is a nested [File](https://developers.openai.com/api/reference/resources/files) object that is ready to use in the rest of the platform. + * + * You can specify the order of the Parts by passing in an ordered list of the Part IDs. + * + * The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed. + * Returns the Upload object with status `completed`, including an additional `file` property containing the created usable File object. + */ + readonly "completeUpload": { + ( + uploadId: string, + options: { readonly payload: typeof CompleteUploadRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CompleteUpload400", CompleteUpload400> + | OpenAiClientError<"CompleteUpload404", CompleteUpload404> + | OpenAiClientError<"CompleteUpload410", CompleteUpload410> + | OpenAiClientError<"CompleteUpload429", CompleteUpload429> + | OpenAiClientError<"CompleteUpload500", CompleteUpload500> + > + ( + uploadId: string, + options: { readonly payload: typeof CompleteUploadRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CompleteUpload400", CompleteUpload400> + | OpenAiClientError<"CompleteUpload404", CompleteUpload404> + | OpenAiClientError<"CompleteUpload410", CompleteUpload410> + | OpenAiClientError<"CompleteUpload429", CompleteUpload429> + | OpenAiClientError<"CompleteUpload500", CompleteUpload500> + > + } + /** + * Adds a [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to an [Upload](https://developers.openai.com/api/reference/resources/uploads) object. A Part represents a chunk of bytes from the file you are trying to upload. + * + * Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB. + * + * It is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete). + */ + readonly "addUploadPart": { + ( + uploadId: string, + options: { readonly payload: typeof AddUploadPartRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AddUploadPart400", AddUploadPart400> + | OpenAiClientError<"AddUploadPart404", AddUploadPart404> + | OpenAiClientError<"AddUploadPart410", AddUploadPart410> + | OpenAiClientError<"AddUploadPart429", AddUploadPart429> + | OpenAiClientError<"AddUploadPart500", AddUploadPart500> + > + ( + uploadId: string, + options: { readonly payload: typeof AddUploadPartRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"AddUploadPart400", AddUploadPart400> + | OpenAiClientError<"AddUploadPart404", AddUploadPart404> + | OpenAiClientError<"AddUploadPart410", AddUploadPart410> + | OpenAiClientError<"AddUploadPart429", AddUploadPart429> + | OpenAiClientError<"AddUploadPart500", AddUploadPart500> + > + } + /** + * Returns a list of vector stores. + */ + readonly "listVectorStores": { + ( + options: { readonly params?: typeof ListVectorStoresParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVectorStores429", ListVectorStores429> + | OpenAiClientError<"ListVectorStores503", ListVectorStores503> + > + ( + options: { + readonly params?: typeof ListVectorStoresParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVectorStores429", ListVectorStores429> + | OpenAiClientError<"ListVectorStores503", ListVectorStores503> + > + } + /** + * Create a vector store. + */ + readonly "createVectorStore": { + ( + options: { readonly payload: typeof CreateVectorStoreRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVectorStore400", CreateVectorStore400> + | OpenAiClientError<"CreateVectorStore404", CreateVectorStore404> + | OpenAiClientError<"CreateVectorStore429", CreateVectorStore429> + > + ( + options: { readonly payload: typeof CreateVectorStoreRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVectorStore400", CreateVectorStore400> + | OpenAiClientError<"CreateVectorStore404", CreateVectorStore404> + | OpenAiClientError<"CreateVectorStore429", CreateVectorStore429> + > + } + /** + * Retrieves a vector store. + */ + readonly "getVectorStore": { + ( + vectorStoreId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVectorStore404", GetVectorStore404> + | OpenAiClientError<"GetVectorStore429", GetVectorStore429> + > + ( + vectorStoreId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVectorStore404", GetVectorStore404> + | OpenAiClientError<"GetVectorStore429", GetVectorStore429> + > + } + /** + * Modifies a vector store. + */ + readonly "modifyVectorStore": { + ( + vectorStoreId: string, + options: { readonly payload: typeof ModifyVectorStoreRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyVectorStore400", ModifyVectorStore400> + | OpenAiClientError<"ModifyVectorStore403", ModifyVectorStore403> + | OpenAiClientError<"ModifyVectorStore404", ModifyVectorStore404> + | OpenAiClientError<"ModifyVectorStore429", ModifyVectorStore429> + > + ( + vectorStoreId: string, + options: { readonly payload: typeof ModifyVectorStoreRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ModifyVectorStore400", ModifyVectorStore400> + | OpenAiClientError<"ModifyVectorStore403", ModifyVectorStore403> + | OpenAiClientError<"ModifyVectorStore404", ModifyVectorStore404> + | OpenAiClientError<"ModifyVectorStore429", ModifyVectorStore429> + > + } + /** + * Delete a vector store. + */ + readonly "deleteVectorStore": { + ( + vectorStoreId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVectorStore404", DeleteVectorStore404> + | OpenAiClientError<"DeleteVectorStore409", DeleteVectorStore409> + | OpenAiClientError<"DeleteVectorStore429", DeleteVectorStore429> + > + ( + vectorStoreId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVectorStore404", DeleteVectorStore404> + | OpenAiClientError<"DeleteVectorStore409", DeleteVectorStore409> + | OpenAiClientError<"DeleteVectorStore429", DeleteVectorStore429> + > + } + /** + * Create a vector store file batch. + */ + readonly "createVectorStoreFileBatch": { + ( + vectorStoreId: string, + options: { readonly payload: typeof CreateVectorStoreFileBatchRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVectorStoreFileBatch400", CreateVectorStoreFileBatch400> + | OpenAiClientError<"CreateVectorStoreFileBatch404", CreateVectorStoreFileBatch404> + | OpenAiClientError<"CreateVectorStoreFileBatch409", CreateVectorStoreFileBatch409> + | OpenAiClientError<"CreateVectorStoreFileBatch429", CreateVectorStoreFileBatch429> + | OpenAiClientError<"CreateVectorStoreFileBatch500", CreateVectorStoreFileBatch500> + > + ( + vectorStoreId: string, + options: { + readonly payload: typeof CreateVectorStoreFileBatchRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVectorStoreFileBatch400", CreateVectorStoreFileBatch400> + | OpenAiClientError<"CreateVectorStoreFileBatch404", CreateVectorStoreFileBatch404> + | OpenAiClientError<"CreateVectorStoreFileBatch409", CreateVectorStoreFileBatch409> + | OpenAiClientError<"CreateVectorStoreFileBatch429", CreateVectorStoreFileBatch429> + | OpenAiClientError<"CreateVectorStoreFileBatch500", CreateVectorStoreFileBatch500> + > + } + /** + * Retrieves a vector store file batch. + */ + readonly "getVectorStoreFileBatch": { + ( + vectorStoreId: string, + batchId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVectorStoreFileBatch400", GetVectorStoreFileBatch400> + | OpenAiClientError<"GetVectorStoreFileBatch404", GetVectorStoreFileBatch404> + | OpenAiClientError<"GetVectorStoreFileBatch429", GetVectorStoreFileBatch429> + > + ( + vectorStoreId: string, + batchId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVectorStoreFileBatch400", GetVectorStoreFileBatch400> + | OpenAiClientError<"GetVectorStoreFileBatch404", GetVectorStoreFileBatch404> + | OpenAiClientError<"GetVectorStoreFileBatch429", GetVectorStoreFileBatch429> + > + } + /** + * Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + */ + readonly "cancelVectorStoreFileBatch": { + ( + vectorStoreId: string, + batchId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelVectorStoreFileBatch400", CancelVectorStoreFileBatch400> + | OpenAiClientError<"CancelVectorStoreFileBatch404", CancelVectorStoreFileBatch404> + | OpenAiClientError<"CancelVectorStoreFileBatch409", CancelVectorStoreFileBatch409> + | OpenAiClientError<"CancelVectorStoreFileBatch429", CancelVectorStoreFileBatch429> + > + ( + vectorStoreId: string, + batchId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelVectorStoreFileBatch400", CancelVectorStoreFileBatch400> + | OpenAiClientError<"CancelVectorStoreFileBatch404", CancelVectorStoreFileBatch404> + | OpenAiClientError<"CancelVectorStoreFileBatch409", CancelVectorStoreFileBatch409> + | OpenAiClientError<"CancelVectorStoreFileBatch429", CancelVectorStoreFileBatch429> + > + } + /** + * Returns a list of vector store files in a batch. + */ + readonly "listFilesInVectorStoreBatch": { + ( + vectorStoreId: string, + batchId: string, + options: { + readonly params?: typeof ListFilesInVectorStoreBatchParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFilesInVectorStoreBatch400", ListFilesInVectorStoreBatch400> + | OpenAiClientError<"ListFilesInVectorStoreBatch404", ListFilesInVectorStoreBatch404> + | OpenAiClientError<"ListFilesInVectorStoreBatch429", ListFilesInVectorStoreBatch429> + > + ( + vectorStoreId: string, + batchId: string, + options: { + readonly params?: typeof ListFilesInVectorStoreBatchParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListFilesInVectorStoreBatch400", ListFilesInVectorStoreBatch400> + | OpenAiClientError<"ListFilesInVectorStoreBatch404", ListFilesInVectorStoreBatch404> + | OpenAiClientError<"ListFilesInVectorStoreBatch429", ListFilesInVectorStoreBatch429> + > + } + /** + * Returns a list of vector store files. + */ + readonly "listVectorStoreFiles": { + ( + vectorStoreId: string, + options: { readonly params?: typeof ListVectorStoreFilesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVectorStoreFiles400", ListVectorStoreFiles400> + | OpenAiClientError<"ListVectorStoreFiles404", ListVectorStoreFiles404> + | OpenAiClientError<"ListVectorStoreFiles429", ListVectorStoreFiles429> + > + ( + vectorStoreId: string, + options: { + readonly params?: typeof ListVectorStoreFilesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVectorStoreFiles400", ListVectorStoreFiles400> + | OpenAiClientError<"ListVectorStoreFiles404", ListVectorStoreFiles404> + | OpenAiClientError<"ListVectorStoreFiles429", ListVectorStoreFiles429> + > + } + /** + * Create a vector store file by attaching a [File](https://developers.openai.com/api/reference/resources/files) to a [vector store](https://developers.openai.com/api/reference/resources/vector_stores). + */ + readonly "createVectorStoreFile": { + ( + vectorStoreId: string, + options: { readonly payload: typeof CreateVectorStoreFileRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVectorStoreFile400", CreateVectorStoreFile400> + | OpenAiClientError<"CreateVectorStoreFile404", CreateVectorStoreFile404> + | OpenAiClientError<"CreateVectorStoreFile409", CreateVectorStoreFile409> + | OpenAiClientError<"CreateVectorStoreFile429", CreateVectorStoreFile429> + | OpenAiClientError<"CreateVectorStoreFile500", CreateVectorStoreFile500> + > + ( + vectorStoreId: string, + options: { + readonly payload: typeof CreateVectorStoreFileRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVectorStoreFile400", CreateVectorStoreFile400> + | OpenAiClientError<"CreateVectorStoreFile404", CreateVectorStoreFile404> + | OpenAiClientError<"CreateVectorStoreFile409", CreateVectorStoreFile409> + | OpenAiClientError<"CreateVectorStoreFile429", CreateVectorStoreFile429> + | OpenAiClientError<"CreateVectorStoreFile500", CreateVectorStoreFile500> + > + } + /** + * Retrieves a vector store file. + */ + readonly "getVectorStoreFile": { + ( + vectorStoreId: string, + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVectorStoreFile404", GetVectorStoreFile404> + | OpenAiClientError<"GetVectorStoreFile429", GetVectorStoreFile429> + | OpenAiClientError<"GetVectorStoreFile500", GetVectorStoreFile500> + > + ( + vectorStoreId: string, + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVectorStoreFile404", GetVectorStoreFile404> + | OpenAiClientError<"GetVectorStoreFile429", GetVectorStoreFile429> + | OpenAiClientError<"GetVectorStoreFile500", GetVectorStoreFile500> + > + } + /** + * Update attributes on a vector store file. + */ + readonly "updateVectorStoreFileAttributes": { + ( + vectorStoreId: string, + fileId: string, + options: { readonly payload: typeof UpdateVectorStoreFileAttributesRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateVectorStoreFileAttributes400", UpdateVectorStoreFileAttributes400> + | OpenAiClientError<"UpdateVectorStoreFileAttributes404", UpdateVectorStoreFileAttributes404> + | OpenAiClientError<"UpdateVectorStoreFileAttributes409", UpdateVectorStoreFileAttributes409> + | OpenAiClientError<"UpdateVectorStoreFileAttributes429", UpdateVectorStoreFileAttributes429> + | OpenAiClientError<"UpdateVectorStoreFileAttributes500", UpdateVectorStoreFileAttributes500> + > + ( + vectorStoreId: string, + fileId: string, + options: { + readonly payload: typeof UpdateVectorStoreFileAttributesRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateVectorStoreFileAttributes400", UpdateVectorStoreFileAttributes400> + | OpenAiClientError<"UpdateVectorStoreFileAttributes404", UpdateVectorStoreFileAttributes404> + | OpenAiClientError<"UpdateVectorStoreFileAttributes409", UpdateVectorStoreFileAttributes409> + | OpenAiClientError<"UpdateVectorStoreFileAttributes429", UpdateVectorStoreFileAttributes429> + | OpenAiClientError<"UpdateVectorStoreFileAttributes500", UpdateVectorStoreFileAttributes500> + > + } + /** + * Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete) endpoint. + */ + readonly "deleteVectorStoreFile": { + ( + vectorStoreId: string, + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVectorStoreFile400", DeleteVectorStoreFile400> + | OpenAiClientError<"DeleteVectorStoreFile404", DeleteVectorStoreFile404> + | OpenAiClientError<"DeleteVectorStoreFile409", DeleteVectorStoreFile409> + | OpenAiClientError<"DeleteVectorStoreFile429", DeleteVectorStoreFile429> + | OpenAiClientError<"DeleteVectorStoreFile500", DeleteVectorStoreFile500> + > + ( + vectorStoreId: string, + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVectorStoreFile400", DeleteVectorStoreFile400> + | OpenAiClientError<"DeleteVectorStoreFile404", DeleteVectorStoreFile404> + | OpenAiClientError<"DeleteVectorStoreFile409", DeleteVectorStoreFile409> + | OpenAiClientError<"DeleteVectorStoreFile429", DeleteVectorStoreFile429> + | OpenAiClientError<"DeleteVectorStoreFile500", DeleteVectorStoreFile500> + > + } + /** + * Retrieve the parsed contents of a vector store file. + */ + readonly "retrieveVectorStoreFileContent": { + ( + vectorStoreId: string, + fileId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVectorStoreFileContent400", RetrieveVectorStoreFileContent400> + | OpenAiClientError<"RetrieveVectorStoreFileContent404", RetrieveVectorStoreFileContent404> + | OpenAiClientError<"RetrieveVectorStoreFileContent429", RetrieveVectorStoreFileContent429> + | OpenAiClientError<"RetrieveVectorStoreFileContent500", RetrieveVectorStoreFileContent500> + > + ( + vectorStoreId: string, + fileId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVectorStoreFileContent400", RetrieveVectorStoreFileContent400> + | OpenAiClientError<"RetrieveVectorStoreFileContent404", RetrieveVectorStoreFileContent404> + | OpenAiClientError<"RetrieveVectorStoreFileContent429", RetrieveVectorStoreFileContent429> + | OpenAiClientError<"RetrieveVectorStoreFileContent500", RetrieveVectorStoreFileContent500> + > + } + /** + * Search a vector store for relevant chunks based on a query and file attributes filter. + */ + readonly "searchVectorStore": { + ( + vectorStoreId: string, + options: { readonly payload: typeof SearchVectorStoreRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"SearchVectorStore400", SearchVectorStore400> + | OpenAiClientError<"SearchVectorStore404", SearchVectorStore404> + | OpenAiClientError<"SearchVectorStore429", SearchVectorStore429> + > + ( + vectorStoreId: string, + options: { readonly payload: typeof SearchVectorStoreRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"SearchVectorStore400", SearchVectorStore400> + | OpenAiClientError<"SearchVectorStore404", SearchVectorStore404> + | OpenAiClientError<"SearchVectorStore429", SearchVectorStore429> + > + } + /** + * Create a conversation. + */ + readonly "createConversation": { + ( + options: { readonly payload: typeof CreateConversationRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateConversation400", CreateConversation400> + | OpenAiClientError<"CreateConversation429", CreateConversation429> + | OpenAiClientError<"CreateConversation500", CreateConversation500> + > + ( + options: { readonly payload: typeof CreateConversationRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateConversation400", CreateConversation400> + | OpenAiClientError<"CreateConversation429", CreateConversation429> + | OpenAiClientError<"CreateConversation500", CreateConversation500> + > + } + /** + * Get a conversation + */ + readonly "getConversation": { + ( + conversationId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetConversation404", GetConversation404> + | OpenAiClientError<"GetConversation429", GetConversation429> + > + ( + conversationId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetConversation404", GetConversation404> + | OpenAiClientError<"GetConversation429", GetConversation429> + > + } + /** + * Update a conversation + */ + readonly "updateConversation": { + ( + conversationId: string, + options: { readonly payload: typeof UpdateConversationRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateConversation400", UpdateConversation400> + | OpenAiClientError<"UpdateConversation404", UpdateConversation404> + | OpenAiClientError<"UpdateConversation429", UpdateConversation429> + | OpenAiClientError<"UpdateConversation500", UpdateConversation500> + > + ( + conversationId: string, + options: { readonly payload: typeof UpdateConversationRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateConversation400", UpdateConversation400> + | OpenAiClientError<"UpdateConversation404", UpdateConversation404> + | OpenAiClientError<"UpdateConversation429", UpdateConversation429> + | OpenAiClientError<"UpdateConversation500", UpdateConversation500> + > + } + /** + * Delete a conversation. Items in the conversation will not be deleted. + */ + readonly "deleteConversation": { + ( + conversationId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteConversation404", DeleteConversation404> + | OpenAiClientError<"DeleteConversation429", DeleteConversation429> + > + ( + conversationId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteConversation404", DeleteConversation404> + | OpenAiClientError<"DeleteConversation429", DeleteConversation429> + > + } + /** + * Get a safety case by ID. + */ + readonly "Getsafetycase": { + ( + id: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getsafetycase400", Getsafetycase400> + | OpenAiClientError<"Getsafetycase404", Getsafetycase404> + | OpenAiClientError<"Getsafetycase429", Getsafetycase429> + | OpenAiClientError<"Getsafetycase500", Getsafetycase500> + | OpenAiClientError<"Getsafetycase503", Getsafetycase503> + > + ( + id: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getsafetycase400", Getsafetycase400> + | OpenAiClientError<"Getsafetycase404", Getsafetycase404> + | OpenAiClientError<"Getsafetycase429", Getsafetycase429> + | OpenAiClientError<"Getsafetycase500", Getsafetycase500> + | OpenAiClientError<"Getsafetycase503", Getsafetycase503> + > + } + /** + * Get a safety alert belonging to the authenticated API project. + */ + readonly "Getprojectsafetyalert": { + ( + id: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getprojectsafetyalert400", Getprojectsafetyalert400> + | OpenAiClientError<"Getprojectsafetyalert404", Getprojectsafetyalert404> + | OpenAiClientError<"Getprojectsafetyalert429", Getprojectsafetyalert429> + | OpenAiClientError<"Getprojectsafetyalert503", Getprojectsafetyalert503> + > + ( + id: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getprojectsafetyalert400", Getprojectsafetyalert400> + | OpenAiClientError<"Getprojectsafetyalert404", Getprojectsafetyalert404> + | OpenAiClientError<"Getprojectsafetyalert429", Getprojectsafetyalert429> + | OpenAiClientError<"Getprojectsafetyalert503", Getprojectsafetyalert503> + > + } + /** + * Get Live session content + */ + readonly "downloadLiveRecording": { + ( + sessionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DownloadLiveRecording400", DownloadLiveRecording400> + | OpenAiClientError<"DownloadLiveRecording404", DownloadLiveRecording404> + | OpenAiClientError<"DownloadLiveRecording429", DownloadLiveRecording429> + | OpenAiClientError<"DownloadLiveRecording500", DownloadLiveRecording500> + | OpenAiClientError<"DownloadLiveRecording503", DownloadLiveRecording503> + > + ( + sessionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DownloadLiveRecording400", DownloadLiveRecording400> + | OpenAiClientError<"DownloadLiveRecording404", DownloadLiveRecording404> + | OpenAiClientError<"DownloadLiveRecording429", DownloadLiveRecording429> + | OpenAiClientError<"DownloadLiveRecording500", DownloadLiveRecording500> + | OpenAiClientError<"DownloadLiveRecording503", DownloadLiveRecording503> + > + } + /** + * Get Live session content + */ + readonly "downloadLiveRecordingStream": ( + sessionId: string + ) => Stream.Stream + /** + * Get Decisions API usage details for the organization. + */ + readonly "OrganizationDecisionsusage": { + ( + options: { readonly params: typeof OrganizationDecisionsusageParams.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"OrganizationDecisionsusage400", OrganizationDecisionsusage400> + | OpenAiClientError<"OrganizationDecisionsusage403", OrganizationDecisionsusage403> + | OpenAiClientError<"OrganizationDecisionsusage429", OrganizationDecisionsusage429> + > + ( + options: { + readonly params: typeof OrganizationDecisionsusageParams.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"OrganizationDecisionsusage400", OrganizationDecisionsusage400> + | OpenAiClientError<"OrganizationDecisionsusage403", OrganizationDecisionsusage403> + | OpenAiClientError<"OrganizationDecisionsusage429", OrganizationDecisionsusage429> + > + } + /** + * List the organization's customer-managed external storage configurations. + */ + readonly "Listexternalstorageconfigurations": { + ( + options: { + readonly params?: typeof ListexternalstorageconfigurationsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Listexternalstorageconfigurations400", Listexternalstorageconfigurations400> + | OpenAiClientError<"Listexternalstorageconfigurations403", Listexternalstorageconfigurations403> + | OpenAiClientError<"Listexternalstorageconfigurations404", Listexternalstorageconfigurations404> + > + ( + options: { + readonly params?: typeof ListexternalstorageconfigurationsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Listexternalstorageconfigurations400", Listexternalstorageconfigurations400> + | OpenAiClientError<"Listexternalstorageconfigurations403", Listexternalstorageconfigurations403> + | OpenAiClientError<"Listexternalstorageconfigurations404", Listexternalstorageconfigurations404> + > + } + /** + * Register one customer-managed external storage configuration. + */ + readonly "Createanexternalstorageconfiguration": { + ( + options: { + readonly payload: typeof CreateanexternalstorageconfigurationRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Createanexternalstorageconfiguration400", Createanexternalstorageconfiguration400> + | OpenAiClientError<"Createanexternalstorageconfiguration403", Createanexternalstorageconfiguration403> + | OpenAiClientError<"Createanexternalstorageconfiguration404", Createanexternalstorageconfiguration404> + | OpenAiClientError<"Createanexternalstorageconfiguration503", Createanexternalstorageconfiguration503> + > + ( + options: { + readonly payload: typeof CreateanexternalstorageconfigurationRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Createanexternalstorageconfiguration400", Createanexternalstorageconfiguration400> + | OpenAiClientError<"Createanexternalstorageconfiguration403", Createanexternalstorageconfiguration403> + | OpenAiClientError<"Createanexternalstorageconfiguration404", Createanexternalstorageconfiguration404> + | OpenAiClientError<"Createanexternalstorageconfiguration503", Createanexternalstorageconfiguration503> + > + } + /** + * Get one customer-managed external storage configuration. + */ + readonly "Getanexternalstorageconfiguration": { + ( + externalStorageId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getanexternalstorageconfiguration400", Getanexternalstorageconfiguration400> + | OpenAiClientError<"Getanexternalstorageconfiguration403", Getanexternalstorageconfiguration403> + | OpenAiClientError<"Getanexternalstorageconfiguration404", Getanexternalstorageconfiguration404> + > + ( + externalStorageId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getanexternalstorageconfiguration400", Getanexternalstorageconfiguration400> + | OpenAiClientError<"Getanexternalstorageconfiguration403", Getanexternalstorageconfiguration403> + | OpenAiClientError<"Getanexternalstorageconfiguration404", Getanexternalstorageconfiguration404> + > + } + /** + * Disconnect a customer-managed external storage configuration. Removing the project's last configuration restores organization-default retention if customer-managed retention was active. Repeating a deletion also completes any interrupted retention update. Cloud storage is unchanged. + */ + readonly "Deleteanexternalstorageconfiguration": { + ( + externalStorageId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Deleteanexternalstorageconfiguration400", Deleteanexternalstorageconfiguration400> + | OpenAiClientError<"Deleteanexternalstorageconfiguration403", Deleteanexternalstorageconfiguration403> + | OpenAiClientError<"Deleteanexternalstorageconfiguration404", Deleteanexternalstorageconfiguration404> + > + ( + externalStorageId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Deleteanexternalstorageconfiguration400", Deleteanexternalstorageconfiguration400> + | OpenAiClientError<"Deleteanexternalstorageconfiguration403", Deleteanexternalstorageconfiguration403> + | OpenAiClientError<"Deleteanexternalstorageconfiguration404", Deleteanexternalstorageconfiguration404> + > + } + /** + * Validate one customer-managed external storage configuration. + */ + readonly "Validateanexternalstorageconfiguration": { + ( + externalStorageId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Validateanexternalstorageconfiguration400", Validateanexternalstorageconfiguration400> + | OpenAiClientError<"Validateanexternalstorageconfiguration403", Validateanexternalstorageconfiguration403> + | OpenAiClientError<"Validateanexternalstorageconfiguration404", Validateanexternalstorageconfiguration404> + | OpenAiClientError<"Validateanexternalstorageconfiguration503", Validateanexternalstorageconfiguration503> + > + ( + externalStorageId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Validateanexternalstorageconfiguration400", Validateanexternalstorageconfiguration400> + | OpenAiClientError<"Validateanexternalstorageconfiguration403", Validateanexternalstorageconfiguration403> + | OpenAiClientError<"Validateanexternalstorageconfiguration404", Validateanexternalstorageconfiguration404> + | OpenAiClientError<"Validateanexternalstorageconfiguration503", Validateanexternalstorageconfiguration503> + > + } + /** + * Get the organization's hard spend limit. + */ + readonly "Getorganizationspendlimit": { + ( + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getorganizationspendlimit403", Getorganizationspendlimit403> + | OpenAiClientError<"Getorganizationspendlimit404", Getorganizationspendlimit404> + > + ( + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getorganizationspendlimit403", Getorganizationspendlimit403> + | OpenAiClientError<"Getorganizationspendlimit404", Getorganizationspendlimit404> + > + } + /** + * Create or replace the organization's hard spend limit. + */ + readonly "Updateorganizationspendlimit": { + ( + options: { readonly payload: typeof UpdateorganizationspendlimitRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Updateorganizationspendlimit400", Updateorganizationspendlimit400> + | OpenAiClientError<"Updateorganizationspendlimit403", Updateorganizationspendlimit403> + > + ( + options: { + readonly payload: typeof UpdateorganizationspendlimitRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Updateorganizationspendlimit400", Updateorganizationspendlimit400> + | OpenAiClientError<"Updateorganizationspendlimit403", Updateorganizationspendlimit403> + > + } + /** + * Delete the organization's hard spend limit. + */ + readonly "Deleteorganizationspendlimit": { + ( + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Deleteorganizationspendlimit403", Deleteorganizationspendlimit403> + | OpenAiClientError<"Deleteorganizationspendlimit404", Deleteorganizationspendlimit404> + > + ( + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Deleteorganizationspendlimit403", Deleteorganizationspendlimit403> + | OpenAiClientError<"Deleteorganizationspendlimit404", Deleteorganizationspendlimit404> + > + } + /** + * Get a project's hard spend limit. + */ + readonly "Getprojectspendlimit": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getprojectspendlimit400", Getprojectspendlimit400> + | OpenAiClientError<"Getprojectspendlimit403", Getprojectspendlimit403> + | OpenAiClientError<"Getprojectspendlimit404", Getprojectspendlimit404> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getprojectspendlimit400", Getprojectspendlimit400> + | OpenAiClientError<"Getprojectspendlimit403", Getprojectspendlimit403> + | OpenAiClientError<"Getprojectspendlimit404", Getprojectspendlimit404> + > + } + /** + * Create or replace a project's hard spend limit. + */ + readonly "Updateprojectspendlimit": { + ( + projectId: string, + options: { readonly payload: typeof UpdateprojectspendlimitRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Updateprojectspendlimit400", Updateprojectspendlimit400> + | OpenAiClientError<"Updateprojectspendlimit403", Updateprojectspendlimit403> + | OpenAiClientError<"Updateprojectspendlimit404", Updateprojectspendlimit404> + > + ( + projectId: string, + options: { + readonly payload: typeof UpdateprojectspendlimitRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Updateprojectspendlimit400", Updateprojectspendlimit400> + | OpenAiClientError<"Updateprojectspendlimit403", Updateprojectspendlimit403> + | OpenAiClientError<"Updateprojectspendlimit404", Updateprojectspendlimit404> + > + } + /** + * Delete a project's hard spend limit. + */ + readonly "Deleteprojectspendlimit": { + ( + projectId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Deleteprojectspendlimit400", Deleteprojectspendlimit400> + | OpenAiClientError<"Deleteprojectspendlimit403", Deleteprojectspendlimit403> + | OpenAiClientError<"Deleteprojectspendlimit404", Deleteprojectspendlimit404> + > + ( + projectId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Deleteprojectspendlimit400", Deleteprojectspendlimit400> + | OpenAiClientError<"Deleteprojectspendlimit403", Deleteprojectspendlimit403> + | OpenAiClientError<"Deleteprojectspendlimit404", Deleteprojectspendlimit404> + > + } + /** + * Creates an API key for a service account in the project. + */ + readonly "CreateanAPIkeyforaserviceaccount": { + ( + projectId: string, + serviceAccountId: string, + options: { readonly payload: typeof CreateanAPIkeyforaserviceaccountRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount400", CreateanAPIkeyforaserviceaccount400> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount403", CreateanAPIkeyforaserviceaccount403> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount404", CreateanAPIkeyforaserviceaccount404> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount409", CreateanAPIkeyforaserviceaccount409> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount429", CreateanAPIkeyforaserviceaccount429> + > + ( + projectId: string, + serviceAccountId: string, + options: { + readonly payload: typeof CreateanAPIkeyforaserviceaccountRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount400", CreateanAPIkeyforaserviceaccount400> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount403", CreateanAPIkeyforaserviceaccount403> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount404", CreateanAPIkeyforaserviceaccount404> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount409", CreateanAPIkeyforaserviceaccount409> + | OpenAiClientError<"CreateanAPIkeyforaserviceaccount429", CreateanAPIkeyforaserviceaccount429> + > + } + /** + * Check whether an image or audio file contains known OpenAI provenance signals. [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance). + * + * If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect. + */ + readonly "Createcontentprovenancecheck": { + ( + options: { readonly payload: typeof CreatecontentprovenancecheckRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Createcontentprovenancecheck400", Createcontentprovenancecheck400> + | OpenAiClientError<"Createcontentprovenancecheck404", Createcontentprovenancecheck404> + | OpenAiClientError<"Createcontentprovenancecheck429", Createcontentprovenancecheck429> + | OpenAiClientError<"Createcontentprovenancecheck500", Createcontentprovenancecheck500> + | OpenAiClientError<"Createcontentprovenancecheck503", Createcontentprovenancecheck503> + > + ( + options: { + readonly payload: typeof CreatecontentprovenancecheckRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Createcontentprovenancecheck400", Createcontentprovenancecheck400> + | OpenAiClientError<"Createcontentprovenancecheck404", Createcontentprovenancecheck404> + | OpenAiClientError<"Createcontentprovenancecheck429", Createcontentprovenancecheck429> + | OpenAiClientError<"Createcontentprovenancecheck500", Createcontentprovenancecheck500> + | OpenAiClientError<"Createcontentprovenancecheck503", Createcontentprovenancecheck503> + > + } + /** + * List recently generated videos for the current project. + */ + readonly "ListVideos": { + ( + options: { readonly params?: typeof ListVideosParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVideos400", ListVideos400> + | OpenAiClientError<"ListVideos404", ListVideos404> + > + ( + options: + | { readonly params?: typeof ListVideosParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVideos400", ListVideos400> + | OpenAiClientError<"ListVideos404", ListVideos404> + > + } + /** + * Create a new video generation job from a prompt and optional reference assets. + */ + readonly "createVideo": { + ( + options: { readonly payload: typeof CreateVideoRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideo400", CreateVideo400> + | OpenAiClientError<"CreateVideo404", CreateVideo404> + | OpenAiClientError<"CreateVideo429", CreateVideo429> + | OpenAiClientError<"CreateVideo500", CreateVideo500> + | OpenAiClientError<"CreateVideo503", CreateVideo503> + > + ( + options: { readonly payload: typeof CreateVideoRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideo400", CreateVideo400> + | OpenAiClientError<"CreateVideo404", CreateVideo404> + | OpenAiClientError<"CreateVideo429", CreateVideo429> + | OpenAiClientError<"CreateVideo500", CreateVideo500> + | OpenAiClientError<"CreateVideo503", CreateVideo503> + > + } + /** + * Create a character from an uploaded video. + */ + readonly "CreateVideoCharacter": { + ( + options: { readonly payload: typeof CreateVideoCharacterRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoCharacter400", CreateVideoCharacter400> + | OpenAiClientError<"CreateVideoCharacter404", CreateVideoCharacter404> + | OpenAiClientError<"CreateVideoCharacter500", CreateVideoCharacter500> + | OpenAiClientError<"CreateVideoCharacter504", CreateVideoCharacter504> + > + ( + options: { + readonly payload: typeof CreateVideoCharacterRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoCharacter400", CreateVideoCharacter400> + | OpenAiClientError<"CreateVideoCharacter404", CreateVideoCharacter404> + | OpenAiClientError<"CreateVideoCharacter500", CreateVideoCharacter500> + | OpenAiClientError<"CreateVideoCharacter504", CreateVideoCharacter504> + > + } + /** + * Fetch a character. + */ + readonly "GetVideoCharacter": { + ( + characterId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVideoCharacter400", GetVideoCharacter400> + | OpenAiClientError<"GetVideoCharacter404", GetVideoCharacter404> + > + ( + characterId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVideoCharacter400", GetVideoCharacter400> + | OpenAiClientError<"GetVideoCharacter404", GetVideoCharacter404> + > + } + /** + * Create a new video generation job by editing a source video or existing generated video. + */ + readonly "CreateVideoEdit": { + ( + options: { readonly payload: typeof CreateVideoEditRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoEdit400", CreateVideoEdit400> + | OpenAiClientError<"CreateVideoEdit404", CreateVideoEdit404> + | OpenAiClientError<"CreateVideoEdit429", CreateVideoEdit429> + | OpenAiClientError<"CreateVideoEdit500", CreateVideoEdit500> + | OpenAiClientError<"CreateVideoEdit503", CreateVideoEdit503> + > + ( + options: { readonly payload: typeof CreateVideoEditRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoEdit400", CreateVideoEdit400> + | OpenAiClientError<"CreateVideoEdit404", CreateVideoEdit404> + | OpenAiClientError<"CreateVideoEdit429", CreateVideoEdit429> + | OpenAiClientError<"CreateVideoEdit500", CreateVideoEdit500> + | OpenAiClientError<"CreateVideoEdit503", CreateVideoEdit503> + > + } + /** + * Create an extension of a completed video. + */ + readonly "CreateVideoExtend": { + ( + options: { readonly payload: typeof CreateVideoExtendRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoExtend400", CreateVideoExtend400> + | OpenAiClientError<"CreateVideoExtend404", CreateVideoExtend404> + | OpenAiClientError<"CreateVideoExtend429", CreateVideoExtend429> + | OpenAiClientError<"CreateVideoExtend500", CreateVideoExtend500> + | OpenAiClientError<"CreateVideoExtend503", CreateVideoExtend503> + > + ( + options: { + readonly payload: typeof CreateVideoExtendRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoExtend400", CreateVideoExtend400> + | OpenAiClientError<"CreateVideoExtend404", CreateVideoExtend404> + | OpenAiClientError<"CreateVideoExtend429", CreateVideoExtend429> + | OpenAiClientError<"CreateVideoExtend500", CreateVideoExtend500> + | OpenAiClientError<"CreateVideoExtend503", CreateVideoExtend503> + > + } + /** + * Fetch the latest metadata for a generated video. + */ + readonly "GetVideo": { + ( + videoId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVideo400", GetVideo400> + | OpenAiClientError<"GetVideo404", GetVideo404> + > + ( + videoId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetVideo400", GetVideo400> + | OpenAiClientError<"GetVideo404", GetVideo404> + > + } + /** + * Permanently delete a completed or failed video and its stored assets. + */ + readonly "DeleteVideo": { + ( + videoId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVideo400", DeleteVideo400> + | OpenAiClientError<"DeleteVideo404", DeleteVideo404> + | OpenAiClientError<"DeleteVideo500", DeleteVideo500> + > + ( + videoId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVideo400", DeleteVideo400> + | OpenAiClientError<"DeleteVideo404", DeleteVideo404> + | OpenAiClientError<"DeleteVideo500", DeleteVideo500> + > + } + /** + * Download the generated video bytes or a derived preview asset. + * + * Streams the rendered video content for the specified video job. + */ + readonly "RetrieveVideoContent": { + ( + videoId: string, + options: { readonly params?: typeof RetrieveVideoContentParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVideoContent400", RetrieveVideoContent400> + | OpenAiClientError<"RetrieveVideoContent404", RetrieveVideoContent404> + | OpenAiClientError<"RetrieveVideoContent500", RetrieveVideoContent500> + > + ( + videoId: string, + options: { + readonly params?: typeof RetrieveVideoContentParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVideoContent400", RetrieveVideoContent400> + | OpenAiClientError<"RetrieveVideoContent404", RetrieveVideoContent404> + | OpenAiClientError<"RetrieveVideoContent500", RetrieveVideoContent500> + > + } + /** + * Download the generated video bytes or a derived preview asset. + * + * Streams the rendered video content for the specified video job. + */ + readonly "RetrieveVideoContentStream": ( + videoId: string, + options: { readonly params?: typeof RetrieveVideoContentParams.Encoded | undefined } | undefined + ) => Stream.Stream + /** + * Create a remix of a completed video using a refreshed prompt. + */ + readonly "CreateVideoRemix": { + ( + videoId: string, + options: { readonly payload: typeof CreateVideoRemixRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoRemix400", CreateVideoRemix400> + | OpenAiClientError<"CreateVideoRemix404", CreateVideoRemix404> + | OpenAiClientError<"CreateVideoRemix429", CreateVideoRemix429> + | OpenAiClientError<"CreateVideoRemix500", CreateVideoRemix500> + | OpenAiClientError<"CreateVideoRemix503", CreateVideoRemix503> + > + ( + videoId: string, + options: { + readonly payload: typeof CreateVideoRemixRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVideoRemix400", CreateVideoRemix400> + | OpenAiClientError<"CreateVideoRemix404", CreateVideoRemix404> + | OpenAiClientError<"CreateVideoRemix429", CreateVideoRemix429> + | OpenAiClientError<"CreateVideoRemix500", CreateVideoRemix500> + | OpenAiClientError<"CreateVideoRemix503", CreateVideoRemix503> + > + } + /** + * Returns input token counts of the request. + * + * Returns an object with `object` set to `response.input_tokens` and an `input_tokens` count. + */ + readonly "Getinputtokencounts": { + ( + options: { readonly payload: typeof GetinputtokencountsRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getinputtokencounts400", Getinputtokencounts400> + | OpenAiClientError<"Getinputtokencounts429", Getinputtokencounts429> + > + ( + options: { readonly payload: typeof GetinputtokencountsRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Getinputtokencounts400", Getinputtokencounts400> + | OpenAiClientError<"Getinputtokencounts429", Getinputtokencounts429> + > + } + /** + * Compact a conversation. Returns a compacted response object. + * + * Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced). + */ + readonly "Compactconversation": { + ( + options: { readonly payload: typeof CompactconversationRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Compactconversation400", Compactconversation400> + | OpenAiClientError<"Compactconversation429", Compactconversation429> + | OpenAiClientError<"Compactconversation503", Compactconversation503> + > + ( + options: { readonly payload: typeof CompactconversationRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"Compactconversation400", Compactconversation400> + | OpenAiClientError<"Compactconversation429", Compactconversation429> + | OpenAiClientError<"Compactconversation503", Compactconversation503> + > + } + /** + * List all skills for the current project. + */ + readonly "ListSkills": { + ( + options: { readonly params?: typeof ListSkillsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListSkills404", ListSkills404> + | OpenAiClientError<"ListSkills429", ListSkills429> + > + ( + options: + | { readonly params?: typeof ListSkillsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListSkills404", ListSkills404> + | OpenAiClientError<"ListSkills429", ListSkills429> + > + } + /** + * Create a new skill. + */ + readonly "CreateSkill": { + ( + options: { readonly payload: typeof CreateSkillRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateSkill400", CreateSkill400> + | OpenAiClientError<"CreateSkill429", CreateSkill429> + | OpenAiClientError<"CreateSkill500", CreateSkill500> + > + ( + options: { readonly payload: typeof CreateSkillRequestFormData.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateSkill400", CreateSkill400> + | OpenAiClientError<"CreateSkill429", CreateSkill429> + | OpenAiClientError<"CreateSkill500", CreateSkill500> + > + } + /** + * Get a skill by its ID. + */ + readonly "GetSkill": { + ( + skillId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkill404", GetSkill404> + | OpenAiClientError<"GetSkill429", GetSkill429> + > + ( + skillId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkill404", GetSkill404> + | OpenAiClientError<"GetSkill429", GetSkill429> + > + } + /** + * Update the default version pointer for a skill. + */ + readonly "UpdateSkillDefaultVersion": { + ( + skillId: string, + options: { readonly payload: typeof UpdateSkillDefaultVersionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateSkillDefaultVersion400", UpdateSkillDefaultVersion400> + | OpenAiClientError<"UpdateSkillDefaultVersion404", UpdateSkillDefaultVersion404> + | OpenAiClientError<"UpdateSkillDefaultVersion429", UpdateSkillDefaultVersion429> + > + ( + skillId: string, + options: { + readonly payload: typeof UpdateSkillDefaultVersionRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateSkillDefaultVersion400", UpdateSkillDefaultVersion400> + | OpenAiClientError<"UpdateSkillDefaultVersion404", UpdateSkillDefaultVersion404> + | OpenAiClientError<"UpdateSkillDefaultVersion429", UpdateSkillDefaultVersion429> + > + } + /** + * Delete a skill by its ID. + */ + readonly "DeleteSkill": { + ( + skillId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteSkill404", DeleteSkill404> + | OpenAiClientError<"DeleteSkill429", DeleteSkill429> + > + ( + skillId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteSkill404", DeleteSkill404> + | OpenAiClientError<"DeleteSkill429", DeleteSkill429> + > + } + /** + * Download a skill zip bundle by its ID. + */ + readonly "GetSkillContent": { + ( + skillId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkillContent404", GetSkillContent404> + | OpenAiClientError<"GetSkillContent429", GetSkillContent429> + > + ( + skillId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkillContent404", GetSkillContent404> + | OpenAiClientError<"GetSkillContent429", GetSkillContent429> + > + } + /** + * Download a skill zip bundle by its ID. + */ + readonly "GetSkillContentStream": (skillId: string) => Stream.Stream + /** + * List skill versions for a skill. + */ + readonly "ListSkillVersions": { + ( + skillId: string, + options: { readonly params?: typeof ListSkillVersionsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListSkillVersions400", ListSkillVersions400> + | OpenAiClientError<"ListSkillVersions404", ListSkillVersions404> + | OpenAiClientError<"ListSkillVersions429", ListSkillVersions429> + > + ( + skillId: string, + options: { + readonly params?: typeof ListSkillVersionsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListSkillVersions400", ListSkillVersions400> + | OpenAiClientError<"ListSkillVersions404", ListSkillVersions404> + | OpenAiClientError<"ListSkillVersions429", ListSkillVersions429> + > + } + /** + * Create a new immutable skill version. + */ + readonly "CreateSkillVersion": { + ( + skillId: string, + options: { readonly payload: typeof CreateSkillVersionRequestFormData.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateSkillVersion400", CreateSkillVersion400> + | OpenAiClientError<"CreateSkillVersion404", CreateSkillVersion404> + | OpenAiClientError<"CreateSkillVersion429", CreateSkillVersion429> + | OpenAiClientError<"CreateSkillVersion500", CreateSkillVersion500> + > + ( + skillId: string, + options: { + readonly payload: typeof CreateSkillVersionRequestFormData.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateSkillVersion400", CreateSkillVersion400> + | OpenAiClientError<"CreateSkillVersion404", CreateSkillVersion404> + | OpenAiClientError<"CreateSkillVersion429", CreateSkillVersion429> + | OpenAiClientError<"CreateSkillVersion500", CreateSkillVersion500> + > + } + /** + * Get a specific skill version. + */ + readonly "GetSkillVersion": { + ( + skillId: string, + version: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkillVersion400", GetSkillVersion400> + | OpenAiClientError<"GetSkillVersion404", GetSkillVersion404> + | OpenAiClientError<"GetSkillVersion429", GetSkillVersion429> + > + ( + skillId: string, + version: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkillVersion400", GetSkillVersion400> + | OpenAiClientError<"GetSkillVersion404", GetSkillVersion404> + | OpenAiClientError<"GetSkillVersion429", GetSkillVersion429> + > + } + /** + * Delete a skill version. + */ + readonly "DeleteSkillVersion": { + ( + skillId: string, + version: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteSkillVersion400", DeleteSkillVersion400> + | OpenAiClientError<"DeleteSkillVersion404", DeleteSkillVersion404> + | OpenAiClientError<"DeleteSkillVersion429", DeleteSkillVersion429> + > + ( + skillId: string, + version: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteSkillVersion400", DeleteSkillVersion400> + | OpenAiClientError<"DeleteSkillVersion404", DeleteSkillVersion404> + | OpenAiClientError<"DeleteSkillVersion429", DeleteSkillVersion429> + > + } + /** + * Download a skill version zip bundle. + */ + readonly "GetSkillVersionContent": { + ( + skillId: string, + version: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkillVersionContent400", GetSkillVersionContent400> + | OpenAiClientError<"GetSkillVersionContent404", GetSkillVersionContent404> + | OpenAiClientError<"GetSkillVersionContent429", GetSkillVersionContent429> + > + ( + skillId: string, + version: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetSkillVersionContent400", GetSkillVersionContent400> + | OpenAiClientError<"GetSkillVersionContent404", GetSkillVersionContent404> + | OpenAiClientError<"GetSkillVersionContent429", GetSkillVersionContent429> + > + } + /** + * Download a skill version zip bundle. + */ + readonly "GetSkillVersionContentStream": ( + skillId: string, + version: string + ) => Stream.Stream + /** + * Cancel an active ChatKit session and return its most recent metadata. + * + * Cancelling prevents new requests from using the issued client secret. + */ + readonly "CancelChatSessionMethod": { + ( + sessionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelChatSessionMethod400", CancelChatSessionMethod400> + | OpenAiClientError<"CancelChatSessionMethod404", CancelChatSessionMethod404> + | OpenAiClientError<"CancelChatSessionMethod429", CancelChatSessionMethod429> + > + ( + sessionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CancelChatSessionMethod400", CancelChatSessionMethod400> + | OpenAiClientError<"CancelChatSessionMethod404", CancelChatSessionMethod404> + | OpenAiClientError<"CancelChatSessionMethod429", CancelChatSessionMethod429> + > + } + /** + * Create a ChatKit session. + */ + readonly "CreateChatSessionMethod": { + ( + options: { readonly payload: typeof CreateChatSessionMethodRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateChatSessionMethod400", CreateChatSessionMethod400> + | OpenAiClientError<"CreateChatSessionMethod404", CreateChatSessionMethod404> + | OpenAiClientError<"CreateChatSessionMethod429", CreateChatSessionMethod429> + > + ( + options: { + readonly payload: typeof CreateChatSessionMethodRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateChatSessionMethod400", CreateChatSessionMethod400> + | OpenAiClientError<"CreateChatSessionMethod404", CreateChatSessionMethod404> + | OpenAiClientError<"CreateChatSessionMethod429", CreateChatSessionMethod429> + > + } + /** + * List items that belong to a ChatKit thread. + */ + readonly "ListThreadItemsMethod": { + ( + threadId: string, + options: { readonly params?: typeof ListThreadItemsMethodParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListThreadItemsMethod400", ListThreadItemsMethod400> + | OpenAiClientError<"ListThreadItemsMethod404", ListThreadItemsMethod404> + | OpenAiClientError<"ListThreadItemsMethod429", ListThreadItemsMethod429> + > + ( + threadId: string, + options: { + readonly params?: typeof ListThreadItemsMethodParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListThreadItemsMethod400", ListThreadItemsMethod400> + | OpenAiClientError<"ListThreadItemsMethod404", ListThreadItemsMethod404> + | OpenAiClientError<"ListThreadItemsMethod429", ListThreadItemsMethod429> + > + } + /** + * Retrieve a ChatKit thread by its identifier. + */ + readonly "GetThreadMethod": { + ( + threadId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetThreadMethod400", GetThreadMethod400> + | OpenAiClientError<"GetThreadMethod404", GetThreadMethod404> + | OpenAiClientError<"GetThreadMethod429", GetThreadMethod429> + > + ( + threadId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"GetThreadMethod400", GetThreadMethod400> + | OpenAiClientError<"GetThreadMethod404", GetThreadMethod404> + | OpenAiClientError<"GetThreadMethod429", GetThreadMethod429> + > + } + /** + * Delete a ChatKit thread along with its items and stored attachments. + */ + readonly "DeleteThreadMethod": { + ( + threadId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteThreadMethod400", DeleteThreadMethod400> + | OpenAiClientError<"DeleteThreadMethod404", DeleteThreadMethod404> + | OpenAiClientError<"DeleteThreadMethod429", DeleteThreadMethod429> + > + ( + threadId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteThreadMethod400", DeleteThreadMethod400> + | OpenAiClientError<"DeleteThreadMethod404", DeleteThreadMethod404> + | OpenAiClientError<"DeleteThreadMethod429", DeleteThreadMethod429> + > + } + /** + * List ChatKit threads with optional pagination and user filters. + */ + readonly "ListThreadsMethod": { + ( + options: { readonly params?: typeof ListThreadsMethodParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListThreadsMethod400", ListThreadsMethod400> + | OpenAiClientError<"ListThreadsMethod404", ListThreadsMethod404> + | OpenAiClientError<"ListThreadsMethod429", ListThreadsMethod429> + > + ( + options: { + readonly params?: typeof ListThreadsMethodParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListThreadsMethod400", ListThreadsMethod400> + | OpenAiClientError<"ListThreadsMethod404", ListThreadsMethod404> + | OpenAiClientError<"ListThreadsMethod429", ListThreadsMethod429> + > + } + /** + * Use this endpoint to ask classification or scoring questions about the same input. You’ll get the answers back in the order you asked the questions. + * + * For text, you can pass a string. You can also send user messages containing `input_text` and `input_image` parts, with up to 128 images per request. Images can be base64 data URLs or publicly accessible HTTP(S) URLs. File IDs aren’t accepted. Other message roles, function calls, files, audio, and item references aren’t supported. + * + * Sometimes a question returns a refusal instead of an answer. The result has type `refusal` and includes the question’s name, or `null` if you didn’t give it one. + */ + readonly "createDecision": { + ( + options: { readonly payload: typeof CreateDecisionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect, HttpClientError.HttpClientError | SchemaError> + ( + options: { readonly payload: typeof CreateDecisionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + HttpClientError.HttpClientError | SchemaError + > + } + /** + * Lists OpenAI-hosted environments owned by the authenticated principal. Requires access to the prewarming beta. + */ + readonly "listAgentEnvironments": { + ( + options: { readonly params?: typeof ListAgentEnvironmentsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentEnvironments400", ListAgentEnvironments400> + | OpenAiClientError<"ListAgentEnvironments401", ListAgentEnvironments401> + | OpenAiClientError<"ListAgentEnvironments403", ListAgentEnvironments403> + | OpenAiClientError<"ListAgentEnvironments404", ListAgentEnvironments404> + | OpenAiClientError<"ListAgentEnvironments409", ListAgentEnvironments409> + | OpenAiClientError<"ListAgentEnvironments500", ListAgentEnvironments500> + | OpenAiClientError<"ListAgentEnvironments503", ListAgentEnvironments503> + > + ( + options: { + readonly params?: typeof ListAgentEnvironmentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentEnvironments400", ListAgentEnvironments400> + | OpenAiClientError<"ListAgentEnvironments401", ListAgentEnvironments401> + | OpenAiClientError<"ListAgentEnvironments403", ListAgentEnvironments403> + | OpenAiClientError<"ListAgentEnvironments404", ListAgentEnvironments404> + | OpenAiClientError<"ListAgentEnvironments409", ListAgentEnvironments409> + | OpenAiClientError<"ListAgentEnvironments500", ListAgentEnvironments500> + | OpenAiClientError<"ListAgentEnvironments503", ListAgentEnvironments503> + > + } + /** + * Creates an OpenAI-hosted environment before creating a session. Requires access to the prewarming beta. + */ + readonly "createAgentEnvironment": { + ( + options: { + readonly params?: typeof CreateAgentEnvironmentParams2.Encoded | undefined + readonly payload: typeof CreateAgentEnvironmentRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentEnvironment400", CreateAgentEnvironment400> + | OpenAiClientError<"CreateAgentEnvironment401", CreateAgentEnvironment401> + | OpenAiClientError<"CreateAgentEnvironment403", CreateAgentEnvironment403> + | OpenAiClientError<"CreateAgentEnvironment404", CreateAgentEnvironment404> + | OpenAiClientError<"CreateAgentEnvironment409", CreateAgentEnvironment409> + | OpenAiClientError<"CreateAgentEnvironment429", CreateAgentEnvironment429> + | OpenAiClientError<"CreateAgentEnvironment500", CreateAgentEnvironment500> + | OpenAiClientError<"CreateAgentEnvironment503", CreateAgentEnvironment503> + > + ( + options: { + readonly params?: typeof CreateAgentEnvironmentParams2.Encoded | undefined + readonly payload: typeof CreateAgentEnvironmentRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentEnvironment400", CreateAgentEnvironment400> + | OpenAiClientError<"CreateAgentEnvironment401", CreateAgentEnvironment401> + | OpenAiClientError<"CreateAgentEnvironment403", CreateAgentEnvironment403> + | OpenAiClientError<"CreateAgentEnvironment404", CreateAgentEnvironment404> + | OpenAiClientError<"CreateAgentEnvironment409", CreateAgentEnvironment409> + | OpenAiClientError<"CreateAgentEnvironment429", CreateAgentEnvironment429> + | OpenAiClientError<"CreateAgentEnvironment500", CreateAgentEnvironment500> + | OpenAiClientError<"CreateAgentEnvironment503", CreateAgentEnvironment503> + > + } + /** + * Retrieves an execution environment's connection status and safe installed metadata. See [environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle). + */ + readonly "retrieveAgentEnvironment": { + ( + environmentId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentEnvironment400", RetrieveAgentEnvironment400> + | OpenAiClientError<"RetrieveAgentEnvironment401", RetrieveAgentEnvironment401> + | OpenAiClientError<"RetrieveAgentEnvironment403", RetrieveAgentEnvironment403> + | OpenAiClientError<"RetrieveAgentEnvironment404", RetrieveAgentEnvironment404> + | OpenAiClientError<"RetrieveAgentEnvironment409", RetrieveAgentEnvironment409> + | OpenAiClientError<"RetrieveAgentEnvironment500", RetrieveAgentEnvironment500> + | OpenAiClientError<"RetrieveAgentEnvironment503", RetrieveAgentEnvironment503> + > + ( + environmentId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentEnvironment400", RetrieveAgentEnvironment400> + | OpenAiClientError<"RetrieveAgentEnvironment401", RetrieveAgentEnvironment401> + | OpenAiClientError<"RetrieveAgentEnvironment403", RetrieveAgentEnvironment403> + | OpenAiClientError<"RetrieveAgentEnvironment404", RetrieveAgentEnvironment404> + | OpenAiClientError<"RetrieveAgentEnvironment409", RetrieveAgentEnvironment409> + | OpenAiClientError<"RetrieveAgentEnvironment500", RetrieveAgentEnvironment500> + | OpenAiClientError<"RetrieveAgentEnvironment503", RetrieveAgentEnvironment503> + > + } + /** + * Lists live files on a connected execution environment with optional directory filtering and opaque cursor pagination. See [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + */ + readonly "listAgentEnvironmentFiles": { + ( + environmentId: string, + options: { readonly params?: typeof ListAgentEnvironmentFilesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentEnvironmentFiles400", ListAgentEnvironmentFiles400> + | OpenAiClientError<"ListAgentEnvironmentFiles401", ListAgentEnvironmentFiles401> + | OpenAiClientError<"ListAgentEnvironmentFiles403", ListAgentEnvironmentFiles403> + | OpenAiClientError<"ListAgentEnvironmentFiles404", ListAgentEnvironmentFiles404> + | OpenAiClientError<"ListAgentEnvironmentFiles409", ListAgentEnvironmentFiles409> + | OpenAiClientError<"ListAgentEnvironmentFiles500", ListAgentEnvironmentFiles500> + | OpenAiClientError<"ListAgentEnvironmentFiles503", ListAgentEnvironmentFiles503> + > + ( + environmentId: string, + options: { + readonly params?: typeof ListAgentEnvironmentFilesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentEnvironmentFiles400", ListAgentEnvironmentFiles400> + | OpenAiClientError<"ListAgentEnvironmentFiles401", ListAgentEnvironmentFiles401> + | OpenAiClientError<"ListAgentEnvironmentFiles403", ListAgentEnvironmentFiles403> + | OpenAiClientError<"ListAgentEnvironmentFiles404", ListAgentEnvironmentFiles404> + | OpenAiClientError<"ListAgentEnvironmentFiles409", ListAgentEnvironmentFiles409> + | OpenAiClientError<"ListAgentEnvironmentFiles500", ListAgentEnvironmentFiles500> + | OpenAiClientError<"ListAgentEnvironmentFiles503", ListAgentEnvironmentFiles503> + > + } + /** + * Copies inline bytes or a Files API file into a connected execution environment. See [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files). + */ + readonly "createAgentEnvironmentFile": { + ( + environmentId: string, + options: { readonly payload: typeof CreateAgentEnvironmentFileRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentEnvironmentFile400", CreateAgentEnvironmentFile400> + | OpenAiClientError<"CreateAgentEnvironmentFile401", CreateAgentEnvironmentFile401> + | OpenAiClientError<"CreateAgentEnvironmentFile403", CreateAgentEnvironmentFile403> + | OpenAiClientError<"CreateAgentEnvironmentFile404", CreateAgentEnvironmentFile404> + | OpenAiClientError<"CreateAgentEnvironmentFile409", CreateAgentEnvironmentFile409> + | OpenAiClientError<"CreateAgentEnvironmentFile429", CreateAgentEnvironmentFile429> + | OpenAiClientError<"CreateAgentEnvironmentFile500", CreateAgentEnvironmentFile500> + | OpenAiClientError<"CreateAgentEnvironmentFile503", CreateAgentEnvironmentFile503> + > + ( + environmentId: string, + options: { + readonly payload: typeof CreateAgentEnvironmentFileRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentEnvironmentFile400", CreateAgentEnvironmentFile400> + | OpenAiClientError<"CreateAgentEnvironmentFile401", CreateAgentEnvironmentFile401> + | OpenAiClientError<"CreateAgentEnvironmentFile403", CreateAgentEnvironmentFile403> + | OpenAiClientError<"CreateAgentEnvironmentFile404", CreateAgentEnvironmentFile404> + | OpenAiClientError<"CreateAgentEnvironmentFile409", CreateAgentEnvironmentFile409> + | OpenAiClientError<"CreateAgentEnvironmentFile429", CreateAgentEnvironmentFile429> + | OpenAiClientError<"CreateAgentEnvironmentFile500", CreateAgentEnvironmentFile500> + | OpenAiClientError<"CreateAgentEnvironmentFile503", CreateAgentEnvironmentFile503> + > + } + /** + * Lists subagents in a session, including nested and closed subagents. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + */ + readonly "listAgentSessionSubagents": { + ( + sessionId: string, + options: { readonly params?: typeof ListAgentSessionSubagentsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagents400", ListAgentSessionSubagents400> + | OpenAiClientError<"ListAgentSessionSubagents401", ListAgentSessionSubagents401> + | OpenAiClientError<"ListAgentSessionSubagents403", ListAgentSessionSubagents403> + | OpenAiClientError<"ListAgentSessionSubagents404", ListAgentSessionSubagents404> + | OpenAiClientError<"ListAgentSessionSubagents409", ListAgentSessionSubagents409> + | OpenAiClientError<"ListAgentSessionSubagents500", ListAgentSessionSubagents500> + | OpenAiClientError<"ListAgentSessionSubagents503", ListAgentSessionSubagents503> + > + ( + sessionId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagents400", ListAgentSessionSubagents400> + | OpenAiClientError<"ListAgentSessionSubagents401", ListAgentSessionSubagents401> + | OpenAiClientError<"ListAgentSessionSubagents403", ListAgentSessionSubagents403> + | OpenAiClientError<"ListAgentSessionSubagents404", ListAgentSessionSubagents404> + | OpenAiClientError<"ListAgentSessionSubagents409", ListAgentSessionSubagents409> + | OpenAiClientError<"ListAgentSessionSubagents500", ListAgentSessionSubagents500> + | OpenAiClientError<"ListAgentSessionSubagents503", ListAgentSessionSubagents503> + > + } + /** + * Retrieves a subagent belonging to this session. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + */ + readonly "retrieveAgentSessionSubagent": { + ( + sessionId: string, + subagentId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionSubagent400", RetrieveAgentSessionSubagent400> + | OpenAiClientError<"RetrieveAgentSessionSubagent401", RetrieveAgentSessionSubagent401> + | OpenAiClientError<"RetrieveAgentSessionSubagent403", RetrieveAgentSessionSubagent403> + | OpenAiClientError<"RetrieveAgentSessionSubagent404", RetrieveAgentSessionSubagent404> + | OpenAiClientError<"RetrieveAgentSessionSubagent409", RetrieveAgentSessionSubagent409> + | OpenAiClientError<"RetrieveAgentSessionSubagent500", RetrieveAgentSessionSubagent500> + | OpenAiClientError<"RetrieveAgentSessionSubagent503", RetrieveAgentSessionSubagent503> + > + ( + sessionId: string, + subagentId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionSubagent400", RetrieveAgentSessionSubagent400> + | OpenAiClientError<"RetrieveAgentSessionSubagent401", RetrieveAgentSessionSubagent401> + | OpenAiClientError<"RetrieveAgentSessionSubagent403", RetrieveAgentSessionSubagent403> + | OpenAiClientError<"RetrieveAgentSessionSubagent404", RetrieveAgentSessionSubagent404> + | OpenAiClientError<"RetrieveAgentSessionSubagent409", RetrieveAgentSessionSubagent409> + | OpenAiClientError<"RetrieveAgentSessionSubagent500", RetrieveAgentSessionSubagent500> + | OpenAiClientError<"RetrieveAgentSessionSubagent503", RetrieveAgentSessionSubagent503> + > + } + /** + * Lists this subagent's own items across all of its turns. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + */ + readonly "listAgentSessionSubagentItems": { + ( + sessionId: string, + subagentId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentItemsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagentItems400", ListAgentSessionSubagentItems400> + | OpenAiClientError<"ListAgentSessionSubagentItems401", ListAgentSessionSubagentItems401> + | OpenAiClientError<"ListAgentSessionSubagentItems403", ListAgentSessionSubagentItems403> + | OpenAiClientError<"ListAgentSessionSubagentItems404", ListAgentSessionSubagentItems404> + | OpenAiClientError<"ListAgentSessionSubagentItems409", ListAgentSessionSubagentItems409> + | OpenAiClientError<"ListAgentSessionSubagentItems500", ListAgentSessionSubagentItems500> + | OpenAiClientError<"ListAgentSessionSubagentItems503", ListAgentSessionSubagentItems503> + > + ( + sessionId: string, + subagentId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagentItems400", ListAgentSessionSubagentItems400> + | OpenAiClientError<"ListAgentSessionSubagentItems401", ListAgentSessionSubagentItems401> + | OpenAiClientError<"ListAgentSessionSubagentItems403", ListAgentSessionSubagentItems403> + | OpenAiClientError<"ListAgentSessionSubagentItems404", ListAgentSessionSubagentItems404> + | OpenAiClientError<"ListAgentSessionSubagentItems409", ListAgentSessionSubagentItems409> + | OpenAiClientError<"ListAgentSessionSubagentItems500", ListAgentSessionSubagentItems500> + | OpenAiClientError<"ListAgentSessionSubagentItems503", ListAgentSessionSubagentItems503> + > + } + /** + * Lists all turns of this subagent, including turns after a resume. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + */ + readonly "listAgentSessionSubagentTurns": { + ( + sessionId: string, + subagentId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentTurnsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagentTurns400", ListAgentSessionSubagentTurns400> + | OpenAiClientError<"ListAgentSessionSubagentTurns401", ListAgentSessionSubagentTurns401> + | OpenAiClientError<"ListAgentSessionSubagentTurns403", ListAgentSessionSubagentTurns403> + | OpenAiClientError<"ListAgentSessionSubagentTurns404", ListAgentSessionSubagentTurns404> + | OpenAiClientError<"ListAgentSessionSubagentTurns409", ListAgentSessionSubagentTurns409> + | OpenAiClientError<"ListAgentSessionSubagentTurns500", ListAgentSessionSubagentTurns500> + | OpenAiClientError<"ListAgentSessionSubagentTurns503", ListAgentSessionSubagentTurns503> + > + ( + sessionId: string, + subagentId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentTurnsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagentTurns400", ListAgentSessionSubagentTurns400> + | OpenAiClientError<"ListAgentSessionSubagentTurns401", ListAgentSessionSubagentTurns401> + | OpenAiClientError<"ListAgentSessionSubagentTurns403", ListAgentSessionSubagentTurns403> + | OpenAiClientError<"ListAgentSessionSubagentTurns404", ListAgentSessionSubagentTurns404> + | OpenAiClientError<"ListAgentSessionSubagentTurns409", ListAgentSessionSubagentTurns409> + | OpenAiClientError<"ListAgentSessionSubagentTurns500", ListAgentSessionSubagentTurns500> + | OpenAiClientError<"ListAgentSessionSubagentTurns503", ListAgentSessionSubagentTurns503> + > + } + /** + * Retrieves a turn belonging to this subagent. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + */ + readonly "retrieveAgentSessionSubagentTurn": { + ( + sessionId: string, + subagentId: string, + turnId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn400", RetrieveAgentSessionSubagentTurn400> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn401", RetrieveAgentSessionSubagentTurn401> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn403", RetrieveAgentSessionSubagentTurn403> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn404", RetrieveAgentSessionSubagentTurn404> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn409", RetrieveAgentSessionSubagentTurn409> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn500", RetrieveAgentSessionSubagentTurn500> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn503", RetrieveAgentSessionSubagentTurn503> + > + ( + sessionId: string, + subagentId: string, + turnId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn400", RetrieveAgentSessionSubagentTurn400> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn401", RetrieveAgentSessionSubagentTurn401> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn403", RetrieveAgentSessionSubagentTurn403> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn404", RetrieveAgentSessionSubagentTurn404> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn409", RetrieveAgentSessionSubagentTurn409> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn500", RetrieveAgentSessionSubagentTurn500> + | OpenAiClientError<"RetrieveAgentSessionSubagentTurn503", RetrieveAgentSessionSubagentTurn503> + > + } + /** + * Lists items belonging to one turn of this subagent. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent). + */ + readonly "listAgentSessionSubagentTurnItems": { + ( + sessionId: string, + subagentId: string, + turnId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentTurnItemsParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagentTurnItems400", ListAgentSessionSubagentTurnItems400> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems401", ListAgentSessionSubagentTurnItems401> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems403", ListAgentSessionSubagentTurnItems403> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems404", ListAgentSessionSubagentTurnItems404> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems409", ListAgentSessionSubagentTurnItems409> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems500", ListAgentSessionSubagentTurnItems500> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems503", ListAgentSessionSubagentTurnItems503> + > + ( + sessionId: string, + subagentId: string, + turnId: string, + options: { + readonly params?: typeof ListAgentSessionSubagentTurnItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionSubagentTurnItems400", ListAgentSessionSubagentTurnItems400> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems401", ListAgentSessionSubagentTurnItems401> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems403", ListAgentSessionSubagentTurnItems403> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems404", ListAgentSessionSubagentTurnItems404> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems409", ListAgentSessionSubagentTurnItems409> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems500", ListAgentSessionSubagentTurnItems500> + | OpenAiClientError<"ListAgentSessionSubagentTurnItems503", ListAgentSessionSubagentTurnItems503> + > + } + /** + * Lists reusable agents in the current project. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + */ + readonly "listAgents": { + ( + options: { readonly params?: typeof ListAgentsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgents400", ListAgents400> + | OpenAiClientError<"ListAgents401", ListAgents401> + | OpenAiClientError<"ListAgents403", ListAgents403> + | OpenAiClientError<"ListAgents404", ListAgents404> + | OpenAiClientError<"ListAgents409", ListAgents409> + | OpenAiClientError<"ListAgents500", ListAgents500> + | OpenAiClientError<"ListAgents503", ListAgents503> + > + ( + options: + | { readonly params?: typeof ListAgentsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgents400", ListAgents400> + | OpenAiClientError<"ListAgents401", ListAgents401> + | OpenAiClientError<"ListAgents403", ListAgents403> + | OpenAiClientError<"ListAgents404", ListAgents404> + | OpenAiClientError<"ListAgents409", ListAgents409> + | OpenAiClientError<"ListAgents500", ListAgents500> + | OpenAiClientError<"ListAgents503", ListAgents503> + > + } + /** + * Creates a reusable agent without storing credentials. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + */ + readonly "createAgent": { + ( + options: { readonly payload: typeof CreateAgentRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgent400", CreateAgent400> + | OpenAiClientError<"CreateAgent401", CreateAgent401> + | OpenAiClientError<"CreateAgent403", CreateAgent403> + | OpenAiClientError<"CreateAgent404", CreateAgent404> + | OpenAiClientError<"CreateAgent409", CreateAgent409> + | OpenAiClientError<"CreateAgent500", CreateAgent500> + | OpenAiClientError<"CreateAgent503", CreateAgent503> + > + ( + options: { readonly payload: typeof CreateAgentRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgent400", CreateAgent400> + | OpenAiClientError<"CreateAgent401", CreateAgent401> + | OpenAiClientError<"CreateAgent403", CreateAgent403> + | OpenAiClientError<"CreateAgent404", CreateAgent404> + | OpenAiClientError<"CreateAgent409", CreateAgent409> + | OpenAiClientError<"CreateAgent500", CreateAgent500> + | OpenAiClientError<"CreateAgent503", CreateAgent503> + > + } + /** + * Retrieves a reusable agent by ID. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + */ + readonly "retrieveAgent": { + ( + agentId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgent400", RetrieveAgent400> + | OpenAiClientError<"RetrieveAgent401", RetrieveAgent401> + | OpenAiClientError<"RetrieveAgent403", RetrieveAgent403> + | OpenAiClientError<"RetrieveAgent404", RetrieveAgent404> + | OpenAiClientError<"RetrieveAgent409", RetrieveAgent409> + | OpenAiClientError<"RetrieveAgent500", RetrieveAgent500> + | OpenAiClientError<"RetrieveAgent503", RetrieveAgent503> + > + ( + agentId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgent400", RetrieveAgent400> + | OpenAiClientError<"RetrieveAgent401", RetrieveAgent401> + | OpenAiClientError<"RetrieveAgent403", RetrieveAgent403> + | OpenAiClientError<"RetrieveAgent404", RetrieveAgent404> + | OpenAiClientError<"RetrieveAgent409", RetrieveAgent409> + | OpenAiClientError<"RetrieveAgent500", RetrieveAgent500> + | OpenAiClientError<"RetrieveAgent503", RetrieveAgent503> + > + } + /** + * Updates a reusable agent. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + */ + readonly "updateAgent": { + ( + agentId: string, + options: { readonly payload: typeof UpdateAgentRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateAgent400", UpdateAgent400> + | OpenAiClientError<"UpdateAgent401", UpdateAgent401> + | OpenAiClientError<"UpdateAgent403", UpdateAgent403> + | OpenAiClientError<"UpdateAgent404", UpdateAgent404> + | OpenAiClientError<"UpdateAgent409", UpdateAgent409> + | OpenAiClientError<"UpdateAgent500", UpdateAgent500> + | OpenAiClientError<"UpdateAgent503", UpdateAgent503> + > + ( + agentId: string, + options: { readonly payload: typeof UpdateAgentRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateAgent400", UpdateAgent400> + | OpenAiClientError<"UpdateAgent401", UpdateAgent401> + | OpenAiClientError<"UpdateAgent403", UpdateAgent403> + | OpenAiClientError<"UpdateAgent404", UpdateAgent404> + | OpenAiClientError<"UpdateAgent409", UpdateAgent409> + | OpenAiClientError<"UpdateAgent500", UpdateAgent500> + | OpenAiClientError<"UpdateAgent503", UpdateAgent503> + > + } + /** + * Deletes a reusable agent. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration). + */ + readonly "deleteAgent": { + ( + agentId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgent400", DeleteAgent400> + | OpenAiClientError<"DeleteAgent401", DeleteAgent401> + | OpenAiClientError<"DeleteAgent403", DeleteAgent403> + | OpenAiClientError<"DeleteAgent404", DeleteAgent404> + | OpenAiClientError<"DeleteAgent409", DeleteAgent409> + | OpenAiClientError<"DeleteAgent500", DeleteAgent500> + | OpenAiClientError<"DeleteAgent503", DeleteAgent503> + > + ( + agentId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgent400", DeleteAgent400> + | OpenAiClientError<"DeleteAgent401", DeleteAgent401> + | OpenAiClientError<"DeleteAgent403", DeleteAgent403> + | OpenAiClientError<"DeleteAgent404", DeleteAgent404> + | OpenAiClientError<"DeleteAgent409", DeleteAgent409> + | OpenAiClientError<"DeleteAgent500", DeleteAgent500> + | OpenAiClientError<"DeleteAgent503", DeleteAgent503> + > + } + /** + * Lists reusable environment templates without returning confidential values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + */ + readonly "listAgentEnvironmentTemplates": { + ( + options: { + readonly params?: typeof ListAgentEnvironmentTemplatesParams.Encoded | undefined + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentEnvironmentTemplates400", ListAgentEnvironmentTemplates400> + | OpenAiClientError<"ListAgentEnvironmentTemplates401", ListAgentEnvironmentTemplates401> + | OpenAiClientError<"ListAgentEnvironmentTemplates403", ListAgentEnvironmentTemplates403> + | OpenAiClientError<"ListAgentEnvironmentTemplates404", ListAgentEnvironmentTemplates404> + | OpenAiClientError<"ListAgentEnvironmentTemplates500", ListAgentEnvironmentTemplates500> + | OpenAiClientError<"ListAgentEnvironmentTemplates503", ListAgentEnvironmentTemplates503> + > + ( + options: { + readonly params?: typeof ListAgentEnvironmentTemplatesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentEnvironmentTemplates400", ListAgentEnvironmentTemplates400> + | OpenAiClientError<"ListAgentEnvironmentTemplates401", ListAgentEnvironmentTemplates401> + | OpenAiClientError<"ListAgentEnvironmentTemplates403", ListAgentEnvironmentTemplates403> + | OpenAiClientError<"ListAgentEnvironmentTemplates404", ListAgentEnvironmentTemplates404> + | OpenAiClientError<"ListAgentEnvironmentTemplates500", ListAgentEnvironmentTemplates500> + | OpenAiClientError<"ListAgentEnvironmentTemplates503", ListAgentEnvironmentTemplates503> + > + } + /** + * Creates reusable environment configuration without returning confidential setup commands or environment values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + */ + readonly "createAgentEnvironmentTemplate": { + ( + options: { readonly payload: typeof CreateAgentEnvironmentTemplateRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentEnvironmentTemplate400", CreateAgentEnvironmentTemplate400> + | OpenAiClientError<"CreateAgentEnvironmentTemplate401", CreateAgentEnvironmentTemplate401> + | OpenAiClientError<"CreateAgentEnvironmentTemplate403", CreateAgentEnvironmentTemplate403> + | OpenAiClientError<"CreateAgentEnvironmentTemplate404", CreateAgentEnvironmentTemplate404> + | OpenAiClientError<"CreateAgentEnvironmentTemplate500", CreateAgentEnvironmentTemplate500> + | OpenAiClientError<"CreateAgentEnvironmentTemplate503", CreateAgentEnvironmentTemplate503> + > + ( + options: { + readonly payload: typeof CreateAgentEnvironmentTemplateRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentEnvironmentTemplate400", CreateAgentEnvironmentTemplate400> + | OpenAiClientError<"CreateAgentEnvironmentTemplate401", CreateAgentEnvironmentTemplate401> + | OpenAiClientError<"CreateAgentEnvironmentTemplate403", CreateAgentEnvironmentTemplate403> + | OpenAiClientError<"CreateAgentEnvironmentTemplate404", CreateAgentEnvironmentTemplate404> + | OpenAiClientError<"CreateAgentEnvironmentTemplate500", CreateAgentEnvironmentTemplate500> + | OpenAiClientError<"CreateAgentEnvironmentTemplate503", CreateAgentEnvironmentTemplate503> + > + } + /** + * Retrieves reusable environment configuration without returning confidential values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + */ + readonly "retrieveAgentEnvironmentTemplate": { + ( + environmentTemplateId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate400", RetrieveAgentEnvironmentTemplate400> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate401", RetrieveAgentEnvironmentTemplate401> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate403", RetrieveAgentEnvironmentTemplate403> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate404", RetrieveAgentEnvironmentTemplate404> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate500", RetrieveAgentEnvironmentTemplate500> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate503", RetrieveAgentEnvironmentTemplate503> + > + ( + environmentTemplateId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate400", RetrieveAgentEnvironmentTemplate400> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate401", RetrieveAgentEnvironmentTemplate401> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate403", RetrieveAgentEnvironmentTemplate403> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate404", RetrieveAgentEnvironmentTemplate404> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate500", RetrieveAgentEnvironmentTemplate500> + | OpenAiClientError<"RetrieveAgentEnvironmentTemplate503", RetrieveAgentEnvironmentTemplate503> + > + } + /** + * Updates reusable environment configuration without returning confidential values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + */ + readonly "updateAgentEnvironmentTemplate": { + ( + environmentTemplateId: string, + options: { readonly payload: typeof UpdateAgentEnvironmentTemplateRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateAgentEnvironmentTemplate400", UpdateAgentEnvironmentTemplate400> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate401", UpdateAgentEnvironmentTemplate401> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate403", UpdateAgentEnvironmentTemplate403> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate404", UpdateAgentEnvironmentTemplate404> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate409", UpdateAgentEnvironmentTemplate409> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate500", UpdateAgentEnvironmentTemplate500> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate503", UpdateAgentEnvironmentTemplate503> + > + ( + environmentTemplateId: string, + options: { + readonly payload: typeof UpdateAgentEnvironmentTemplateRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateAgentEnvironmentTemplate400", UpdateAgentEnvironmentTemplate400> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate401", UpdateAgentEnvironmentTemplate401> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate403", UpdateAgentEnvironmentTemplate403> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate404", UpdateAgentEnvironmentTemplate404> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate409", UpdateAgentEnvironmentTemplate409> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate500", UpdateAgentEnvironmentTemplate500> + | OpenAiClientError<"UpdateAgentEnvironmentTemplate503", UpdateAgentEnvironmentTemplate503> + > + } + /** + * Deletes reusable environment configuration and all confidential template inputs. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup). + */ + readonly "deleteAgentEnvironmentTemplate": { + ( + environmentTemplateId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgentEnvironmentTemplate400", DeleteAgentEnvironmentTemplate400> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate401", DeleteAgentEnvironmentTemplate401> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate403", DeleteAgentEnvironmentTemplate403> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate404", DeleteAgentEnvironmentTemplate404> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate409", DeleteAgentEnvironmentTemplate409> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate500", DeleteAgentEnvironmentTemplate500> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate503", DeleteAgentEnvironmentTemplate503> + > + ( + environmentTemplateId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgentEnvironmentTemplate400", DeleteAgentEnvironmentTemplate400> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate401", DeleteAgentEnvironmentTemplate401> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate403", DeleteAgentEnvironmentTemplate403> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate404", DeleteAgentEnvironmentTemplate404> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate409", DeleteAgentEnvironmentTemplate409> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate500", DeleteAgentEnvironmentTemplate500> + | OpenAiClientError<"DeleteAgentEnvironmentTemplate503", DeleteAgentEnvironmentTemplate503> + > + } + /** + * Lists managed agent sessions using ID-based pagination and the requested sort order. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + */ + readonly "listAgentSessions": { + ( + options: { readonly params?: typeof ListAgentSessionsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessions400", ListAgentSessions400> + | OpenAiClientError<"ListAgentSessions401", ListAgentSessions401> + | OpenAiClientError<"ListAgentSessions403", ListAgentSessions403> + | OpenAiClientError<"ListAgentSessions404", ListAgentSessions404> + | OpenAiClientError<"ListAgentSessions409", ListAgentSessions409> + | OpenAiClientError<"ListAgentSessions500", ListAgentSessions500> + | OpenAiClientError<"ListAgentSessions503", ListAgentSessions503> + > + ( + options: { + readonly params?: typeof ListAgentSessionsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessions400", ListAgentSessions400> + | OpenAiClientError<"ListAgentSessions401", ListAgentSessions401> + | OpenAiClientError<"ListAgentSessions403", ListAgentSessions403> + | OpenAiClientError<"ListAgentSessions404", ListAgentSessions404> + | OpenAiClientError<"ListAgentSessions409", ListAgentSessions409> + | OpenAiClientError<"ListAgentSessions500", ListAgentSessions500> + | OpenAiClientError<"ListAgentSessions503", ListAgentSessions503> + > + } + /** + * Creates a managed agent session, optionally submits initial input, and returns the session or streams its events when stream is true. See [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + */ + readonly "createAgentSession": { + ( + options: { readonly payload: typeof CreateAgentSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentSession400", CreateAgentSession400> + | OpenAiClientError<"CreateAgentSession401", CreateAgentSession401> + | OpenAiClientError<"CreateAgentSession403", CreateAgentSession403> + | OpenAiClientError<"CreateAgentSession404", CreateAgentSession404> + | OpenAiClientError<"CreateAgentSession409", CreateAgentSession409> + | OpenAiClientError<"CreateAgentSession429", CreateAgentSession429> + | OpenAiClientError<"CreateAgentSession500", CreateAgentSession500> + | OpenAiClientError<"CreateAgentSession503", CreateAgentSession503> + > + ( + options: { readonly payload: typeof CreateAgentSessionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentSession400", CreateAgentSession400> + | OpenAiClientError<"CreateAgentSession401", CreateAgentSession401> + | OpenAiClientError<"CreateAgentSession403", CreateAgentSession403> + | OpenAiClientError<"CreateAgentSession404", CreateAgentSession404> + | OpenAiClientError<"CreateAgentSession409", CreateAgentSession409> + | OpenAiClientError<"CreateAgentSession429", CreateAgentSession429> + | OpenAiClientError<"CreateAgentSession500", CreateAgentSession500> + | OpenAiClientError<"CreateAgentSession503", CreateAgentSession503> + > + } + /** + * Creates a managed agent session, optionally submits initial input, and returns the session or streams its events when stream is true. See [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions). + */ + readonly "createAgentSessionSse": ( + options: { readonly payload: typeof CreateAgentSessionRequestJson.Encoded } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: CreateAgentSession201Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof CreateAgentSession201Sse.DecodingServices + > + /** + * Retrieves the current state of a managed agent session. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + */ + readonly "retrieveAgentSession": { + ( + sessionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSession400", RetrieveAgentSession400> + | OpenAiClientError<"RetrieveAgentSession401", RetrieveAgentSession401> + | OpenAiClientError<"RetrieveAgentSession403", RetrieveAgentSession403> + | OpenAiClientError<"RetrieveAgentSession404", RetrieveAgentSession404> + | OpenAiClientError<"RetrieveAgentSession409", RetrieveAgentSession409> + | OpenAiClientError<"RetrieveAgentSession500", RetrieveAgentSession500> + | OpenAiClientError<"RetrieveAgentSession503", RetrieveAgentSession503> + > + ( + sessionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSession400", RetrieveAgentSession400> + | OpenAiClientError<"RetrieveAgentSession401", RetrieveAgentSession401> + | OpenAiClientError<"RetrieveAgentSession403", RetrieveAgentSession403> + | OpenAiClientError<"RetrieveAgentSession404", RetrieveAgentSession404> + | OpenAiClientError<"RetrieveAgentSession409", RetrieveAgentSession409> + | OpenAiClientError<"RetrieveAgentSession500", RetrieveAgentSession500> + | OpenAiClientError<"RetrieveAgentSession503", RetrieveAgentSession503> + > + } + /** + * Updates session metadata, model, reasoning effort, or service tier. Model settings apply to subsequent turns. Omitted fields are unchanged. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + */ + readonly "updateAgentSession": { + ( + sessionId: string, + options: { readonly payload: typeof UpdateAgentSessionRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateAgentSession400", UpdateAgentSession400> + | OpenAiClientError<"UpdateAgentSession401", UpdateAgentSession401> + | OpenAiClientError<"UpdateAgentSession403", UpdateAgentSession403> + | OpenAiClientError<"UpdateAgentSession404", UpdateAgentSession404> + | OpenAiClientError<"UpdateAgentSession409", UpdateAgentSession409> + | OpenAiClientError<"UpdateAgentSession500", UpdateAgentSession500> + | OpenAiClientError<"UpdateAgentSession503", UpdateAgentSession503> + > + ( + sessionId: string, + options: { readonly payload: typeof UpdateAgentSessionRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateAgentSession400", UpdateAgentSession400> + | OpenAiClientError<"UpdateAgentSession401", UpdateAgentSession401> + | OpenAiClientError<"UpdateAgentSession403", UpdateAgentSession403> + | OpenAiClientError<"UpdateAgentSession404", UpdateAgentSession404> + | OpenAiClientError<"UpdateAgentSession409", UpdateAgentSession409> + | OpenAiClientError<"UpdateAgentSession500", UpdateAgentSession500> + | OpenAiClientError<"UpdateAgentSession503", UpdateAgentSession503> + > + } + /** + * Removes a managed agent session from the public API and returns a deletion confirmation. If backend execution has ended, deletion can cancel a still-open public turn and abandon unpublished outputs. Running execution must be cancelled first. Physical cleanup may continue asynchronously. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage). + */ + readonly "deleteAgentSession": { + ( + sessionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgentSession400", DeleteAgentSession400> + | OpenAiClientError<"DeleteAgentSession401", DeleteAgentSession401> + | OpenAiClientError<"DeleteAgentSession403", DeleteAgentSession403> + | OpenAiClientError<"DeleteAgentSession404", DeleteAgentSession404> + | OpenAiClientError<"DeleteAgentSession409", DeleteAgentSession409> + | OpenAiClientError<"DeleteAgentSession500", DeleteAgentSession500> + | OpenAiClientError<"DeleteAgentSession503", DeleteAgentSession503> + > + ( + sessionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgentSession400", DeleteAgentSession400> + | OpenAiClientError<"DeleteAgentSession401", DeleteAgentSession401> + | OpenAiClientError<"DeleteAgentSession403", DeleteAgentSession403> + | OpenAiClientError<"DeleteAgentSession404", DeleteAgentSession404> + | OpenAiClientError<"DeleteAgentSession409", DeleteAgentSession409> + | OpenAiClientError<"DeleteAgentSession500", DeleteAgentSession500> + | OpenAiClientError<"DeleteAgentSession503", DeleteAgentSession503> + > + } + /** + * Lists immutable artifacts published by completed hosted session turns. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + */ + readonly "listAgentSessionArtifacts": { + ( + sessionId: string, + options: { readonly params?: typeof ListAgentSessionArtifactsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionArtifacts400", ListAgentSessionArtifacts400> + | OpenAiClientError<"ListAgentSessionArtifacts401", ListAgentSessionArtifacts401> + | OpenAiClientError<"ListAgentSessionArtifacts403", ListAgentSessionArtifacts403> + | OpenAiClientError<"ListAgentSessionArtifacts404", ListAgentSessionArtifacts404> + | OpenAiClientError<"ListAgentSessionArtifacts409", ListAgentSessionArtifacts409> + | OpenAiClientError<"ListAgentSessionArtifacts500", ListAgentSessionArtifacts500> + | OpenAiClientError<"ListAgentSessionArtifacts503", ListAgentSessionArtifacts503> + > + ( + sessionId: string, + options: { + readonly params?: typeof ListAgentSessionArtifactsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionArtifacts400", ListAgentSessionArtifacts400> + | OpenAiClientError<"ListAgentSessionArtifacts401", ListAgentSessionArtifacts401> + | OpenAiClientError<"ListAgentSessionArtifacts403", ListAgentSessionArtifacts403> + | OpenAiClientError<"ListAgentSessionArtifacts404", ListAgentSessionArtifacts404> + | OpenAiClientError<"ListAgentSessionArtifacts409", ListAgentSessionArtifacts409> + | OpenAiClientError<"ListAgentSessionArtifacts500", ListAgentSessionArtifacts500> + | OpenAiClientError<"ListAgentSessionArtifacts503", ListAgentSessionArtifacts503> + > + } + /** + * Retrieves immutable metadata for one durable session artifact. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + */ + readonly "retrieveAgentSessionArtifact": { + ( + sessionId: string, + artifactId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionArtifact400", RetrieveAgentSessionArtifact400> + | OpenAiClientError<"RetrieveAgentSessionArtifact401", RetrieveAgentSessionArtifact401> + | OpenAiClientError<"RetrieveAgentSessionArtifact403", RetrieveAgentSessionArtifact403> + | OpenAiClientError<"RetrieveAgentSessionArtifact404", RetrieveAgentSessionArtifact404> + | OpenAiClientError<"RetrieveAgentSessionArtifact409", RetrieveAgentSessionArtifact409> + | OpenAiClientError<"RetrieveAgentSessionArtifact500", RetrieveAgentSessionArtifact500> + | OpenAiClientError<"RetrieveAgentSessionArtifact503", RetrieveAgentSessionArtifact503> + > + ( + sessionId: string, + artifactId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionArtifact400", RetrieveAgentSessionArtifact400> + | OpenAiClientError<"RetrieveAgentSessionArtifact401", RetrieveAgentSessionArtifact401> + | OpenAiClientError<"RetrieveAgentSessionArtifact403", RetrieveAgentSessionArtifact403> + | OpenAiClientError<"RetrieveAgentSessionArtifact404", RetrieveAgentSessionArtifact404> + | OpenAiClientError<"RetrieveAgentSessionArtifact409", RetrieveAgentSessionArtifact409> + | OpenAiClientError<"RetrieveAgentSessionArtifact500", RetrieveAgentSessionArtifact500> + | OpenAiClientError<"RetrieveAgentSessionArtifact503", RetrieveAgentSessionArtifact503> + > + } + /** + * Deletes an immutable session artifact without deleting its live environment file or original Files API object. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + */ + readonly "deleteAgentSessionArtifact": { + ( + sessionId: string, + artifactId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgentSessionArtifact400", DeleteAgentSessionArtifact400> + | OpenAiClientError<"DeleteAgentSessionArtifact401", DeleteAgentSessionArtifact401> + | OpenAiClientError<"DeleteAgentSessionArtifact403", DeleteAgentSessionArtifact403> + | OpenAiClientError<"DeleteAgentSessionArtifact404", DeleteAgentSessionArtifact404> + | OpenAiClientError<"DeleteAgentSessionArtifact409", DeleteAgentSessionArtifact409> + | OpenAiClientError<"DeleteAgentSessionArtifact500", DeleteAgentSessionArtifact500> + | OpenAiClientError<"DeleteAgentSessionArtifact503", DeleteAgentSessionArtifact503> + > + ( + sessionId: string, + artifactId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteAgentSessionArtifact400", DeleteAgentSessionArtifact400> + | OpenAiClientError<"DeleteAgentSessionArtifact401", DeleteAgentSessionArtifact401> + | OpenAiClientError<"DeleteAgentSessionArtifact403", DeleteAgentSessionArtifact403> + | OpenAiClientError<"DeleteAgentSessionArtifact404", DeleteAgentSessionArtifact404> + | OpenAiClientError<"DeleteAgentSessionArtifact409", DeleteAgentSessionArtifact409> + | OpenAiClientError<"DeleteAgentSessionArtifact500", DeleteAgentSessionArtifact500> + | OpenAiClientError<"DeleteAgentSessionArtifact503", DeleteAgentSessionArtifact503> + > + } + /** + * Downloads immutable session artifact bytes after the execution environment expires. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + */ + readonly "retrieveAgentSessionArtifactContent": { + ( + sessionId: string, + artifactId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionArtifactContent400", RetrieveAgentSessionArtifactContent400> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent401", RetrieveAgentSessionArtifactContent401> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent403", RetrieveAgentSessionArtifactContent403> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent404", RetrieveAgentSessionArtifactContent404> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent409", RetrieveAgentSessionArtifactContent409> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent500", RetrieveAgentSessionArtifactContent500> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent503", RetrieveAgentSessionArtifactContent503> + > + ( + sessionId: string, + artifactId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionArtifactContent400", RetrieveAgentSessionArtifactContent400> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent401", RetrieveAgentSessionArtifactContent401> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent403", RetrieveAgentSessionArtifactContent403> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent404", RetrieveAgentSessionArtifactContent404> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent409", RetrieveAgentSessionArtifactContent409> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent500", RetrieveAgentSessionArtifactContent500> + | OpenAiClientError<"RetrieveAgentSessionArtifactContent503", RetrieveAgentSessionArtifactContent503> + > + } + /** + * Downloads immutable session artifact bytes after the execution environment expires. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts). + */ + readonly "retrieveAgentSessionArtifactContentStream": ( + sessionId: string, + artifactId: string + ) => Stream.Stream + /** + * Streams live events for an agent session. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + */ + readonly "listAgentSessionEvents": { + ( + sessionId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionEvents400", ListAgentSessionEvents400> + | OpenAiClientError<"ListAgentSessionEvents401", ListAgentSessionEvents401> + | OpenAiClientError<"ListAgentSessionEvents403", ListAgentSessionEvents403> + | OpenAiClientError<"ListAgentSessionEvents404", ListAgentSessionEvents404> + | OpenAiClientError<"ListAgentSessionEvents409", ListAgentSessionEvents409> + | OpenAiClientError<"ListAgentSessionEvents500", ListAgentSessionEvents500> + | OpenAiClientError<"ListAgentSessionEvents503", ListAgentSessionEvents503> + > + ( + sessionId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionEvents400", ListAgentSessionEvents400> + | OpenAiClientError<"ListAgentSessionEvents401", ListAgentSessionEvents401> + | OpenAiClientError<"ListAgentSessionEvents403", ListAgentSessionEvents403> + | OpenAiClientError<"ListAgentSessionEvents404", ListAgentSessionEvents404> + | OpenAiClientError<"ListAgentSessionEvents409", ListAgentSessionEvents409> + | OpenAiClientError<"ListAgentSessionEvents500", ListAgentSessionEvents500> + | OpenAiClientError<"ListAgentSessionEvents503", ListAgentSessionEvents503> + > + } + /** + * Streams live events for an agent session. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + */ + readonly "listAgentSessionEventsSse": ( + sessionId: string + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: ListAgentSessionEvents200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof ListAgentSessionEvents200Sse.DecodingServices + > + /** + * Submits message, cancellation, tool-result, or computer-use approval-response events to a managed agent session. Cancellation can recover a still-open turn whose backend execution has ended by marking it cancelled and abandoning unpublished outputs. Saved results, published files, and existing terminal outcomes are preserved. HTTP 202 confirms acceptance, not durable completion. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events). + */ + readonly "createAgentSessionEvents": { + ( + sessionId: string, + options: { + readonly params?: typeof CreateAgentSessionEventsParams.Encoded | undefined + readonly payload: typeof CreateAgentSessionEventsRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentSessionEvents400", CreateAgentSessionEvents400> + | OpenAiClientError<"CreateAgentSessionEvents401", CreateAgentSessionEvents401> + | OpenAiClientError<"CreateAgentSessionEvents403", CreateAgentSessionEvents403> + | OpenAiClientError<"CreateAgentSessionEvents404", CreateAgentSessionEvents404> + | OpenAiClientError<"CreateAgentSessionEvents409", CreateAgentSessionEvents409> + | OpenAiClientError<"CreateAgentSessionEvents500", CreateAgentSessionEvents500> + | OpenAiClientError<"CreateAgentSessionEvents503", CreateAgentSessionEvents503> + > + ( + sessionId: string, + options: { + readonly params?: typeof CreateAgentSessionEventsParams.Encoded | undefined + readonly payload: typeof CreateAgentSessionEventsRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateAgentSessionEvents400", CreateAgentSessionEvents400> + | OpenAiClientError<"CreateAgentSessionEvents401", CreateAgentSessionEvents401> + | OpenAiClientError<"CreateAgentSessionEvents403", CreateAgentSessionEvents403> + | OpenAiClientError<"CreateAgentSessionEvents404", CreateAgentSessionEvents404> + | OpenAiClientError<"CreateAgentSessionEvents409", CreateAgentSessionEvents409> + | OpenAiClientError<"CreateAgentSessionEvents500", CreateAgentSessionEvents500> + | OpenAiClientError<"CreateAgentSessionEvents503", CreateAgentSessionEvents503> + > + } + /** + * Lists items produced by the session's root agent, including its interactions with subagents. Each subagent has its own item history. See [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability). + */ + readonly "listAgentSessionItems": { + ( + sessionId: string, + options: { readonly params?: typeof ListAgentSessionItemsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionItems400", ListAgentSessionItems400> + | OpenAiClientError<"ListAgentSessionItems401", ListAgentSessionItems401> + | OpenAiClientError<"ListAgentSessionItems403", ListAgentSessionItems403> + | OpenAiClientError<"ListAgentSessionItems404", ListAgentSessionItems404> + | OpenAiClientError<"ListAgentSessionItems409", ListAgentSessionItems409> + | OpenAiClientError<"ListAgentSessionItems500", ListAgentSessionItems500> + | OpenAiClientError<"ListAgentSessionItems503", ListAgentSessionItems503> + > + ( + sessionId: string, + options: { + readonly params?: typeof ListAgentSessionItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionItems400", ListAgentSessionItems400> + | OpenAiClientError<"ListAgentSessionItems401", ListAgentSessionItems401> + | OpenAiClientError<"ListAgentSessionItems403", ListAgentSessionItems403> + | OpenAiClientError<"ListAgentSessionItems404", ListAgentSessionItems404> + | OpenAiClientError<"ListAgentSessionItems409", ListAgentSessionItems409> + | OpenAiClientError<"ListAgentSessionItems500", ListAgentSessionItems500> + | OpenAiClientError<"ListAgentSessionItems503", ListAgentSessionItems503> + > + } + /** + * Lists published root-turn traces as OTLP JSON, ordered by turn creation time and ID. Unpublished traces are skipped. Each page returns data available when read; it does not wait for late traces. Trace reads and the JSON response are limited to 16 MiB per request. If the limit is exceeded, request fewer traces. + */ + readonly "listAgentSessionTraces": { + ( + sessionId: string, + options: { readonly params?: typeof ListAgentSessionTracesParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionTraces400", ListAgentSessionTraces400> + | OpenAiClientError<"ListAgentSessionTraces401", ListAgentSessionTraces401> + | OpenAiClientError<"ListAgentSessionTraces403", ListAgentSessionTraces403> + | OpenAiClientError<"ListAgentSessionTraces404", ListAgentSessionTraces404> + | OpenAiClientError<"ListAgentSessionTraces409", ListAgentSessionTraces409> + | OpenAiClientError<"ListAgentSessionTraces500", ListAgentSessionTraces500> + | OpenAiClientError<"ListAgentSessionTraces503", ListAgentSessionTraces503> + > + ( + sessionId: string, + options: { + readonly params?: typeof ListAgentSessionTracesParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionTraces400", ListAgentSessionTraces400> + | OpenAiClientError<"ListAgentSessionTraces401", ListAgentSessionTraces401> + | OpenAiClientError<"ListAgentSessionTraces403", ListAgentSessionTraces403> + | OpenAiClientError<"ListAgentSessionTraces404", ListAgentSessionTraces404> + | OpenAiClientError<"ListAgentSessionTraces409", ListAgentSessionTraces409> + | OpenAiClientError<"ListAgentSessionTraces500", ListAgentSessionTraces500> + | OpenAiClientError<"ListAgentSessionTraces503", ListAgentSessionTraces503> + > + } + /** + * Lists turns by creation time and turn ID. The after cursor is exclusive in the selected order. See [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns). + */ + readonly "listAgentSessionTurns": { + ( + sessionId: string, + options: { readonly params?: typeof ListAgentSessionTurnsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionTurns400", ListAgentSessionTurns400> + | OpenAiClientError<"ListAgentSessionTurns401", ListAgentSessionTurns401> + | OpenAiClientError<"ListAgentSessionTurns403", ListAgentSessionTurns403> + | OpenAiClientError<"ListAgentSessionTurns404", ListAgentSessionTurns404> + | OpenAiClientError<"ListAgentSessionTurns409", ListAgentSessionTurns409> + | OpenAiClientError<"ListAgentSessionTurns500", ListAgentSessionTurns500> + | OpenAiClientError<"ListAgentSessionTurns503", ListAgentSessionTurns503> + > + ( + sessionId: string, + options: { + readonly params?: typeof ListAgentSessionTurnsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionTurns400", ListAgentSessionTurns400> + | OpenAiClientError<"ListAgentSessionTurns401", ListAgentSessionTurns401> + | OpenAiClientError<"ListAgentSessionTurns403", ListAgentSessionTurns403> + | OpenAiClientError<"ListAgentSessionTurns404", ListAgentSessionTurns404> + | OpenAiClientError<"ListAgentSessionTurns409", ListAgentSessionTurns409> + | OpenAiClientError<"ListAgentSessionTurns500", ListAgentSessionTurns500> + | OpenAiClientError<"ListAgentSessionTurns503", ListAgentSessionTurns503> + > + } + /** + * Retrieves a turn's current status, timestamps, usage, and error. Returns 404 if the turn does not belong to the session. See [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns). + */ + readonly "retrieveAgentSessionTurn": { + ( + sessionId: string, + turnId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionTurn400", RetrieveAgentSessionTurn400> + | OpenAiClientError<"RetrieveAgentSessionTurn401", RetrieveAgentSessionTurn401> + | OpenAiClientError<"RetrieveAgentSessionTurn403", RetrieveAgentSessionTurn403> + | OpenAiClientError<"RetrieveAgentSessionTurn404", RetrieveAgentSessionTurn404> + | OpenAiClientError<"RetrieveAgentSessionTurn409", RetrieveAgentSessionTurn409> + | OpenAiClientError<"RetrieveAgentSessionTurn500", RetrieveAgentSessionTurn500> + | OpenAiClientError<"RetrieveAgentSessionTurn503", RetrieveAgentSessionTurn503> + > + ( + sessionId: string, + turnId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveAgentSessionTurn400", RetrieveAgentSessionTurn400> + | OpenAiClientError<"RetrieveAgentSessionTurn401", RetrieveAgentSessionTurn401> + | OpenAiClientError<"RetrieveAgentSessionTurn403", RetrieveAgentSessionTurn403> + | OpenAiClientError<"RetrieveAgentSessionTurn404", RetrieveAgentSessionTurn404> + | OpenAiClientError<"RetrieveAgentSessionTurn409", RetrieveAgentSessionTurn409> + | OpenAiClientError<"RetrieveAgentSessionTurn500", RetrieveAgentSessionTurn500> + | OpenAiClientError<"RetrieveAgentSessionTurn503", RetrieveAgentSessionTurn503> + > + } + /** + * Lists items belonging to one root-agent turn, including its interactions with subagents. See [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability). + */ + readonly "listAgentSessionTurnItems": { + ( + sessionId: string, + turnId: string, + options: { readonly params?: typeof ListAgentSessionTurnItemsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionTurnItems400", ListAgentSessionTurnItems400> + | OpenAiClientError<"ListAgentSessionTurnItems401", ListAgentSessionTurnItems401> + | OpenAiClientError<"ListAgentSessionTurnItems403", ListAgentSessionTurnItems403> + | OpenAiClientError<"ListAgentSessionTurnItems404", ListAgentSessionTurnItems404> + | OpenAiClientError<"ListAgentSessionTurnItems409", ListAgentSessionTurnItems409> + | OpenAiClientError<"ListAgentSessionTurnItems500", ListAgentSessionTurnItems500> + | OpenAiClientError<"ListAgentSessionTurnItems503", ListAgentSessionTurnItems503> + > + ( + sessionId: string, + turnId: string, + options: { + readonly params?: typeof ListAgentSessionTurnItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListAgentSessionTurnItems400", ListAgentSessionTurnItems400> + | OpenAiClientError<"ListAgentSessionTurnItems401", ListAgentSessionTurnItems401> + | OpenAiClientError<"ListAgentSessionTurnItems403", ListAgentSessionTurnItems403> + | OpenAiClientError<"ListAgentSessionTurnItems404", ListAgentSessionTurnItems404> + | OpenAiClientError<"ListAgentSessionTurnItems409", ListAgentSessionTurnItems409> + | OpenAiClientError<"ListAgentSessionTurnItems500", ListAgentSessionTurnItems500> + | OpenAiClientError<"ListAgentSessionTurnItems503", ListAgentSessionTurnItems503> + > + } + /** + * Lists vaults using ID-based pagination. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "listVaults": { + ( + options: { readonly params?: typeof ListVaultsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVaults400", ListVaults400> + | OpenAiClientError<"ListVaults401", ListVaults401> + | OpenAiClientError<"ListVaults403", ListVaults403> + | OpenAiClientError<"ListVaults404", ListVaults404> + | OpenAiClientError<"ListVaults409", ListVaults409> + | OpenAiClientError<"ListVaults500", ListVaults500> + | OpenAiClientError<"ListVaults503", ListVaults503> + > + ( + options: + | { readonly params?: typeof ListVaultsParams.Encoded | undefined; readonly config?: Config | undefined } + | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVaults400", ListVaults400> + | OpenAiClientError<"ListVaults401", ListVaults401> + | OpenAiClientError<"ListVaults403", ListVaults403> + | OpenAiClientError<"ListVaults404", ListVaults404> + | OpenAiClientError<"ListVaults409", ListVaults409> + | OpenAiClientError<"ListVaults500", ListVaults500> + | OpenAiClientError<"ListVaults503", ListVaults503> + > + } + /** + * Creates a vault for the current project. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "createVault": { + ( + options: { readonly payload: typeof CreateVaultRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVault400", CreateVault400> + | OpenAiClientError<"CreateVault401", CreateVault401> + | OpenAiClientError<"CreateVault403", CreateVault403> + | OpenAiClientError<"CreateVault404", CreateVault404> + | OpenAiClientError<"CreateVault409", CreateVault409> + | OpenAiClientError<"CreateVault500", CreateVault500> + | OpenAiClientError<"CreateVault503", CreateVault503> + > + ( + options: { readonly payload: typeof CreateVaultRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVault400", CreateVault400> + | OpenAiClientError<"CreateVault401", CreateVault401> + | OpenAiClientError<"CreateVault403", CreateVault403> + | OpenAiClientError<"CreateVault404", CreateVault404> + | OpenAiClientError<"CreateVault409", CreateVault409> + | OpenAiClientError<"CreateVault500", CreateVault500> + | OpenAiClientError<"CreateVault503", CreateVault503> + > + } + /** + * Retrieves a vault by its ID. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "retrieveVault": { + ( + vaultId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVault400", RetrieveVault400> + | OpenAiClientError<"RetrieveVault401", RetrieveVault401> + | OpenAiClientError<"RetrieveVault403", RetrieveVault403> + | OpenAiClientError<"RetrieveVault404", RetrieveVault404> + | OpenAiClientError<"RetrieveVault409", RetrieveVault409> + | OpenAiClientError<"RetrieveVault500", RetrieveVault500> + | OpenAiClientError<"RetrieveVault503", RetrieveVault503> + > + ( + vaultId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVault400", RetrieveVault400> + | OpenAiClientError<"RetrieveVault401", RetrieveVault401> + | OpenAiClientError<"RetrieveVault403", RetrieveVault403> + | OpenAiClientError<"RetrieveVault404", RetrieveVault404> + | OpenAiClientError<"RetrieveVault409", RetrieveVault409> + | OpenAiClientError<"RetrieveVault500", RetrieveVault500> + | OpenAiClientError<"RetrieveVault503", RetrieveVault503> + > + } + /** + * Updates the name or metadata of an active vault. Omitted fields remain unchanged. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "updateVault": { + ( + vaultId: string, + options: { readonly payload: typeof UpdateVaultRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateVault400", UpdateVault400> + | OpenAiClientError<"UpdateVault401", UpdateVault401> + | OpenAiClientError<"UpdateVault403", UpdateVault403> + | OpenAiClientError<"UpdateVault404", UpdateVault404> + | OpenAiClientError<"UpdateVault409", UpdateVault409> + | OpenAiClientError<"UpdateVault500", UpdateVault500> + | OpenAiClientError<"UpdateVault503", UpdateVault503> + > + ( + vaultId: string, + options: { readonly payload: typeof UpdateVaultRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateVault400", UpdateVault400> + | OpenAiClientError<"UpdateVault401", UpdateVault401> + | OpenAiClientError<"UpdateVault403", UpdateVault403> + | OpenAiClientError<"UpdateVault404", UpdateVault404> + | OpenAiClientError<"UpdateVault409", UpdateVault409> + | OpenAiClientError<"UpdateVault500", UpdateVault500> + | OpenAiClientError<"UpdateVault503", UpdateVault503> + > + } + /** + * Deletes a vault and all its credentials. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "deleteVault": { + ( + vaultId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVault400", DeleteVault400> + | OpenAiClientError<"DeleteVault401", DeleteVault401> + | OpenAiClientError<"DeleteVault403", DeleteVault403> + | OpenAiClientError<"DeleteVault404", DeleteVault404> + | OpenAiClientError<"DeleteVault409", DeleteVault409> + | OpenAiClientError<"DeleteVault500", DeleteVault500> + | OpenAiClientError<"DeleteVault503", DeleteVault503> + > + ( + vaultId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVault400", DeleteVault400> + | OpenAiClientError<"DeleteVault401", DeleteVault401> + | OpenAiClientError<"DeleteVault403", DeleteVault403> + | OpenAiClientError<"DeleteVault404", DeleteVault404> + | OpenAiClientError<"DeleteVault409", DeleteVault409> + | OpenAiClientError<"DeleteVault500", DeleteVault500> + | OpenAiClientError<"DeleteVault503", DeleteVault503> + > + } + /** + * Lists a vault's credentials using ID-based pagination without returning secret values. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "listVaultCredentials": { + ( + vaultId: string, + options: { readonly params?: typeof ListVaultCredentialsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVaultCredentials400", ListVaultCredentials400> + | OpenAiClientError<"ListVaultCredentials401", ListVaultCredentials401> + | OpenAiClientError<"ListVaultCredentials403", ListVaultCredentials403> + | OpenAiClientError<"ListVaultCredentials404", ListVaultCredentials404> + | OpenAiClientError<"ListVaultCredentials409", ListVaultCredentials409> + | OpenAiClientError<"ListVaultCredentials500", ListVaultCredentials500> + | OpenAiClientError<"ListVaultCredentials503", ListVaultCredentials503> + > + ( + vaultId: string, + options: { + readonly params?: typeof ListVaultCredentialsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListVaultCredentials400", ListVaultCredentials400> + | OpenAiClientError<"ListVaultCredentials401", ListVaultCredentials401> + | OpenAiClientError<"ListVaultCredentials403", ListVaultCredentials403> + | OpenAiClientError<"ListVaultCredentials404", ListVaultCredentials404> + | OpenAiClientError<"ListVaultCredentials409", ListVaultCredentials409> + | OpenAiClientError<"ListVaultCredentials500", ListVaultCredentials500> + | OpenAiClientError<"ListVaultCredentials503", ListVaultCredentials503> + > + } + /** + * Creates a vault credential. Secret values are write-only and are never returned. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "createVaultCredential": { + ( + vaultId: string, + options: { readonly payload: typeof CreateVaultCredentialRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVaultCredential400", CreateVaultCredential400> + | OpenAiClientError<"CreateVaultCredential401", CreateVaultCredential401> + | OpenAiClientError<"CreateVaultCredential403", CreateVaultCredential403> + | OpenAiClientError<"CreateVaultCredential404", CreateVaultCredential404> + | OpenAiClientError<"CreateVaultCredential409", CreateVaultCredential409> + | OpenAiClientError<"CreateVaultCredential500", CreateVaultCredential500> + | OpenAiClientError<"CreateVaultCredential503", CreateVaultCredential503> + > + ( + vaultId: string, + options: { + readonly payload: typeof CreateVaultCredentialRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateVaultCredential400", CreateVaultCredential400> + | OpenAiClientError<"CreateVaultCredential401", CreateVaultCredential401> + | OpenAiClientError<"CreateVaultCredential403", CreateVaultCredential403> + | OpenAiClientError<"CreateVaultCredential404", CreateVaultCredential404> + | OpenAiClientError<"CreateVaultCredential409", CreateVaultCredential409> + | OpenAiClientError<"CreateVaultCredential500", CreateVaultCredential500> + | OpenAiClientError<"CreateVaultCredential503", CreateVaultCredential503> + > + } + /** + * Retrieves vault credential metadata without returning secret values. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "retrieveVaultCredential": { + ( + vaultId: string, + credentialId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVaultCredential400", RetrieveVaultCredential400> + | OpenAiClientError<"RetrieveVaultCredential401", RetrieveVaultCredential401> + | OpenAiClientError<"RetrieveVaultCredential403", RetrieveVaultCredential403> + | OpenAiClientError<"RetrieveVaultCredential404", RetrieveVaultCredential404> + | OpenAiClientError<"RetrieveVaultCredential409", RetrieveVaultCredential409> + | OpenAiClientError<"RetrieveVaultCredential500", RetrieveVaultCredential500> + | OpenAiClientError<"RetrieveVaultCredential503", RetrieveVaultCredential503> + > + ( + vaultId: string, + credentialId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveVaultCredential400", RetrieveVaultCredential400> + | OpenAiClientError<"RetrieveVaultCredential401", RetrieveVaultCredential401> + | OpenAiClientError<"RetrieveVaultCredential403", RetrieveVaultCredential403> + | OpenAiClientError<"RetrieveVaultCredential404", RetrieveVaultCredential404> + | OpenAiClientError<"RetrieveVaultCredential409", RetrieveVaultCredential409> + | OpenAiClientError<"RetrieveVaultCredential500", RetrieveVaultCredential500> + | OpenAiClientError<"RetrieveVaultCredential503", RetrieveVaultCredential503> + > + } + /** + * Updates credential metadata or rotates its write-only secret. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "rotateVaultCredential": { + ( + vaultId: string, + credentialId: string, + options: { readonly payload: typeof RotateVaultCredentialRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RotateVaultCredential400", RotateVaultCredential400> + | OpenAiClientError<"RotateVaultCredential401", RotateVaultCredential401> + | OpenAiClientError<"RotateVaultCredential403", RotateVaultCredential403> + | OpenAiClientError<"RotateVaultCredential404", RotateVaultCredential404> + | OpenAiClientError<"RotateVaultCredential409", RotateVaultCredential409> + | OpenAiClientError<"RotateVaultCredential500", RotateVaultCredential500> + | OpenAiClientError<"RotateVaultCredential503", RotateVaultCredential503> + > + ( + vaultId: string, + credentialId: string, + options: { + readonly payload: typeof RotateVaultCredentialRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RotateVaultCredential400", RotateVaultCredential400> + | OpenAiClientError<"RotateVaultCredential401", RotateVaultCredential401> + | OpenAiClientError<"RotateVaultCredential403", RotateVaultCredential403> + | OpenAiClientError<"RotateVaultCredential404", RotateVaultCredential404> + | OpenAiClientError<"RotateVaultCredential409", RotateVaultCredential409> + | OpenAiClientError<"RotateVaultCredential500", RotateVaultCredential500> + | OpenAiClientError<"RotateVaultCredential503", RotateVaultCredential503> + > + } + /** + * Deletes a vault credential. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults). + */ + readonly "deleteVaultCredential": { + ( + vaultId: string, + credentialId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVaultCredential400", DeleteVaultCredential400> + | OpenAiClientError<"DeleteVaultCredential401", DeleteVaultCredential401> + | OpenAiClientError<"DeleteVaultCredential403", DeleteVaultCredential403> + | OpenAiClientError<"DeleteVaultCredential404", DeleteVaultCredential404> + | OpenAiClientError<"DeleteVaultCredential409", DeleteVaultCredential409> + | OpenAiClientError<"DeleteVaultCredential500", DeleteVaultCredential500> + | OpenAiClientError<"DeleteVaultCredential503", DeleteVaultCredential503> + > + ( + vaultId: string, + credentialId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteVaultCredential400", DeleteVaultCredential400> + | OpenAiClientError<"DeleteVaultCredential401", DeleteVaultCredential401> + | OpenAiClientError<"DeleteVaultCredential403", DeleteVaultCredential403> + | OpenAiClientError<"DeleteVaultCredential404", DeleteVaultCredential404> + | OpenAiClientError<"DeleteVaultCredential409", DeleteVaultCredential409> + | OpenAiClientError<"DeleteVaultCredential500", DeleteVaultCredential500> + | OpenAiClientError<"DeleteVaultCredential503", DeleteVaultCredential503> + > + } + /** + * Returns webhook endpoints for the authenticated project in newest-first order. + */ + readonly "ListWebhookEndpoints": { + ( + options: { readonly params?: typeof ListWebhookEndpointsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListWebhookEndpoints400", ListWebhookEndpoints400> + | OpenAiClientError<"ListWebhookEndpoints404", ListWebhookEndpoints404> + > + ( + options: { + readonly params?: typeof ListWebhookEndpointsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListWebhookEndpoints400", ListWebhookEndpoints400> + | OpenAiClientError<"ListWebhookEndpoints404", ListWebhookEndpoints404> + > + } + /** + * Creates a webhook endpoint for the authenticated project. + */ + readonly "CreateWebhookEndpoint": { + ( + options: { readonly payload: typeof CreateWebhookEndpointRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateWebhookEndpoint400", CreateWebhookEndpoint400> + > + ( + options: { + readonly payload: typeof CreateWebhookEndpointRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"CreateWebhookEndpoint400", CreateWebhookEndpoint400> + > + } + /** + * Retrieves a webhook endpoint for the authenticated project. + */ + readonly "RetrieveWebhookEndpoint": { + ( + webhookEndpointId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveWebhookEndpoint400", RetrieveWebhookEndpoint400> + | OpenAiClientError<"RetrieveWebhookEndpoint404", RetrieveWebhookEndpoint404> + > + ( + webhookEndpointId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RetrieveWebhookEndpoint400", RetrieveWebhookEndpoint400> + | OpenAiClientError<"RetrieveWebhookEndpoint404", RetrieveWebhookEndpoint404> + > + } + /** + * Updates a webhook endpoint for the authenticated project. + */ + readonly "UpdateWebhookEndpoint": { + ( + webhookEndpointId: string, + options: { readonly payload: typeof UpdateWebhookEndpointRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateWebhookEndpoint400", UpdateWebhookEndpoint400> + | OpenAiClientError<"UpdateWebhookEndpoint404", UpdateWebhookEndpoint404> + > + ( + webhookEndpointId: string, + options: { + readonly payload: typeof UpdateWebhookEndpointRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"UpdateWebhookEndpoint400", UpdateWebhookEndpoint400> + | OpenAiClientError<"UpdateWebhookEndpoint404", UpdateWebhookEndpoint404> + > + } + /** + * Deletes a webhook endpoint for the authenticated project. + */ + readonly "DeleteWebhookEndpoint": { + ( + webhookEndpointId: string, + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteWebhookEndpoint400", DeleteWebhookEndpoint400> + | OpenAiClientError<"DeleteWebhookEndpoint404", DeleteWebhookEndpoint404> + > + ( + webhookEndpointId: string, + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"DeleteWebhookEndpoint400", DeleteWebhookEndpoint400> + | OpenAiClientError<"DeleteWebhookEndpoint404", DeleteWebhookEndpoint404> + > + } + /** + * Rotates the signing secret for a webhook endpoint in the authenticated project. + */ + readonly "RotateWebhookEndpointSigningSecret": { + ( + webhookEndpointId: string, + options: { + readonly payload: typeof RotateWebhookEndpointSigningSecretRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RotateWebhookEndpointSigningSecret400", RotateWebhookEndpointSigningSecret400> + | OpenAiClientError<"RotateWebhookEndpointSigningSecret404", RotateWebhookEndpointSigningSecret404> + > + ( + webhookEndpointId: string, + options: { + readonly payload: typeof RotateWebhookEndpointSigningSecretRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"RotateWebhookEndpointSigningSecret400", RotateWebhookEndpointSigningSecret400> + | OpenAiClientError<"RotateWebhookEndpointSigningSecret404", RotateWebhookEndpointSigningSecret404> + > + } + /** + * Sends a sample event to a webhook endpoint for the authenticated project. + */ + readonly "TestWebhookEndpoint": { + ( + webhookEndpointId: string, + options: { readonly payload: typeof TestWebhookEndpointRequestJson.Encoded; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"TestWebhookEndpoint400", TestWebhookEndpoint400> + | OpenAiClientError<"TestWebhookEndpoint404", TestWebhookEndpoint404> + | OpenAiClientError<"TestWebhookEndpoint500", TestWebhookEndpoint500> + > + ( + webhookEndpointId: string, + options: { readonly payload: typeof TestWebhookEndpointRequestJson.Encoded; readonly config?: Config | undefined } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"TestWebhookEndpoint400", TestWebhookEndpoint400> + | OpenAiClientError<"TestWebhookEndpoint404", TestWebhookEndpoint404> + | OpenAiClientError<"TestWebhookEndpoint500", TestWebhookEndpoint500> + > + } + /** + * Returns webhook event types visible to the authenticated project. + */ + readonly "ListWebhookEventTypes": { + ( + options: { readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListWebhookEventTypes401", ListWebhookEventTypes401> + > + ( + options: { readonly config?: Config | undefined } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"ListWebhookEventTypes401", ListWebhookEventTypes401> + > + } + /** + * Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or + * [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text) + * or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call + * your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in + * [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + * or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data + * as input for the model's response. + */ + readonly "betaCreateResponse": { + ( + options: { + readonly params?: typeof BetaCreateResponseParams.Encoded | undefined + readonly payload: typeof BetaCreateResponseRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaCreateResponse400", BetaCreateResponse400> + | OpenAiClientError<"BetaCreateResponse404", BetaCreateResponse404> + | OpenAiClientError<"BetaCreateResponse429", BetaCreateResponse429> + | OpenAiClientError<"BetaCreateResponse503", BetaCreateResponse503> + > + ( + options: { + readonly params?: typeof BetaCreateResponseParams.Encoded | undefined + readonly payload: typeof BetaCreateResponseRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaCreateResponse400", BetaCreateResponse400> + | OpenAiClientError<"BetaCreateResponse404", BetaCreateResponse404> + | OpenAiClientError<"BetaCreateResponse429", BetaCreateResponse429> + | OpenAiClientError<"BetaCreateResponse503", BetaCreateResponse503> + > + } + /** + * Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or + * [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text) + * or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call + * your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in + * [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search) + * or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data + * as input for the model's response. + */ + readonly "betaCreateResponseSse": ( + options: { + readonly params?: typeof BetaCreateResponseParams.Encoded | undefined + readonly payload: typeof BetaCreateResponseRequestJson.Encoded + } + ) => Stream.Stream< + { readonly event: string; readonly id: string | undefined; readonly data: BetaCreateResponse200Sse }, + HttpClientError.HttpClientError | SchemaError | Sse.Retry | Sse.SseError, + typeof BetaCreateResponse200Sse.DecodingServices + > + /** + * Retrieves a model response with the given ID. + */ + readonly "betaGetResponse": { + ( + responseId: string, + options: { readonly params?: typeof BetaGetResponseParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaGetResponse404", BetaGetResponse404> + | OpenAiClientError<"BetaGetResponse429", BetaGetResponse429> + > + ( + responseId: string, + options: { + readonly params?: typeof BetaGetResponseParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaGetResponse404", BetaGetResponse404> + | OpenAiClientError<"BetaGetResponse429", BetaGetResponse429> + > + } + /** + * Deletes a model response with the given ID. + */ + readonly "betaDeleteResponse": { + ( + responseId: string, + options: { readonly params?: typeof BetaDeleteResponseParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaDeleteResponse404", BetaDeleteResponse404> + | OpenAiClientError<"BetaDeleteResponse429", BetaDeleteResponse429> + > + ( + responseId: string, + options: { + readonly params?: typeof BetaDeleteResponseParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaDeleteResponse404", BetaDeleteResponse404> + | OpenAiClientError<"BetaDeleteResponse429", BetaDeleteResponse429> + > + } + /** + * Cancels a model response with the given ID. Only responses created with + * the `background` parameter set to `true` can be cancelled. + * [Learn more](https://developers.openai.com/api/docs/guides/background). + */ + readonly "betaCancelResponse": { + ( + responseId: string, + options: { readonly params?: typeof BetaCancelResponseParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaCancelResponse404", BetaCancelResponse404> + | OpenAiClientError<"BetaCancelResponse429", BetaCancelResponse429> + > + ( + responseId: string, + options: { + readonly params?: typeof BetaCancelResponseParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaCancelResponse404", BetaCancelResponse404> + | OpenAiClientError<"BetaCancelResponse429", BetaCancelResponse429> + > + } + /** + * Compact a conversation. Returns a compacted response object. + * + * Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced). + */ + readonly "betaCompactconversation": { + ( + options: { + readonly params?: typeof BetaCompactconversationParams.Encoded | undefined + readonly payload: typeof BetaCompactconversationRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaCompactconversation400", BetaCompactconversation400> + | OpenAiClientError<"BetaCompactconversation429", BetaCompactconversation429> + | OpenAiClientError<"BetaCompactconversation503", BetaCompactconversation503> + > + ( + options: { + readonly params?: typeof BetaCompactconversationParams.Encoded | undefined + readonly payload: typeof BetaCompactconversationRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaCompactconversation400", BetaCompactconversation400> + | OpenAiClientError<"BetaCompactconversation429", BetaCompactconversation429> + | OpenAiClientError<"BetaCompactconversation503", BetaCompactconversation503> + > + } + /** + * Returns a list of input items for a given response. + */ + readonly "betaListInputItems": { + ( + responseId: string, + options: { readonly params?: typeof BetaListInputItemsParams.Encoded | undefined; readonly config: Config } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaListInputItems404", BetaListInputItems404> + | OpenAiClientError<"BetaListInputItems429", BetaListInputItems429> + > + ( + responseId: string, + options: { + readonly params?: typeof BetaListInputItemsParams.Encoded | undefined + readonly config?: Config | undefined + } | undefined + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaListInputItems404", BetaListInputItems404> + | OpenAiClientError<"BetaListInputItems429", BetaListInputItems429> + > + } + /** + * Returns input token counts of the request. + * + * Returns an object with `object` set to `response.input_tokens` and an `input_tokens` count. + */ + readonly "betaGetinputtokencounts": { + ( + options: { + readonly params?: typeof BetaGetinputtokencountsParams.Encoded | undefined + readonly payload: typeof BetaGetinputtokencountsRequestJson.Encoded + readonly config: Config + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaGetinputtokencounts400", BetaGetinputtokencounts400> + | OpenAiClientError<"BetaGetinputtokencounts429", BetaGetinputtokencounts429> + > + ( + options: { + readonly params?: typeof BetaGetinputtokencountsParams.Encoded | undefined + readonly payload: typeof BetaGetinputtokencountsRequestJson.Encoded + readonly config?: Config | undefined + } + ): Effect.Effect< + WithOptionalResponse, + | HttpClientError.HttpClientError + | SchemaError + | OpenAiClientError<"BetaGetinputtokencounts400", BetaGetinputtokencounts400> + | OpenAiClientError<"BetaGetinputtokencounts429", BetaGetinputtokencounts429> + > + } +} + +export interface OpenAiClientError { + readonly _tag: Tag + readonly request: HttpClientRequest.HttpClientRequest + readonly response: HttpClientResponse.HttpClientResponse + readonly cause: E +} + +class OpenAiClientErrorImpl extends Data.Error<{ + _tag: string + cause: any + request: HttpClientRequest.HttpClientRequest + response: HttpClientResponse.HttpClientResponse +}> {} + +export const OpenAiClientError = ( + tag: Tag, + cause: E, + response: HttpClientResponse.HttpClientResponse +): OpenAiClientError => + new OpenAiClientErrorImpl({ + _tag: tag, + cause, + response, + request: response.request + }) as any diff --git a/repos/effect/packages/ai/openai/src/OpenAiClient.ts b/packages/ai/openai/src/OpenAiClient.ts similarity index 92% rename from repos/effect/packages/ai/openai/src/OpenAiClient.ts rename to packages/ai/openai/src/OpenAiClient.ts index 0238e5152..73a3daeaa 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiClient.ts +++ b/packages/ai/openai/src/OpenAiClient.ts @@ -38,6 +38,8 @@ import * as Errors from "./internal/errors.ts" import { OpenAiConfig } from "./OpenAiConfig.ts" import * as OpenAiSchema from "./OpenAiSchema.ts" +const decodeResponseStreamEvent = Schema.decodeUnknownEffect(Schema.fromJsonString(OpenAiSchema.ResponseStreamEvent)) + // ============================================================================= // Service Interface // ============================================================================= @@ -88,6 +90,13 @@ export interface Service { readonly createEmbedding: ( options: typeof OpenAiSchema.CreateEmbeddingRequest.Encoded ) => Effect.Effect + + /** + * Answer decision questions using the OpenAI decisions endpoint. + */ + readonly createDecisions: ( + options: typeof OpenAiSchema.CreateDecisionsRequest.Encoded + ) => Effect.Effect } // ============================================================================= @@ -273,13 +282,15 @@ export const make = Effect.fnUntraced( ] => { const stream = response.stream.pipe( Stream.decodeText(), - Stream.pipeThroughChannel(Sse.decodeDataSchema(OpenAiSchema.ResponseStreamEvent)), + Stream.pipeThroughChannel(Sse.decode()), + // Some OpenAI-compatible servers end the stream with `data: [DONE]` + Stream.takeWhile((event) => event.data !== "[DONE]"), + Stream.mapEffect((event) => decodeResponseStreamEvent(event.data)), Stream.takeUntil((event) => - event.data.type === "response.completed" || - event.data.type === "response.incomplete" || - event.data.type === "response.failed" + event.type === "response.completed" || + event.type === "response.incomplete" || + event.type === "response.failed" ), - Stream.map((event) => event.data), Stream.catchTags({ // TODO: handle SSE retries Retry: (error) => Stream.die(error), @@ -311,31 +322,47 @@ export const make = Effect.fnUntraced( ) }) - const decodeEmbedding = HttpClientResponse.schemaBodyJson(OpenAiSchema.CreateEmbeddingResponse) + const postJson = >( + path: string, + schema: Response, + method: string + ) => { + const decode = HttpClientResponse.schemaBodyJson(schema) + return (payload: unknown): Effect.Effect => + resolveHttpClient.pipe( + Effect.flatMap((client) => + client.execute(HttpClientRequest.post(path, { + body: HttpBody.jsonUnsafe(payload) + })).pipe( + Effect.flatMap(decode), + Effect.catchTags({ + HttpClientError: (error) => Errors.mapHttpClientError(error, method), + SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, method)) + }) + ) + ), + withRedactedHeaders + ) + } + + const createEmbedding: Service["createEmbedding"] = postJson( + "/embeddings", + OpenAiSchema.CreateEmbeddingResponse, + "createEmbedding" + ) - const createEmbedding = ( - payload: typeof OpenAiSchema.CreateEmbeddingRequest.Encoded - ): Effect.Effect => - resolveHttpClient.pipe( - Effect.flatMap((client) => - client.execute(HttpClientRequest.post("/embeddings", { - body: HttpBody.jsonUnsafe(payload) - })).pipe( - Effect.flatMap(decodeEmbedding), - Effect.catchTags({ - HttpClientError: (error) => Errors.mapHttpClientError(error, "createEmbedding"), - SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createEmbedding")) - }) - ) - ), - withRedactedHeaders - ) + const createDecisions: Service["createDecisions"] = postJson( + "/decisions", + OpenAiSchema.CreateDecisionsResponse, + "createDecisions" + ) return OpenAiClient.of({ client: httpClient, createResponse, createResponseStream, - createEmbedding + createEmbedding, + createDecisions }) }, withRedactedHeaders diff --git a/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts b/packages/ai/openai/src/OpenAiClientGenerated.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts rename to packages/ai/openai/src/OpenAiClientGenerated.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiConfig.ts b/packages/ai/openai/src/OpenAiConfig.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiConfig.ts rename to packages/ai/openai/src/OpenAiConfig.ts diff --git a/packages/ai/openai/src/OpenAiDecisionModel.ts b/packages/ai/openai/src/OpenAiDecisionModel.ts new file mode 100644 index 000000000..3e5995b2e --- /dev/null +++ b/packages/ai/openai/src/OpenAiDecisionModel.ts @@ -0,0 +1,208 @@ +/** + * OpenAI implementation of DecisionModel over the Decisions API. + * + * @stability unstable + * @since 4.0.5 + */ +import * as AiError from "effect/ai/AiError" +import type * as Decision from "effect/ai/Decision" +import * as DecisionModel from "effect/ai/DecisionModel" +import * as AiModel from "effect/ai/Model" +import * as Effect from "effect/Effect" +import * as Base64 from "effect/encoding/Base64" +import * as Layer from "effect/Layer" +import * as Record from "effect/Record" +import { OpenAiClient } from "./OpenAiClient.ts" +import type * as OpenAiSchema from "./OpenAiSchema.ts" + +/** + * OpenAI decision model identifiers. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type Model = typeof OpenAiSchema.CreateDecisionsRequest.Encoded["model"] + +/** + * Creates a decision model with OpenAI provider metadata. + * + * @stability unstable + * @category constructors + * @since 4.0.5 + */ +export const model = ( + model: Model +): AiModel.Model<"openai", DecisionModel.DecisionModel, OpenAiClient> => AiModel.make("openai", model, layer({ model })) + +/** + * Builds a decision service with tolerance for two-decimal probability rounding. + * + * **Details** + * + * String state is sent as `input` unchanged, and other state is sent as JSON. + * With images, `input` becomes one user message with the state as text + * followed by the images. Byte arrays and base64 strings are sent as `data:` + * URLs, and `data:` URLs are sent unchanged. + * + * Probability criteria are appended to the question instructions, and each + * rating level is sent with its label and description. + * String instructions and descriptions are unchanged; structured values and + * null descriptions are serialized with `JSON.stringify`. + * + * **Gotchas** + * + * - Remote URLs fail with `AiError.InvalidUserInputError` because OpenAI only + * accepts inline images. + * - If OpenAI refuses any decision, the whole call fails with + * `AiError.ContentPolicyError`. The refused names are in + * `metadata.openai.refusedDecisions`. + * + * @stability unstable + * @category constructors + * @since 4.0.5 + */ +export const make = Effect.fnUntraced( + function*( + options: { readonly model: Model } + ): Effect.fn.Return { + const client = yield* OpenAiClient + return yield* DecisionModel.make({ + probabilityPrecision: 2, + supportsImages: true, + decide: Effect.fnUntraced(function*({ state, decisions, images }) { + if (images?.some((image) => image.data instanceof URL && image.data.protocol !== "data:")) { + return yield* invalidUserInput("OpenAI decisions accept base64 or data URL images, not remote URLs") + } + const questions: Array = [] + for (const [name, decision] of Object.entries(decisions)) { + questions.push(encodeQuestion(name, decision)) + } + const text = typeof state === "string" ? state : JSON.stringify(state) + const response = yield* client.createDecisions({ + model: options.model, + input: images === undefined ? text : [{ + role: "user", + content: [ + { type: "input_text", text }, + ...images.map((image) => ({ type: "input_image" as const, image_url: encodeImage(image) })) + ] + }], + questions + }) + const answers: Record = Object.create(null) + const refused: Array = [] + for (const answer of response.answers) { + switch (answer.type) { + case "refusal": + refused.push(answer.name) + break + case "choice": + answers[answer.name] = { + _tag: "Classify", + label: answer.choice, + probabilities: Record.fromIterableWith(answer.probabilities, (p) => [p.value, p.probability]), + confidence: answer.confidence + } + break + case "score": { + const decision = decisions[answer.name] + if (decision?._tag !== "Rate") break + const probabilities: Record = Object.create(null) + for (const { value, probability } of answer.probabilities) { + const level = decision.criteria[value] + if (level !== undefined) probabilities[level.label] = probability + } + answers[answer.name] = { + _tag: "Rate", + rating: answer.score, + probabilities, + confidence: answer.confidence + } + break + } + case "predicate": + answers[answer.name] = { _tag: "Probability", probability: answer.probability } + break + } + } + if (refused.length > 0) { + return yield* Effect.fail(AiError.make({ + module: "OpenAiDecisionModel", + method: "decide", + reason: new AiError.ContentPolicyError({ + description: `OpenAI refused to answer: ${refused.join(", ")}`, + metadata: { openai: { errorCode: null, errorType: null, requestId: null, refusedDecisions: refused } } + }) + })) + } + return { + answers, + usage: { inputTokens: response.usage?.input_tokens, outputTokens: response.usage?.output_tokens } + } + }) + }) + } +) + +const invalidUserInput = (description: string) => + Effect.fail(AiError.make({ + module: "OpenAiDecisionModel", + method: "decide", + reason: new AiError.InvalidUserInputError({ description }) + })) + +const encodeQuestion = ( + name: string, + decision: Decision.Any +): typeof OpenAiSchema.DecisionQuestion.Encoded => { + const instructions = encodeDescription(decision.instructions) + switch (decision._tag) { + case "Classify": { + const choices = Object.entries(decision.criteria).map(([value, description]) => ({ + value, + description: encodeDescription(description) + })) + return { type: "choice", name, instructions, choices } + } + case "Rate": + return { + type: "score", + name, + instructions, + levels: decision.criteria.map(({ label, description }) => ({ + label, + description: encodeDescription(description) + })) + } + case "Probability": { + if (decision.criteria === undefined) return { type: "predicate", name, instructions } + const { false: no, true: yes } = decision.criteria + return { + type: "predicate", + name, + instructions: `${instructions}\n\nfalse: ${encodeDescription(no)}\ntrue: ${encodeDescription(yes)}` + } + } + } +} + +const encodeDescription = (description: Decision.Description | null): string => + typeof description === "string" ? description : JSON.stringify(description) + +const encodeImage = (image: DecisionModel.Image): string => + image.data instanceof URL + ? image.data.href + : `data:${image.mediaType};base64,${typeof image.data === "string" ? image.data : Base64.encode(image.data)}` + +/** + * Provides DecisionModel using an existing OpenAiClient. + * + * @stability unstable + * @category layers + * @since 4.0.5 + */ +export const layer = ( + options: { readonly model: Model } +): Layer.Layer => + Layer.effect(DecisionModel.DecisionModel, make(options)) diff --git a/repos/effect/packages/ai/openai/src/OpenAiEmbeddingModel.ts b/packages/ai/openai/src/OpenAiEmbeddingModel.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiEmbeddingModel.ts rename to packages/ai/openai/src/OpenAiEmbeddingModel.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiError.ts b/packages/ai/openai/src/OpenAiError.ts similarity index 92% rename from repos/effect/packages/ai/openai/src/OpenAiError.ts rename to packages/ai/openai/src/OpenAiError.ts index 1f777293e..470a9ef03 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiError.ts +++ b/packages/ai/openai/src/OpenAiError.ts @@ -30,6 +30,25 @@ export type OpenAiErrorMetadata = { readonly requestId: string | null } +/** + * OpenAI-specific content policy metadata fields. + * + * **Details** + * + * Extends base error metadata with the names of decisions that OpenAI refused + * to answer. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type OpenAiContentPolicyMetadata = OpenAiErrorMetadata & { + /** + * Names of the decisions that came back as refusals. + */ + readonly refusedDecisions?: Array +} + /** * OpenAI-specific rate limit metadata fields. * @@ -132,7 +151,7 @@ declare module "effect/ai/AiError" { /** * OpenAI-specific details for the content policy response. */ - readonly openai?: OpenAiErrorMetadata | null + readonly openai?: OpenAiContentPolicyMetadata | null } /** diff --git a/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts b/packages/ai/openai/src/OpenAiLanguageModel.ts similarity index 97% rename from repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts rename to packages/ai/openai/src/OpenAiLanguageModel.ts index f84ac6377..8217553d5 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts +++ b/packages/ai/openai/src/OpenAiLanguageModel.ts @@ -76,6 +76,8 @@ type PromptCacheBreakpoint = { readonly mode: "explicit" } * Config values are merged with the config object passed to `model`, `make`, or * `layer`, with scoped context values taking precedence. * + * Explicit `include` values are merged with automatic entries without duplicates. + * * @see {@link withConfigOverride} for scoping language model request overrides * * @stability unstable @@ -130,6 +132,19 @@ export class Config extends Context.Service< * Defaults to `true` when `store` is `true`. */ readonly useItemReferences?: boolean | undefined + /** + * Override reasoning model detection. Reasoning models use the `developer` + * role for system prompts and request `reasoning.encrypted_content` when + * item references are disabled or a WebSocket connection is used. + * + * Defaults to `true` except for `gpt-3*`, `gpt-4*`, `chatgpt-*`, + * `chat-latest`, and `gpt--chat*`. Fine-tuned `ft:` models follow + * their base model. + * + * Set to `false` for deployments that do not support these defaults. + * Explicit `include` values are still preserved. + */ + readonly reasoningModel?: boolean | undefined } > >()("@effect/ai-openai/OpenAiLanguageModel/Config") {} @@ -197,9 +212,8 @@ declare module "effect/ai/Prompt" { */ readonly itemId?: string | null /** - * The encrypted content of the reasoning item - populated when a response - * is generated with `reasoning.encrypted_content` in the `include` - * parameter. + * Encrypted reasoning returned by OpenAI for `store: false` or when + * `include` contains `reasoning.encrypted_content`. */ readonly encryptedContent?: string | null } | null @@ -576,7 +590,7 @@ declare module "effect/ai/Response" { /** * The service tier reported by OpenAI for the response. */ - readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | null + readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | "ultrafast" | null /** * Prompt cache diagnostics for `prompt_cache_options.comparison_response_id`. */ @@ -662,12 +676,11 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig readonly options: LanguageModel.ProviderOptions readonly toolNameMapper: Tool.NameMapper }): Effect.fn.Return { - const include = new Set() - const capabilities = getModelCapabilities(config.model as string) + const include = new Set(config.include) const messages = yield* prepareMessages({ config, options, - capabilities, + isReasoningModel: resolveReasoningModel(config), include, toolNameMapper }) @@ -684,6 +697,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig fileIdPrefixes: _fip, strictJsonSchema: _sjs, useItemReferences: _uir, + reasoningModel: _rm, ...apiConfig } = config const request: Mutable = { @@ -821,26 +835,18 @@ export const withConfigOverride: { // Prompt Conversion // ============================================================================= -const getSystemMessageMode = (model: string): "system" | "developer" => - model.startsWith("o") || - model.startsWith("gpt-5") || - model.startsWith("codex-") || - model.startsWith("computer-use") - ? "developer" - : "system" - const prepareMessages = Effect.fnUntraced( function*>({ config, options, - capabilities, + isReasoningModel, include, toolNameMapper }: { readonly config: typeof Config.Service readonly options: LanguageModel.ProviderOptions readonly include: Set - readonly capabilities: ModelCapabilities + readonly isReasoningModel: boolean readonly toolNameMapper: Tool.NameMapper }): Effect.fn.Return, AiError.AiError> { const processedApprovalIds = new Set() @@ -875,7 +881,7 @@ const prepareMessages = Effect.fnUntraced( if (Predicate.isNotUndefined(config.top_logprobs)) { include.add("message.output_text.logprobs") } - if ((websocketMode || !useItemReferences) && capabilities.isReasoningModel) { + if ((websocketMode || !useItemReferences) && isReasoningModel) { include.add("reasoning.encrypted_content") } if (codeInterpreterTool) { @@ -892,7 +898,7 @@ const prepareMessages = Effect.fnUntraced( switch (message.role) { case "system": { messages.push({ - role: getSystemMessageMode(config.model as string), + role: isReasoningModel ? "developer" : "system", content: [{ type: "input_text", text: message.content, @@ -1556,7 +1562,7 @@ const makeResponse = Effect.fnUntraced( }) }) - const params = yield* transformToolCallParams(options.tools, part.name, toolParams) + const params = yield* transformToolCallParams(options, part.name, toolParams) parts.push({ type: "tool-call", @@ -2343,7 +2349,7 @@ const makeStreamResponse = Effect.fnUntraced( }) }) - const params = yield* transformToolCallParams(options.tools, toolName, toolParams) + const params = yield* transformToolCallParams(options, toolName, toolParams) parts.push({ type: "tool-params-end", @@ -2651,7 +2657,7 @@ const makeStreamResponse = Effect.fnUntraced( }) }) - const params = yield* transformToolCallParams(options.tools, toolCall.name, toolParams) + const params = yield* transformToolCallParams(options, toolCall.name, toolParams) parts.push({ type: "tool-params-end", @@ -3184,52 +3190,16 @@ const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: { return { type: "text" } }) -interface ModelCapabilities { - readonly isReasoningModel: boolean - readonly systemMessageMode: "remove" | "system" | "developer" - readonly supportsFlexProcessing: boolean - readonly supportsPriorityProcessing: boolean - /** - * Allow temperature, topP, logProbs when reasoningEffort is none. - */ - readonly supportsNonReasoningParameters: boolean -} - -const getModelCapabilities = (modelId: string): ModelCapabilities => { - const supportsFlexProcessing = modelId.startsWith("o3") || - modelId.startsWith("o4-mini") || - (modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat")) - - const supportsPriorityProcessing = modelId.startsWith("gpt-4") || - modelId.startsWith("gpt-5-mini") || - (modelId.startsWith("gpt-5") && - !modelId.startsWith("gpt-5-nano") && - !modelId.startsWith("gpt-5-chat")) || - modelId.startsWith("o3") || - modelId.startsWith("o4-mini") - - // Use allowlist approach: only known reasoning models should use 'developer' role - // This prevents issues with fine-tuned models, third-party models, and custom models - const isReasoningModel = modelId.startsWith("o1") || - modelId.startsWith("o3") || - modelId.startsWith("o4-mini") || - modelId.startsWith("codex-mini") || - modelId.startsWith("computer-use-preview") || - (modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat")) - - // https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility - // GPT-5.1 and GPT-5.2 support temperature, topP, logProbs when reasoningEffort is none - const supportsNonReasoningParameters = modelId.startsWith("gpt-5.1") || modelId.startsWith("gpt-5.2") - - const systemMessageMode = isReasoningModel ? "developer" : "system" +// Default new model names to reasoning without extending an allowlist. +const nonReasoningModelPattern = /^(?:gpt-[34]|chatgpt-|chat-latest|gpt-\d+(?:\.\d+)?-chat)/ - return { - supportsFlexProcessing, - supportsPriorityProcessing, - isReasoningModel, - systemMessageMode, - supportsNonReasoningParameters +const resolveReasoningModel = (config: typeof Config.Service): boolean => { + if (Predicate.isNotUndefined(config.reasoningModel)) { + return config.reasoningModel } + const model = config.model ?? "" + const baseModel = model.startsWith("ft:") ? model.slice(3) : model + return !nonReasoningModelPattern.test(baseModel) } const getApprovalRequestIdMapping = (prompt: Prompt.Prompt): ReadonlyMap => { @@ -3328,7 +3298,7 @@ const getUsage = (usage: OpenAiSchema.ResponseUsage | null | undefined): Respons } } -type ServiceTier = "default" | "auto" | "flex" | "scale" | "priority" | null +type ServiceTier = "default" | "auto" | "flex" | "scale" | "priority" | "ultrafast" | null const toFinishMetadata = (response: OpenAiSchema.Response): { readonly metadata: Response.FinishPartMetadata @@ -3340,6 +3310,7 @@ const toFinishMetadata = (response: OpenAiSchema.Response): { case "flex": case "scale": case "priority": + case "ultrafast": serviceTier = response.service_tier break } @@ -3362,20 +3333,23 @@ const toFinishMetadata = (response: OpenAiSchema.Response): { const getUsageTokenDetail = (details: unknown, key: string): number | undefined => Predicate.hasProperty(details, key) && typeof details[key] === "number" ? details[key] : undefined -const transformToolCallParams = Effect.fnUntraced(function*>( - tools: Tools, +const transformToolCallParams = Effect.fnUntraced(function*( + options: LanguageModel.ProviderOptions, toolName: string, toolParams: unknown ): Effect.fn.Return { - const tool = tools.find((tool) => tool.name === toolName) + const tool = options.tools.find((tool) => tool.name === toolName) if (Predicate.isUndefined(tool)) { + if (options.toolCallValidation === "deferred") { + return toolParams + } return yield* AiError.make({ module: "OpenAiLanguageModel", method: "makeResponse", reason: new AiError.ToolNotFoundError({ toolName, - availableTools: tools.map((tool) => tool.name) + availableTools: options.tools.map((tool) => tool.name) }) }) } diff --git a/repos/effect/packages/ai/openai/src/OpenAiSchema.ts b/packages/ai/openai/src/OpenAiSchema.ts similarity index 89% rename from repos/effect/packages/ai/openai/src/OpenAiSchema.ts rename to packages/ai/openai/src/OpenAiSchema.ts index f88b492fd..484996088 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiSchema.ts +++ b/packages/ai/openai/src/OpenAiSchema.ts @@ -1,8 +1,8 @@ /** * The `OpenAiSchema` module defines the request, response, streaming, and * embedding schemas used by the handwritten OpenAI client. These schemas are - * the transport boundary for JSON sent to and decoded from the Responses and - * embeddings endpoints. + * the transport boundary for JSON sent to and decoded from the Responses, + * embeddings, and decisions endpoints. * * @stability unstable * @since 4.0.0 @@ -1442,3 +1442,193 @@ export const CreateEmbeddingResponse = Schema.Struct({ * @since 4.0.0 */ export type CreateEmbeddingResponse = typeof CreateEmbeddingResponse.Type + +// ============================================================================= +// Decisions +// ============================================================================= + +/** + * Schema for a question sent to the OpenAI Decisions API. + * + * **Details** + * + * A `predicate` asks for the probability that a statement holds. A `choice` + * picks one of the supplied `choices`. A `score` rates against `levels`, + * ordered from lowest to highest. Each question needs a `name` that is unique + * within the request. + * + * @stability unstable + * @category schemas + * @since 4.0.5 + */ +export const DecisionQuestion = Schema.Union([ + Schema.Struct({ + type: Schema.Literal("predicate"), + name: Schema.String, + instructions: Schema.String + }), + Schema.Struct({ + type: Schema.Literal("choice"), + name: Schema.String, + instructions: Schema.String, + choices: Schema.Array(Schema.Struct({ value: Schema.String, description: Schema.String })) + }), + Schema.Struct({ + type: Schema.Literal("score"), + name: Schema.String, + instructions: Schema.String, + levels: Schema.Array(Schema.Struct({ label: Schema.String, description: Schema.String })) + }) +]) + +/** + * A question sent to the OpenAI Decisions API. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type DecisionQuestion = typeof DecisionQuestion.Type + +/** + * Schema for a content part in a Decisions API input message. + * + * **Gotchas** + * + * `image_url` must be an inline base64 `data:` URL. OpenAI rejects hosted + * URLs and file ids. + * + * @stability unstable + * @category schemas + * @since 4.0.5 + */ +export const DecisionInputContent = Schema.Union([ + Schema.Struct({ type: Schema.Literal("input_text"), text: Schema.String }), + Schema.Struct({ type: Schema.Literal("input_image"), image_url: Schema.String }) +]) + +/** + * A content part in a Decisions API input message. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type DecisionInputContent = typeof DecisionInputContent.Type + +/** + * Schema for the request payload sent to `POST /decisions`. + * + * **Details** + * + * `input` is either a string or a list of user messages made of text and image + * parts. + * + * @stability unstable + * @category schemas + * @since 4.0.5 + */ +export const CreateDecisionsRequest = Schema.Struct({ + model: Schema.Union([Schema.Literal("gpt-6-luna"), Schema.StringForLiteralAutocomplete]), + input: Schema.Union([ + Schema.String, + Schema.Array(Schema.Struct({ role: Schema.Literal("user"), content: Schema.Array(DecisionInputContent) })) + ]), + questions: Schema.Array(DecisionQuestion) +}) + +/** + * Request payload sent to `POST /decisions`. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type CreateDecisionsRequest = typeof CreateDecisionsRequest.Type + +/** + * Schema for an answer returned by the OpenAI Decisions API. + * + * **Details** + * + * Each answer echoes the `name` of its question. Answers to `predicate`, + * `choice`, and `score` questions have the same `type` as the question. A + * `score` is a probability-weighted average of the 0-based level indices, so + * it can fall between levels. + * + * **Gotchas** + * + * OpenAI can refuse a single question. That answer has `type: "refusal"` and + * no result fields. + * + * @stability unstable + * @category schemas + * @since 4.0.5 + */ +export const DecisionAnswer = Schema.Union([ + Schema.Struct({ + type: Schema.Literal("predicate"), + name: Schema.String, + probability: Schema.Number + }), + Schema.Struct({ + type: Schema.Literal("choice"), + name: Schema.String, + choice: Schema.String, + probabilities: Schema.Array(Schema.Struct({ value: Schema.String, probability: Schema.Number })), + confidence: Schema.Number + }), + Schema.Struct({ + type: Schema.Literal("score"), + name: Schema.String, + score: Schema.Number, + probabilities: Schema.Array(Schema.Struct({ + value: Schema.Int, + label: Schema.String, + probability: Schema.Number + })), + confidence: Schema.Number + }), + Schema.Struct({ + type: Schema.Literal("refusal"), + name: Schema.String + }) +]) + +/** + * An answer returned by the OpenAI Decisions API. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type DecisionAnswer = typeof DecisionAnswer.Type + +/** + * Schema for a successful response from `POST /decisions`. + * + * **Gotchas** + * + * OpenAI does not document usage for this endpoint, so `usage` and its token + * counts are optional. + * + * @stability unstable + * @category schemas + * @since 4.0.5 + */ +export const CreateDecisionsResponse = Schema.Struct({ + answers: Schema.Array(DecisionAnswer), + usage: Schema.optionalKey(Schema.Struct({ + input_tokens: Schema.optionalKey(Schema.Int), + output_tokens: Schema.optionalKey(Schema.Int) + })) +}) + +/** + * Successful response from `POST /decisions`. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type CreateDecisionsResponse = typeof CreateDecisionsResponse.Type diff --git a/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts b/packages/ai/openai/src/OpenAiTelemetry.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts rename to packages/ai/openai/src/OpenAiTelemetry.ts diff --git a/repos/effect/packages/ai/openai/src/OpenAiTool.ts b/packages/ai/openai/src/OpenAiTool.ts similarity index 99% rename from repos/effect/packages/ai/openai/src/OpenAiTool.ts rename to packages/ai/openai/src/OpenAiTool.ts index b74dec817..8041cd04a 100644 --- a/repos/effect/packages/ai/openai/src/OpenAiTool.ts +++ b/packages/ai/openai/src/OpenAiTool.ts @@ -321,7 +321,7 @@ const WebSearchSuccess = Schema.Struct({ const WebSearchFailure = Schema.Struct({ action: Generated.WebSearchToolCall.fields.action, - status: Schema.Literals(Generated.WebSearchToolCall.fields.status.literals.filter((status) => status !== "completed")) + status: Schema.Literals(Generated.WebSearchCallStatus.literals.filter((status) => status !== "completed")) }) /** diff --git a/repos/effect/packages/ai/openai/src/index.ts b/packages/ai/openai/src/index.ts similarity index 90% rename from repos/effect/packages/ai/openai/src/index.ts rename to packages/ai/openai/src/index.ts index 69d7e26bd..026e4467d 100644 --- a/repos/effect/packages/ai/openai/src/index.ts +++ b/packages/ai/openai/src/index.ts @@ -29,6 +29,12 @@ export * as OpenAiClientGenerated from "./OpenAiClientGenerated.ts" */ export * as OpenAiConfig from "./OpenAiConfig.ts" +/** + * @stability unstable + * @since 4.0.5 + */ +export * as OpenAiDecisionModel from "./OpenAiDecisionModel.ts" + /** * @stability unstable * @since 4.0.0 diff --git a/repos/effect/packages/ai/openai/src/internal/errors.ts b/packages/ai/openai/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/internal/errors.ts rename to packages/ai/openai/src/internal/errors.ts diff --git a/repos/effect/packages/ai/openai/src/internal/utilities.ts b/packages/ai/openai/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/openai/src/internal/utilities.ts rename to packages/ai/openai/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts b/packages/ai/openai/test/OpenAiClient.test.ts similarity index 98% rename from repos/effect/packages/ai/openai/test/OpenAiClient.test.ts rename to packages/ai/openai/test/OpenAiClient.test.ts index 8440e4893..2a3b3d531 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts +++ b/packages/ai/openai/test/OpenAiClient.test.ts @@ -508,6 +508,23 @@ describe("OpenAiClient", () => { }) describe("createResponseStream", () => { + it.effect("emits response.completed before [DONE] in the same SSE chunk", () => + Effect.gen(function*() { + const client = yield* OpenAiClient.OpenAiClient + const [, stream] = yield* client.createResponseStream({ model: "gpt-4o", input: "test" }) + const events = yield* Stream.runCollect(stream) + + assert.deepStrictEqual(events.map((event) => event.type), ["response.completed"]) + }).pipe(Effect.provide(makeTestLayer(undefined, { + _tag: "Sse", + events: [{ + type: "response.completed", + sequence_number: 1, + response: makeResponseBody({ status: "completed" }) + }], + trailer: "data: [DONE]\n\n" + })))) + it.live("terminates an SSE stream at response.failed", () => Effect.gen(function*() { const client = yield* OpenAiClient.OpenAiClient @@ -1128,6 +1145,7 @@ type MockResponse = | { readonly _tag: "Sse" readonly events: ReadonlyArray + readonly trailer?: string | undefined readonly keepOpen?: boolean | undefined readonly status?: number | undefined readonly headers?: Record | undefined @@ -1247,7 +1265,7 @@ const makeResponse = ( : "text/event-stream" const body = response._tag === "Json" ? JSON.stringify(response.body) - : response.events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("") + : response.events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("") + (response.trailer ?? "") const httpResponse = HttpClientResponse.fromWeb( request, diff --git a/packages/ai/openai/test/OpenAiDecisionModel.test.ts b/packages/ai/openai/test/OpenAiDecisionModel.test.ts new file mode 100644 index 000000000..2b4b425e0 --- /dev/null +++ b/packages/ai/openai/test/OpenAiDecisionModel.test.ts @@ -0,0 +1,327 @@ +import { OpenAiClient } from "@effect/ai-openai" +import * as OpenAiDecisionModel from "@effect/ai-openai/OpenAiDecisionModel" +import { assert, describe, it } from "@effect/vitest" +import { Effect, Layer, Redacted, Schema } from "effect" +import { Decision, DecisionModel, Model } from "effect/ai" +import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/http" + +const Triage = Decision.make({ + input: Schema.Struct({ message: Schema.String }), + decisions: { + team: Decision.classify({ + instructions: "Which team should handle this request?", + criteria: { billing: "Payments", technical: "Outages" } + }), + severity: Decision.rate({ + instructions: "How severe is the impact?", + criteria: ["Minor", "Major", "Critical"] + }), + urgent: Decision.probability({ + instructions: "Does this need immediate attention?", + criteria: { false: "Can wait", true: "Act now" } + }) + } +}) + +const Urgency = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ instructions: "Does this need immediate attention?" }) + } +}) + +const input = { message: "Checkout is down" } + +const answers = [ + { + name: "team", + type: "choice", + choice: "technical", + probabilities: [{ value: "billing", probability: 0.1 }, { value: "technical", probability: 0.9 }], + confidence: 0.9 + }, + { + name: "severity", + type: "score", + score: 1.75, + probabilities: [ + { value: 0, label: "minor", probability: 0.05 }, + { value: 1, label: "major", probability: 0.15 }, + { value: 2, label: "critical", probability: 0.8 } + ], + confidence: 0.8 + }, + { name: "urgent", type: "predicate", probability: 0.99 } +] + +const response = ( + answers: ReadonlyArray, + fields: object = { usage: { input_tokens: 120, output_tokens: 0 } } +) => ({ id: "dec_123", object: "decision", model: "gpt-6-luna", answers, ...fields }) + +const clientLayer = ( + body: unknown, + onRequest: (request: HttpClientRequest.HttpClientRequest) => void = () => {} +) => + OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe( + Layer.provide(Layer.succeed( + HttpClient.HttpClient, + HttpClient.makeWith( + Effect.map((request) => { + onRequest(request) + return HttpClientResponse.fromWeb(request, Response.json(body)) + }), + Effect.succeed as HttpClient.HttpClient.Preprocess + ) + )) + ) + +const layer = (body: unknown) => + OpenAiDecisionModel.layer({ model: "gpt-6-luna" }).pipe(Layer.provide(clientLayer(body))) + +const requestBody = (request: HttpClientRequest.HttpClientRequest) => { + assert.strictEqual(request.body._tag, "Uint8Array") + return request.body._tag === "Uint8Array" ? JSON.parse(new TextDecoder().decode(request.body.body)) : undefined +} + +describe("OpenAiDecisionModel", () => { + it.effect("keeps explicit Rate labels separate from their string descriptions", () => { + const definition = Decision.make({ + input: Schema.String, + decisions: { + severity: Decision.rate({ + instructions: "Severity?", + criteria: ["Minor", { label: "Critical", description: "Checkout unavailable" }] + }) + } + }) + return Effect.gen(function*() { + const result = yield* DecisionModel.decide(definition, { input: "Checkout is down" }) + assert.strictEqual(result.answers.severity.label, "Critical") + assert.deepStrictEqual({ ...result.answers.severity.probabilities }, { Minor: 0.1, Critical: 0.9 }) + }).pipe( + Effect.provide(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })), + Effect.provide(clientLayer( + response([{ + name: "severity", + type: "score", + score: 0.9, + confidence: 0.9, + probabilities: [{ value: 0, label: "ignored", probability: 0.1 }, { + value: 1, + label: "ignored", + probability: 0.9 + }] + }]), + (request) => { + assert.deepStrictEqual(requestBody(request).questions, [{ + type: "score", + name: "severity", + instructions: "Severity?", + levels: [{ label: "Minor", description: "Minor" }, { + label: "Critical", + description: "Checkout unavailable" + }] + }]) + } + )) + ) + }) + + it.effect("serializes structured instructions and descriptions to strings, including null choices", () => { + const definition = Decision.make({ + input: Schema.String, + decisions: { + team: Decision.classify({ + instructions: { question: "Team?" }, + criteria: { billing: null, technical: { handles: "Outages" } } + }), + severity: Decision.rate({ + instructions: ["Severity?"], + criteria: ["Minor", "Major", { label: "Critical", description: ["outage"] }] + }), + urgent: Decision.probability({ + instructions: { question: "Urgent?" }, + criteria: { false: ["Can wait"], true: { action: "Act now" } } + }) + } + }) + return DecisionModel.decide(definition, { input: "Checkout is down" }).pipe( + Effect.provide(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })), + Effect.provide(clientLayer(response(answers), (request) => { + assert.deepStrictEqual(requestBody(request).questions, [ + { + type: "choice", + name: "team", + instructions: "{\"question\":\"Team?\"}", + choices: [ + { value: "billing", description: "null" }, + { value: "technical", description: "{\"handles\":\"Outages\"}" } + ] + }, + { + type: "score", + name: "severity", + instructions: "[\"Severity?\"]", + levels: [ + { label: "Minor", description: "Minor" }, + { label: "Major", description: "Major" }, + { label: "Critical", description: "[\"outage\"]" } + ] + }, + { + type: "predicate", + name: "urgent", + instructions: "{\"question\":\"Urgent?\"}\n\nfalse: [\"Can wait\"]\ntrue: {\"action\":\"Act now\"}" + } + ]) + })) + ) + }) + + it.effect("sends every decision as a named question and keys the answers by name", () => + Effect.gen(function*() { + const result = yield* DecisionModel.decide(Triage, { input }) + assert.strictEqual(yield* Model.ProviderName, "openai") + assert.strictEqual(yield* Model.ModelName, "gpt-6-luna") + assert.strictEqual(result.answers.team.label, "technical") + assert.deepStrictEqual({ ...result.answers.team.probabilities }, { billing: 0.1, technical: 0.9 }) + assert.strictEqual(result.answers.team.confidence, 0.9) + assert.strictEqual(result.answers.severity.label, "Critical") + assert.strictEqual(result.answers.severity.rating, 1.75) + assert.deepStrictEqual({ ...result.answers.severity.probabilities }, { Minor: 0.05, Major: 0.15, Critical: 0.8 }) + assert.strictEqual(result.answers.severity.confidence, 0.8) + assert.strictEqual(result.answers.urgent.probability, 0.99) + assert.strictEqual(result.usage.inputTokens, 120) + assert.strictEqual(result.usage.outputTokens, 0) + }).pipe( + Effect.provide(OpenAiDecisionModel.model("gpt-6-luna")), + Effect.provide(clientLayer(response([...answers].reverse()), (request) => { + assert.strictEqual(request.method, "POST") + assert.strictEqual(request.url, "https://api.openai.com/v1/decisions") + assert.deepStrictEqual(requestBody(request), { + model: "gpt-6-luna", + input: JSON.stringify(input), + questions: [ + { + type: "choice", + name: "team", + instructions: "Which team should handle this request?", + choices: [ + { value: "billing", description: "Payments" }, + { value: "technical", description: "Outages" } + ] + }, + { + type: "score", + name: "severity", + instructions: "How severe is the impact?", + levels: [ + { label: "Minor", description: "Minor" }, + { label: "Major", description: "Major" }, + { label: "Critical", description: "Critical" } + ] + }, + { + type: "predicate", + name: "urgent", + instructions: "Does this need immediate attention?\n\nfalse: Can wait\ntrue: Act now" + } + ] + }) + })) + )) + + it.effect("sends string state unchanged without images and leaves omitted usage undefined", () => + Effect.gen(function*() { + const result = yield* DecisionModel.decide(Urgency, { input: "Checkout is down" }) + assert.isUndefined(result.usage.inputTokens) + assert.isUndefined(result.usage.outputTokens) + }).pipe( + Effect.provide(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })), + Effect.provide(clientLayer(response([answers[2]], {}), (request) => { + assert.strictEqual(requestBody(request).input, "Checkout is down") + })) + )) + + it.effect("fails with InvalidOutputError when an answer is missing", () => + Effect.gen(function*() { + const error = yield* Effect.flip(DecisionModel.decide(Triage, { input })) + assert.strictEqual(error.reason._tag, "InvalidOutputError") + }).pipe(Effect.provide(layer(response(answers.slice(0, 2)))))) + + it.effect("fails the whole call with ContentPolicyError when any answer is a refusal", () => + Effect.gen(function*() { + const error = yield* Effect.flip(DecisionModel.decide(Triage, { input })) + assert.strictEqual(error.reason._tag, "ContentPolicyError") + if (error.reason._tag === "ContentPolicyError") { + assert.include(error.reason.description, "severity") + assert.notInclude(error.reason.description, "team") + assert.include(JSON.stringify(error.reason.metadata.openai), "severity") + } + }).pipe(Effect.provide(layer(response([ + answers[0], + { name: "severity", type: "refusal", refusal: "I can't help with that." }, + answers[2] + ]))))) + + it.effect("sends string and JSON state with byte array, base64 string, and data URL images", () => { + const dataUrl = "data:image/webp;base64,UklGRg==" + const images = [ + { mediaType: "image/png", data: new Uint8Array([137, 80, 78, 71]) }, + { mediaType: "image/jpeg", data: "/9j/4AAQ" }, + { mediaType: "image/webp", data: new URL(dataUrl) } + ] + const assertInput = (request: HttpClientRequest.HttpClientRequest, text: string) => { + const body = requestBody(request) + assert.strictEqual(body.input.length, 1) + assert.strictEqual(body.input[0].role, "user") + assert.deepStrictEqual(body.input[0].content, [ + { type: "input_text", text }, + { type: "input_image", image_url: "data:image/png;base64,iVBORw==" }, + { type: "input_image", image_url: "data:image/jpeg;base64,/9j/4AAQ" }, + { type: "input_image", image_url: dataUrl } + ]) + } + return Effect.gen(function*() { + const result = yield* DecisionModel.decide(Urgency, { + input: "Checkout is down", + images + }) + assert.strictEqual(result.answers.urgent.probability, 0.99) + const jsonResult = yield* DecisionModel.decide(Triage, { input, images }).pipe( + Effect.provide(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })), + Effect.provide(clientLayer(response(answers), (request) => { + assertInput(request, "{\"message\":\"Checkout is down\"}") + })) + ) + assert.strictEqual(jsonResult.answers.urgent.probability, 0.99) + }).pipe( + Effect.provide(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })), + Effect.provide(clientLayer(response([answers[2]]), (request) => { + const body = requestBody(request) + assert.deepStrictEqual(body.questions, [ + { type: "predicate", name: "urgent", instructions: "Does this need immediate attention?" } + ]) + assertInput(request, "Checkout is down") + })) + ) + }) + + it.effect("rejects remote image URLs without sending a request", () => { + let requests = 0 + return Effect.gen(function*() { + const error = yield* Effect.flip(DecisionModel.decide(Urgency, { + input: "Checkout is down", + images: [{ mediaType: "image/png", data: new URL("https://example.com/cat.png") }] + })) + assert.strictEqual(error.reason._tag, "InvalidUserInputError") + assert.strictEqual(requests, 0) + }).pipe( + Effect.provide(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })), + Effect.provide(clientLayer(response([answers[2]]), () => { + requests++ + })) + ) + }) +}) diff --git a/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts b/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts similarity index 99% rename from repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts rename to packages/ai/openai/test/OpenAiEmbeddingModel.test.ts index 65ab739b3..ffe9c4496 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts +++ b/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts @@ -283,6 +283,7 @@ const getRequestBody = (request: HttpClientRequest.HttpClientRequest) => const noopOpenAiClient: OpenAiClient.Service = { client: undefined as unknown as OpenAiClient.Service["client"], + createDecisions: () => Effect.die(new Error("noop")), createResponse: () => Effect.die(new Error("noop")), createResponseStream: () => Effect.die(new Error("noop")), createEmbedding: () => Effect.die(new Error("noop")) diff --git a/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts b/packages/ai/openai/test/OpenAiLanguageModel.test.ts similarity index 91% rename from repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts rename to packages/ai/openai/test/OpenAiLanguageModel.test.ts index a015ec206..197f5ff28 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts +++ b/packages/ai/openai/test/OpenAiLanguageModel.test.ts @@ -82,6 +82,40 @@ describe("OpenAiLanguageModel", () => { ) }) + describe("web search find_in_page", () => { + it.effect.each(["generateText", "streamText"] as const)( + "preserves actions without a URL with %s", + (method) => + Effect.gen(function*() { + const action = { type: "find_in_page", pattern: "og:image" } as const + const item = { type: "web_search_call", id: "ws_123", status: "completed", action } + const event = { type: "response.output_item.done", sequence_number: 1, output_index: 0, item } + const body = method === "generateText" + ? JSON.stringify({ ...makeDefaultResponse(), output: [item] }) + : `data: ${JSON.stringify(event)}\n\n` + const client = makeRawResponseClient( + body, + method === "generateText" ? "application/json" : "text/event-stream" + ) + const options = { prompt: "Search the page", toolkit: Toolkit.make(OpenAiTool.WebSearch({})) } + const parts = yield* Effect.gen(function*() { + if (method === "generateText") return (yield* LanguageModel.generateText(options)).content + return yield* LanguageModel.streamText(options).pipe(Stream.runCollect) + }).pipe( + Effect.provide(OpenAiLanguageModel.model("gpt-5.4")), + Effect.provide(OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") })), + Effect.provideService(HttpClient.HttpClient, client) + ) + + deepStrictEqual(parts.filter((part) => part.type === "tool-call").map((part) => part.params), [{ action }]) + deepStrictEqual( + parts.filter((part) => part.type === "tool-result").map((part) => part.result), + [{ action, status: "completed" }] + ) + }) + ) + }) + describe("web search without an action", () => { it.effect.each( [ @@ -125,7 +159,7 @@ describe("OpenAiLanguageModel", () => { })) it.effect("rejects null and malformed actions when present", () => - Effect.forEach([null, { type: "find_in_page", url: "https://example.com" }], (action) => { + Effect.forEach([null, { type: "find_in_page", url: "https://example.com", pattern: null }], (action) => { const event = { type: "response.output_item.done", sequence_number: 1, @@ -573,6 +607,45 @@ describe("OpenAiLanguageModel", () => { }) }).pipe(Effect.provide(makeTestLayer({ body: { model: "o1" } })))) + it.effect("replays encrypted reasoning statelessly for an unknown model", () => + Effect.gen(function*() { + const history = Prompt.fromResponseParts([ + AiResponse.makePart("reasoning-start", { + id: "reasoning_123:0", + metadata: { openai: { itemId: "reasoning_123" } } + }), + AiResponse.makePart("reasoning-delta", { + id: "reasoning_123:0", + delta: "Let me think..." + }), + AiResponse.makePart("reasoning-end", { + id: "reasoning_123:0", + metadata: { + openai: { + itemId: "reasoning_123", + encryptedContent: "encrypted-reasoning" + } + } + }) + ]) + + yield* LanguageModel.generateText({ + prompt: Prompt.concat(history, Prompt.make("Continue")) + }).pipe(Effect.provide(OpenAiLanguageModel.model("custom-model", { store: false }))) + + const requests = yield* MockHttpClient.requests + const body = yield* getRequestBody(requests[0]) + const reasoningItem = body.input.find((item: any) => item.type === "reasoning") + + assert.isDefined(reasoningItem) + deepStrictEqual(reasoningItem, { + type: "reasoning", + id: "reasoning_123", + summary: [{ type: "summary_text", text: "Let me think..." }], + encrypted_content: "encrypted-reasoning" + }) + }).pipe(Effect.provide(makeTestLayer({ body: { model: "custom-model" } })))) + it.effect("serializes stored assistant history according to item reference config", () => Effect.gen(function*() { yield* LanguageModel.generateText({ @@ -1247,7 +1320,7 @@ describe("OpenAiLanguageModel", () => { }) describe("response handling", () => { - it.effect("preserves prompt cache diagnostics alongside the service tier", () => + it.effect("preserves ultrafast service tier alongside prompt cache diagnostics", () => Effect.gen(function*() { const diagnostics = { type: "cache_miss", @@ -1256,7 +1329,7 @@ describe("OpenAiLanguageModel", () => { comparison_reusable_tokens: 2048 } as const const body = JSON.stringify({ - ...makeDefaultResponse({ service_tier: "priority" }), + ...makeDefaultResponse({ service_tier: "ultrafast" }), prompt_cache_diagnostics: diagnostics }) const result = yield* LanguageModel.generateText({ prompt: "test" }).pipe( @@ -1268,7 +1341,7 @@ describe("OpenAiLanguageModel", () => { const finish = result.content.find((part) => part.type === "finish") assert.isDefined(finish) deepStrictEqual(finish.metadata.openai, { - serviceTier: "priority", + serviceTier: "ultrafast", promptCacheDiagnostics: diagnostics }) })) @@ -1759,9 +1832,27 @@ describe("OpenAiLanguageModel", () => { }) describe("streamText", () => { - it.effect.each(["completed", "failed"] as const)( - "preserves prompt cache diagnostics in response.%s", - (status) => + it.effect.each( + [ + { + status: "completed", + serviceTier: "fast", + expectedMetadata: { promptCacheDiagnostics: { type: "cache_hit" } } + }, + { + status: "completed", + serviceTier: "ultrafast", + expectedMetadata: { serviceTier: "ultrafast", promptCacheDiagnostics: { type: "cache_hit" } } + }, + { + status: "failed", + serviceTier: "ultrafast", + expectedMetadata: { serviceTier: "ultrafast", promptCacheDiagnostics: { type: "cache_hit" } } + } + ] as const + )( + "preserves supported tiers and prompt cache diagnostics in response.$status with $serviceTier", + ({ status, serviceTier, expectedMetadata }) => Effect.gen(function*() { const event = { type: `response.${status}`, @@ -1771,7 +1862,7 @@ describe("OpenAiLanguageModel", () => { status, error: status === "failed" ? { code: "server_error", message: "generation failed" } : null }), - service_tier: "fast", + service_tier: serviceTier, prompt_cache_diagnostics: { type: "cache_hit" } } } @@ -1790,7 +1881,7 @@ describe("OpenAiLanguageModel", () => { const finish = parts.find((part) => part.type === "finish") assert.isDefined(finish) - deepStrictEqual(finish.metadata.openai, { promptCacheDiagnostics: { type: "cache_hit" } }) + deepStrictEqual(finish.metadata.openai, expectedMetadata) }) ) @@ -2052,6 +2143,51 @@ describe("OpenAiLanguageModel", () => { assert.isDefined(parts.find((part) => part.type === "finish")) })) + it.effect("streams undeclared tool calls when tool call validation is deferred", () => + Effect.gen(function*() { + const streamEvents = [ + { + type: "response.created", + sequence_number: 1, + response: makeDefaultResponse({ status: "in_progress" }) + }, + { + type: "response.output_item.added", + sequence_number: 2, + output_index: 0, + item: makeFunctionCall("Write", {}, { arguments: "", status: "in_progress" }) + }, + { + type: "response.output_item.done", + sequence_number: 3, + output_index: 0, + item: makeFunctionCall("Write", { path: "a" }) + }, + { + type: "response.completed", + sequence_number: 4, + response: makeDefaultResponse() + } + ] as unknown as ReadonlyArray + + const parts = yield* LanguageModel.streamText({ + prompt: "Use the tools", + toolkit: TestToolkit, + disableToolCallResolution: true, + toolCallValidation: "deferred" + }).pipe( + Stream.runCollect, + Effect.provide(OpenAiLanguageModel.model("gpt-4o-mini")), + Effect.provide(makeStreamTestLayer(streamEvents)), + Effect.provide(TestToolkitLayer) + ) + + deepStrictEqual( + parts.filter((part) => part.type === "tool-call").map(({ name, params }) => ({ name, params })), + [{ name: "Write", params: { path: "a" } }] + ) + })) + it.effect("waits for the stable streamed web search action before emitting the tool call", () => Effect.gen(function*() { const toolkit = Toolkit.make(OpenAiTool.WebSearch({})) @@ -2408,6 +2544,7 @@ describe("OpenAiLanguageModel", () => { fileIdPrefixes: ["file-"], strictJsonSchema: false, useItemReferences: false, + reasoningModel: true, temperature: 0.5 })) ) @@ -2418,11 +2555,94 @@ describe("OpenAiLanguageModel", () => { strictEqual(body.fileIdPrefixes, undefined) strictEqual(body.strictJsonSchema, undefined) strictEqual(body.useItemReferences, undefined) + strictEqual(body.reasoningModel, undefined) strictEqual(body.temperature, 0.5) }).pipe(Effect.provide(makeTestLayer()))) }) + + describe("reasoning capabilities", () => { + it.effect("merges explicit include values with generated values", () => + Effect.gen(function*() { + yield* LanguageModel.generateText({ prompt: "test" }).pipe( + Effect.provide(OpenAiLanguageModel.model("custom-model", { + store: false, + include: ["message.input_image.image_url", "reasoning.encrypted_content"], + top_logprobs: 2 + })) + ) + + const requests = yield* MockHttpClient.requests + const body = yield* getRequestBody(requests[0]) + + deepStrictEqual(body.include, [ + "message.input_image.image_url", + "reasoning.encrypted_content", + "message.output_text.logprobs" + ]) + }).pipe(Effect.provide(makeTestLayer()))) + + for (const { name, model, config, include, role } of reasoningCapabilityCases) { + it.effect(name, () => + Effect.gen(function*() { + yield* LanguageModel.generateText({ + prompt: Prompt.make([ + { role: "system", content: "You are a helpful assistant" }, + { role: "user", content: "Hello" } + ]) + }).pipe(Effect.provide(OpenAiLanguageModel.model(model, config))) + + const requests = yield* MockHttpClient.requests + const body = yield* getRequestBody(requests[0]) + + deepStrictEqual(body.include, include) + strictEqual(body.input[0].role, role) + }).pipe(Effect.provide(makeTestLayer({ body: { model } })))) + } + + it.effect("prefers the reasoningModel override from withConfigOverride", () => + Effect.gen(function*() { + yield* LanguageModel.generateText({ + prompt: Prompt.make([ + { role: "system", content: "You are a helpful assistant" }, + { role: "user", content: "Hello" } + ]) + }).pipe( + OpenAiLanguageModel.withConfigOverride({ reasoningModel: true }), + Effect.provide(OpenAiLanguageModel.model("gpt-4.1", { store: false, reasoningModel: false })) + ) + + const requests = yield* MockHttpClient.requests + const body = yield* getRequestBody(requests[0]) + + deepStrictEqual(body.include, ["reasoning.encrypted_content"]) + strictEqual(body.input[0].role, "developer") + }).pipe(Effect.provide(makeTestLayer({ body: { model: "gpt-4.1" } })))) + }) }) +const reasoningCapabilityCases: ReadonlyArray<{ + readonly name: string + readonly model: string + readonly config: Omit + readonly include: ReadonlyArray | undefined + readonly role: "system" | "developer" +}> = [ + { + name: "defaults an unknown model to reasoning", + model: "custom-model", + config: { store: false }, + include: ["reasoning.encrypted_content"], + role: "developer" + }, + { + name: "disables automatic reasoning without dropping explicit includes", + model: "custom-model", + config: { store: false, reasoningModel: false, include: ["message.input_image.image_url"] }, + include: ["message.input_image.image_url"], + role: "system" + } +] + // ============================================================================= // Test Infrastructure // ============================================================================= @@ -2491,6 +2711,7 @@ const makeStreamTestLayer = (events: ReadonlyArray Effect.die(new Error("unexpected createDecisions call")), createResponse: () => Effect.die(new Error("unexpected createResponse call")), createResponseStream: () => Effect.succeed([response, Stream.fromIterable(events)]), createEmbedding: () => Effect.die(new Error("unexpected createEmbedding call")) diff --git a/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts b/packages/ai/openai/test/OpenAiSchema.test.ts similarity index 99% rename from repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts rename to packages/ai/openai/test/OpenAiSchema.test.ts index 7de0dc6ae..da4e2bb83 100644 --- a/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts +++ b/packages/ai/openai/test/OpenAiSchema.test.ts @@ -457,6 +457,7 @@ describe("OpenAiSchema", () => { OpenAiClient.OpenAiClient, OpenAiClient.OpenAiClient.of({ client: undefined as any, + createDecisions: () => Effect.die("unexpected"), createResponse: () => Effect.die("unexpected"), createResponseStream: () => Effect.succeed([ diff --git a/repos/effect/packages/ai/openai/test/OpenAiTelemetry.test.ts b/packages/ai/openai/test/OpenAiTelemetry.test.ts similarity index 100% rename from repos/effect/packages/ai/openai/test/OpenAiTelemetry.test.ts rename to packages/ai/openai/test/OpenAiTelemetry.test.ts diff --git a/repos/effect/packages/ai/openai/tsconfig.json b/packages/ai/openai/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/openai/tsconfig.json rename to packages/ai/openai/tsconfig.json diff --git a/packages/ai/openai/typetest/OpenAiDecisionModel.tst.ts b/packages/ai/openai/typetest/OpenAiDecisionModel.tst.ts new file mode 100644 index 000000000..f1f715d54 --- /dev/null +++ b/packages/ai/openai/typetest/OpenAiDecisionModel.tst.ts @@ -0,0 +1,86 @@ +import { type OpenAiClient, OpenAiDecisionModel } from "@effect/ai-openai" +import { Effect, type Layer, Schema } from "effect" +import { Decision, DecisionModel, type Model } from "effect/ai" +import { describe, expect, it } from "tstyche" + +declare const client: OpenAiClient.Service + +const Triage = Decision.make({ + input: Schema.String, + decisions: { + urgent: Decision.probability({ + instructions: "The message conveys urgency", + criteria: { false: "No time pressure", true: "Needs action now" } + }) + } +}) + +describe("OpenAiDecisionModel", () => { + it("suggests gpt-6-luna while accepting other model ids", () => { + expect().type.toBe<"gpt-6-luna" | (string & {})>() + expect(OpenAiDecisionModel.layer).type.toBeCallableWith({ model: "gpt-6-luna-next" }) + }) + + it("model provides DecisionModel and requires OpenAiClient", () => { + expect(OpenAiDecisionModel.model("gpt-6-luna")).type.toBe< + Model.Model<"openai", DecisionModel.DecisionModel, OpenAiClient.OpenAiClient> + >() + }) + + it("layer provides DecisionModel and requires OpenAiClient", () => { + expect(OpenAiDecisionModel.layer({ model: "gpt-6-luna" })).type.toBe< + Layer.Layer + >() + }) + + it("make builds a DecisionModel from the client service", () => { + expect(OpenAiDecisionModel.make({ model: "gpt-6-luna" })).type.toBe< + Effect.Effect + >() + }) + + it("satisfies a decide program's DecisionModel requirement", () => { + const program = DecisionModel.decide(Triage, { input: "hello" }).pipe( + Effect.provide(OpenAiDecisionModel.model("gpt-6-luna")) + ) + + type Requirements = typeof program extends Effect.Effect ? R : never + + expect().type.toBe() + }) +}) + +describe("OpenAiClient.createDecisions", () => { + it("accepts string input and message input with text and image parts", () => { + const questions = [ + { type: "predicate", name: "urgent", instructions: "Act now?" }, + { + type: "choice", + name: "team", + instructions: "Which team?", + choices: [{ value: "billing", description: "Pay" }] + }, + { type: "score", name: "severity", instructions: "How bad?", levels: [{ label: "Minor", description: "Minor" }] } + ] as const + expect(client.createDecisions).type.toBeCallableWith({ model: "gpt-6-luna", input: "Checkout is down", questions }) + expect(client.createDecisions).type.toBeCallableWith({ + model: "gpt-6-luna", + input: [{ + role: "user", + content: [ + { type: "input_text", text: "Checkout is down" }, + { type: "input_image", image_url: "data:image/png;base64,iVBORw==" } + ] + }], + questions + }) + }) + + it("rejects questions without a name", () => { + expect(client.createDecisions).type.not.toBeCallableWith({ + model: "gpt-6-luna", + input: "Checkout is down", + questions: [{ type: "predicate", instructions: "Act now?" }] + }) + }) +}) diff --git a/repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts b/packages/ai/openai/typetest/OpenAiTool.tst.ts similarity index 100% rename from repos/effect/packages/ai/openai/typetest/OpenAiTool.tst.ts rename to packages/ai/openai/typetest/OpenAiTool.tst.ts diff --git a/repos/effect/packages/ai/openrouter/CHANGELOG.md b/packages/ai/openrouter/CHANGELOG.md similarity index 93% rename from repos/effect/packages/ai/openrouter/CHANGELOG.md rename to packages/ai/openrouter/CHANGELOG.md index 36088fdc2..7e564ba4f 100644 --- a/repos/effect/packages/ai/openrouter/CHANGELOG.md +++ b/packages/ai/openrouter/CHANGELOG.md @@ -1,5 +1,27 @@ # @effect/ai-openrouter +## 4.0.5 + +### Patch Changes + +- [#9124](https://github.com/Effect-TS/effect/pull/9124) [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e) Thanks @jem-computer! - Support structured decision instructions, descriptions, and score legends in TypeSafe, Cloudflare, and OpenRouter. `Decision.rate` accepts labeled descriptions; `Rate.criteria` entries are now `{ label, description }` objects instead of strings. +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/ai/openrouter/README.md b/packages/ai/openrouter/README.md similarity index 100% rename from repos/effect/packages/ai/openrouter/README.md rename to packages/ai/openrouter/README.md diff --git a/repos/effect/packages/ai/openrouter/codegen.yml b/packages/ai/openrouter/codegen.yml similarity index 100% rename from repos/effect/packages/ai/openrouter/codegen.yml rename to packages/ai/openrouter/codegen.yml diff --git a/repos/effect/packages/ai/openrouter/package.json b/packages/ai/openrouter/package.json similarity index 98% rename from repos/effect/packages/ai/openrouter/package.json rename to packages/ai/openrouter/package.json index 46b99545b..0d63a0084 100644 --- a/repos/effect/packages/ai/openrouter/package.json +++ b/packages/ai/openrouter/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-openrouter", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "An OpenRouter provider integration for Effect AI SDK", diff --git a/repos/effect/packages/ai/openrouter/src/Generated.ts b/packages/ai/openrouter/src/Generated.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/Generated.ts rename to packages/ai/openrouter/src/Generated.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts b/packages/ai/openrouter/src/OpenRouterClient.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts rename to packages/ai/openrouter/src/OpenRouterClient.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts b/packages/ai/openrouter/src/OpenRouterConfig.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts rename to packages/ai/openrouter/src/OpenRouterConfig.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterDecisionModel.ts b/packages/ai/openrouter/src/OpenRouterDecisionModel.ts similarity index 95% rename from repos/effect/packages/ai/openrouter/src/OpenRouterDecisionModel.ts rename to packages/ai/openrouter/src/OpenRouterDecisionModel.ts index 66dfd86e7..b548ed5d6 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterDecisionModel.ts +++ b/packages/ai/openrouter/src/OpenRouterDecisionModel.ts @@ -77,7 +77,11 @@ export const make = Effect.fnUntraced(function*(options: { questions[key] = { type: "choice", instructions: decision.instructions, criteria: decision.criteria } break case "Rate": - questions[key] = { type: "score", instructions: decision.instructions, criteria: decision.criteria } + questions[key] = { + type: "score", + instructions: decision.instructions, + criteria: decision.criteria.map((level) => level.description) + } break case "Probability": questions[key] = { type: "noul", instructions: decision.instructions, criteria: decision.criteria } @@ -111,7 +115,7 @@ export const make = Effect.fnUntraced(function*(options: { const decision = decisions[key] const probabilities: Record = Object.create(null) if (decision?._tag === "Rate") { - for (const [index, label] of decision.criteria.entries()) { + for (const [index, { label }] of decision.criteria.entries()) { const probability = answer.probabilities[String(index)] if (probability !== undefined) { probabilities[label] = probability diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts b/packages/ai/openrouter/src/OpenRouterError.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterError.ts rename to packages/ai/openrouter/src/OpenRouterError.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts b/packages/ai/openrouter/src/OpenRouterLanguageModel.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts rename to packages/ai/openrouter/src/OpenRouterLanguageModel.ts diff --git a/repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts b/packages/ai/openrouter/src/OpenRouterSchema.ts similarity index 84% rename from repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts rename to packages/ai/openrouter/src/OpenRouterSchema.ts index c222a73ac..2eee16bcc 100644 --- a/repos/effect/packages/ai/openrouter/src/OpenRouterSchema.ts +++ b/packages/ai/openrouter/src/OpenRouterSchema.ts @@ -7,6 +7,8 @@ import * as Schema from "effect/Schema" import * as Generated from "./Generated.ts" +const Entry = Schema.Union([Schema.String, Schema.JsonObject, Schema.Array(Schema.Json)]) + /** * A choice question with descriptions keyed by choice label. * @@ -16,8 +18,8 @@ import * as Generated from "./Generated.ts" */ export const ChoiceQuestion = Schema.Struct({ type: Schema.Literal("choice"), - instructions: Schema.String, - criteria: Schema.Record(Schema.String, Schema.String) + instructions: Entry, + criteria: Schema.Record(Schema.String, Schema.NullOr(Entry)) }) /** @@ -29,8 +31,8 @@ export const ChoiceQuestion = Schema.Struct({ */ export const ScoreQuestion = Schema.Struct({ type: Schema.Literal("score"), - instructions: Schema.String, - criteria: Schema.Array(Schema.String) + instructions: Entry, + criteria: Schema.Array(Entry) }) /** @@ -42,12 +44,13 @@ export const ScoreQuestion = Schema.Struct({ */ export const NoulQuestion = Schema.Struct({ type: Schema.Literal("noul"), - instructions: Schema.String, - criteria: Schema.optional(Schema.Struct({ true: Schema.String, false: Schema.String })) + instructions: Entry, + criteria: Schema.optional(Schema.Struct({ true: Entry, false: Entry })) }) /** - * A choice, ordered score, or probability question. + * A choice, score, or probability question with string, JSON object, or JSON array + * instructions and descriptions. Choice descriptions also accept `null`. * * @stability unstable * @category schemas @@ -78,7 +81,7 @@ export const Answer = Schema.Union([ score: Schema.Finite, confidence: Schema.optional(Schema.Finite), probabilities: Schema.optional(Schema.Record(Schema.String, Schema.Finite)), - legend: Schema.optional(Schema.Record(Schema.String, Schema.String)) + legend: Schema.optional(Schema.Record(Schema.String, Schema.Json)) }), Schema.Struct({ type: Schema.Literal("noul"), noul: Schema.Finite }) ]) diff --git a/repos/effect/packages/ai/openrouter/src/index.ts b/packages/ai/openrouter/src/index.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/index.ts rename to packages/ai/openrouter/src/index.ts diff --git a/repos/effect/packages/ai/openrouter/src/internal/errors.ts b/packages/ai/openrouter/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/internal/errors.ts rename to packages/ai/openrouter/src/internal/errors.ts diff --git a/repos/effect/packages/ai/openrouter/src/internal/utilities.ts b/packages/ai/openrouter/src/internal/utilities.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/src/internal/utilities.ts rename to packages/ai/openrouter/src/internal/utilities.ts diff --git a/repos/effect/packages/ai/openrouter/test/Generated.test.ts b/packages/ai/openrouter/test/Generated.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/Generated.test.ts rename to packages/ai/openrouter/test/Generated.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts b/packages/ai/openrouter/test/OpenRouterClient.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterClient.test.ts rename to packages/ai/openrouter/test/OpenRouterClient.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts b/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts similarity index 95% rename from repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts rename to packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts index bd1d21c33..c3da61a01 100644 --- a/repos/effect/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts +++ b/packages/ai/openrouter/test/OpenRouterDecisionModel.test.ts @@ -67,6 +67,16 @@ const decisionsResponse = { usage: { input_tokens: 312, output_tokens: 48 } } +const Structured = Decision.make({ + input: Schema.String, + decisions: { + severity: Decision.rate({ + instructions: ["How severe?"], + criteria: ["Minor", { label: "Critical", description: { impact: "outage" } }] + }) + } +}) + describe("OpenRouterDecisionModel", () => { it.effect("accepts rounded choice probabilities that total 0.99", () => Effect.gen(function*() { @@ -819,8 +829,37 @@ describe("OpenRouterDecisionModel", () => { assert.strictEqual(error.reason._tag, reason) })) } -}) + it.effect("sends structured descriptions and maps score indices to labels", () => + Effect.gen(function*() { + const result = yield* DecisionModel.decide(Structured, { input: "Checkout is down" }) + assert.strictEqual(result.answers.severity.label, "Critical") + assert.deepStrictEqual({ ...result.answers.severity.probabilities }, { Minor: 0.1, Critical: 0.9 }) + }).pipe( + Effect.provide(OpenRouterDecisionModel.layer({ model: "test/decision-model" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const body = yield* getRequestBody(request) + assert.deepStrictEqual(body.questions, { + severity: { type: "score", instructions: ["How severe?"], criteria: ["Minor", { impact: "outage" }] } + }) + return jsonResponse(request, { + model: "test/decision-model", + usage: { input_tokens: 120, output_tokens: 0 }, + answers: { + severity: { + type: "score", + score: 0.9, + probabilities: { "0": 0.1, "1": 0.9 }, + confidence: 0.9, + legend: { "0": "Minor", "1": { impact: "outage" } } + } + } + }) + }) + )) + )) +}) const makeClientLayer = ( handler: ( request: HttpClientRequest.HttpClientRequest diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts b/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts rename to packages/ai/openrouter/test/OpenRouterLanguageModel.test.ts diff --git a/repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts b/packages/ai/openrouter/test/OpenRouterSchema.test.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/test/OpenRouterSchema.test.ts rename to packages/ai/openrouter/test/OpenRouterSchema.test.ts diff --git a/repos/effect/packages/ai/openrouter/tsconfig.json b/packages/ai/openrouter/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/openrouter/tsconfig.json rename to packages/ai/openrouter/tsconfig.json diff --git a/repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts b/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts rename to packages/ai/openrouter/typetest/OpenRouterDecisionModel.tst.ts diff --git a/repos/effect/packages/ai/typesafe/CHANGELOG.md b/packages/ai/typesafe/CHANGELOG.md similarity index 75% rename from repos/effect/packages/ai/typesafe/CHANGELOG.md rename to packages/ai/typesafe/CHANGELOG.md index a7bb9b5da..b8eae2571 100644 --- a/repos/effect/packages/ai/typesafe/CHANGELOG.md +++ b/packages/ai/typesafe/CHANGELOG.md @@ -1,5 +1,27 @@ # @effect/ai-typesafe +## 4.0.5 + +### Patch Changes + +- [#9124](https://github.com/Effect-TS/effect/pull/9124) [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e) Thanks @jem-computer! - Support structured decision instructions, descriptions, and score legends in TypeSafe, Cloudflare, and OpenRouter. `Decision.rate` accepts labeled descriptions; `Rate.criteria` entries are now `{ label, description }` objects instead of strings. +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/ai/typesafe/README.md b/packages/ai/typesafe/README.md similarity index 73% rename from repos/effect/packages/ai/typesafe/README.md rename to packages/ai/typesafe/README.md index 1df4b28e9..45ed94f58 100644 --- a/repos/effect/packages/ai/typesafe/README.md +++ b/packages/ai/typesafe/README.md @@ -28,4 +28,19 @@ Whether your team is considering Effect, rolling it out, or already running it i Use `TypeSafeClient.layerConfig()` to read `TYPESAFE_API_KEY`, then provide `TypeSafeDecisionModel.model("jev-latest")` to a decision workflow. Versioned model identifiers such as `jev-1.13.0` are also supported. +Instructions and descriptions can be strings, JSON objects, or JSON arrays, as in TypeSafe's [structured questions](https://docs.typesafe.ai/primitives/advanced). A rating level with a structured description takes an explicit label, which answers use: + +```ts +import { Decision } from "effect/ai" + +const severity = Decision.rate({ + instructions: "How severe is the reported issue?", + criteria: [ + { label: "cosmetic", description: { what: "No impact to functionality", examples: ["typo in a label"] } }, + { label: "degraded", description: { what: "Broken, but a workaround exists", examples: ["fails in one browser"] } }, + { label: "blocking", description: { what: "No workaround exists", examples: ["cannot log in"] } } + ] +}) +``` + The client does not retry automatically. Rate-limit errors include the provider's retry delay when available. Provider distributions are preserved, not normalized; they must satisfy the core `DecisionModel` validation, including its current `1e-6` sum tolerance. TypeSafe's rounding behavior has not been verified against the live API. diff --git a/repos/effect/packages/ai/typesafe/package.json b/packages/ai/typesafe/package.json similarity index 98% rename from repos/effect/packages/ai/typesafe/package.json rename to packages/ai/typesafe/package.json index 28f861cd0..0c89df764 100644 --- a/repos/effect/packages/ai/typesafe/package.json +++ b/packages/ai/typesafe/package.json @@ -1,6 +1,6 @@ { "name": "@effect/ai-typesafe", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "A TypeSafe DecisionModel provider for Effect", diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts b/packages/ai/typesafe/src/TypeSafeClient.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/TypeSafeClient.ts rename to packages/ai/typesafe/src/TypeSafeClient.ts diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts b/packages/ai/typesafe/src/TypeSafeConfig.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/TypeSafeConfig.ts rename to packages/ai/typesafe/src/TypeSafeConfig.ts diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts b/packages/ai/typesafe/src/TypeSafeDecisionModel.ts similarity index 94% rename from repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts rename to packages/ai/typesafe/src/TypeSafeDecisionModel.ts index 91fead032..2ac15d16c 100644 --- a/repos/effect/packages/ai/typesafe/src/TypeSafeDecisionModel.ts +++ b/packages/ai/typesafe/src/TypeSafeDecisionModel.ts @@ -56,7 +56,11 @@ export const make = Effect.fnUntraced( questions[key] = { type: "choice", instructions: decision.instructions, criteria: decision.criteria } break case "Rate": - questions[key] = { type: "score", instructions: decision.instructions, criteria: decision.criteria } + questions[key] = { + type: "score", + instructions: decision.instructions, + criteria: decision.criteria.map((level) => level.description) + } break case "Probability": questions[key] = { type: "noul", instructions: decision.instructions, criteria: decision.criteria } @@ -81,7 +85,7 @@ export const make = Effect.fnUntraced( const probabilities: Record = Object.create(null) for (let index = 0; index < levels.length; index++) { const probability = answer.probabilities[String(index)] - if (probability !== undefined) probabilities[levels[index]] = probability + if (probability !== undefined) probabilities[levels[index].label] = probability } answers[key] = { _tag: "Rate", rating: answer.score, probabilities, confidence: answer.confidence } break diff --git a/repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts b/packages/ai/typesafe/src/TypeSafeSchema.ts similarity index 82% rename from repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts rename to packages/ai/typesafe/src/TypeSafeSchema.ts index 5f034aca5..794b7a4c4 100644 --- a/repos/effect/packages/ai/typesafe/src/TypeSafeSchema.ts +++ b/packages/ai/typesafe/src/TypeSafeSchema.ts @@ -6,6 +6,8 @@ */ import * as Schema from "effect/Schema" +const Entry = Schema.NullOr(Schema.Union([Schema.String, Schema.JsonObject, Schema.Array(Schema.Json)])) + /** * A classification question. * @@ -15,8 +17,8 @@ import * as Schema from "effect/Schema" */ export const ChoiceQuestion = Schema.Struct({ type: Schema.Literal("choice"), - instructions: Schema.String, - criteria: Schema.Record(Schema.String, Schema.String) + instructions: Entry, + criteria: Schema.Record(Schema.String, Entry) }) /** @@ -28,8 +30,8 @@ export const ChoiceQuestion = Schema.Struct({ */ export const ScoreQuestion = Schema.Struct({ type: Schema.Literal("score"), - instructions: Schema.String, - criteria: Schema.Array(Schema.String) + instructions: Entry, + criteria: Schema.Array(Entry) }) /** @@ -41,12 +43,12 @@ export const ScoreQuestion = Schema.Struct({ */ export const NoulQuestion = Schema.Struct({ type: Schema.Literal("noul"), - instructions: Schema.String, - criteria: Schema.optional(Schema.Struct({ false: Schema.String, true: Schema.String })) + instructions: Entry, + criteria: Schema.optional(Schema.Struct({ false: Entry, true: Entry })) }) /** - * A System One question. + * A System One question with string, JSON object, JSON array, or `null` instructions and descriptions. * * @stability unstable * @category schemas @@ -82,7 +84,7 @@ export const ChoiceAnswer = Schema.Struct({ }) /** - * A zero-based rating and index-keyed distribution. + * A zero-based rating and index-keyed distribution. `legend` maps indices to descriptions. * * @stability unstable * @category schemas @@ -93,7 +95,7 @@ export const ScoreAnswer = Schema.Struct({ score: Schema.Number, probabilities: Schema.Record(Schema.String, Schema.Number), confidence: Schema.Number, - legend: Schema.optional(Schema.Record(Schema.String, Schema.String)) + legend: Schema.optional(Schema.Record(Schema.String, Schema.Json)) }) /** diff --git a/repos/effect/packages/ai/typesafe/src/index.ts b/packages/ai/typesafe/src/index.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/index.ts rename to packages/ai/typesafe/src/index.ts diff --git a/repos/effect/packages/ai/typesafe/src/internal/errors.ts b/packages/ai/typesafe/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/src/internal/errors.ts rename to packages/ai/typesafe/src/internal/errors.ts diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts b/packages/ai/typesafe/test/TypeSafeClient.test.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/test/TypeSafeClient.test.ts rename to packages/ai/typesafe/test/TypeSafeClient.test.ts diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts b/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts similarity index 95% rename from repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts rename to packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts index 12909ed0e..071451bd0 100644 --- a/repos/effect/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts +++ b/packages/ai/typesafe/test/TypeSafeDecisionModel.test.ts @@ -67,6 +67,16 @@ const systemOneResponse = { usage: { input_tokens: 312, output_tokens: 48 } } +const Structured = Decision.make({ + input: Schema.String, + decisions: { + severity: Decision.rate({ + instructions: ["How severe?"], + criteria: ["Minor", { label: "Critical", description: { impact: "outage" } }] + }) + } +}) + describe("TypeSafeDecisionModel", () => { it.effect("accepts rounded choice probabilities that total 0.99", () => Effect.gen(function*() { @@ -655,8 +665,36 @@ describe("TypeSafeDecisionModel", () => { assert.strictEqual(error._tag, "AiError") assert.strictEqual(error.reason._tag, "RateLimitError") })) -}) + it.effect("sends structured descriptions and maps score indices to labels", () => + Effect.gen(function*() { + const result = yield* DecisionModel.decide(Structured, { input: "Checkout is down" }) + assert.strictEqual(result.answers.severity.label, "Critical") + assert.deepStrictEqual({ ...result.answers.severity.probabilities }, { Minor: 0.1, Critical: 0.9 }) + }).pipe( + Effect.provide(TypeSafeDecisionModel.layer({ model: "jev-latest" })), + Effect.provide(makeClientLayer((request) => + Effect.gen(function*() { + const body = yield* getRequestBody(request) + assert.deepStrictEqual(body.questions, { + severity: { type: "score", instructions: ["How severe?"], criteria: ["Minor", { impact: "outage" }] } + }) + return jsonResponse(request, { + model: "jev-latest", + answers: { + severity: { + type: "score", + score: 0.9, + probabilities: { "0": 0.1, "1": 0.9 }, + confidence: 0.9, + legend: { "0": "Minor", "1": { impact: "outage" } } + } + } + }) + }) + )) + )) +}) const makeClientLayer = ( handler: ( request: HttpClientRequest.HttpClientRequest diff --git a/repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts b/packages/ai/typesafe/test/TypeSafeSchema.test.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/test/TypeSafeSchema.test.ts rename to packages/ai/typesafe/test/TypeSafeSchema.test.ts diff --git a/repos/effect/packages/ai/typesafe/tsconfig.json b/packages/ai/typesafe/tsconfig.json similarity index 100% rename from repos/effect/packages/ai/typesafe/tsconfig.json rename to packages/ai/typesafe/tsconfig.json diff --git a/repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts b/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts similarity index 100% rename from repos/effect/packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts rename to packages/ai/typesafe/typetest/TypeSafeDecisionModel.tst.ts diff --git a/repos/effect/packages/atom/react/CHANGELOG.md b/packages/atom/react/CHANGELOG.md similarity index 93% rename from repos/effect/packages/atom/react/CHANGELOG.md rename to packages/atom/react/CHANGELOG.md index d767fc56e..1d20aef21 100644 --- a/repos/effect/packages/atom/react/CHANGELOG.md +++ b/packages/atom/react/CHANGELOG.md @@ -1,5 +1,27 @@ # @effect/atom-react +## 4.0.5 + +### Patch Changes + +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- [#8954](https://github.com/Effect-TS/effect/pull/8954) [`310228c`](https://github.com/Effect-TS/effect/commit/310228c2be1b629988496eef954012d932ca0eea) Thanks @tmkx! - Allow scheduler 0.28 as a peer dependency so React 19.3 installs no longer report a peer conflict. +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/atom/react/LICENSE b/packages/atom/react/LICENSE similarity index 100% rename from repos/effect/packages/atom/react/LICENSE rename to packages/atom/react/LICENSE diff --git a/repos/effect/packages/atom/react/README.md b/packages/atom/react/README.md similarity index 100% rename from repos/effect/packages/atom/react/README.md rename to packages/atom/react/README.md diff --git a/repos/effect/packages/atom/react/package.json b/packages/atom/react/package.json similarity index 97% rename from repos/effect/packages/atom/react/package.json rename to packages/atom/react/package.json index 33646b6cc..543733154 100644 --- a/repos/effect/packages/atom/react/package.json +++ b/packages/atom/react/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-react", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "React bindings for the Effect Atom modules", @@ -62,7 +62,7 @@ "peerDependencies": { "effect": "workspace:^", "react": ">=19.0.0 <20.0.0", - "scheduler": ">=0.25.0 <0.28.0" + "scheduler": ">=0.25.0 <0.29.0" }, "devDependencies": { "@testing-library/dom": "^10.4.2", diff --git a/repos/effect/packages/atom/react/src/Hooks.ts b/packages/atom/react/src/Hooks.ts similarity index 100% rename from repos/effect/packages/atom/react/src/Hooks.ts rename to packages/atom/react/src/Hooks.ts diff --git a/repos/effect/packages/atom/react/src/ReactHydration.ts b/packages/atom/react/src/ReactHydration.ts similarity index 100% rename from repos/effect/packages/atom/react/src/ReactHydration.ts rename to packages/atom/react/src/ReactHydration.ts diff --git a/repos/effect/packages/atom/react/src/RegistryContext.ts b/packages/atom/react/src/RegistryContext.ts similarity index 100% rename from repos/effect/packages/atom/react/src/RegistryContext.ts rename to packages/atom/react/src/RegistryContext.ts diff --git a/repos/effect/packages/atom/react/src/ScopedAtom.ts b/packages/atom/react/src/ScopedAtom.ts similarity index 100% rename from repos/effect/packages/atom/react/src/ScopedAtom.ts rename to packages/atom/react/src/ScopedAtom.ts diff --git a/repos/effect/packages/atom/react/src/index.ts b/packages/atom/react/src/index.ts similarity index 100% rename from repos/effect/packages/atom/react/src/index.ts rename to packages/atom/react/src/index.ts diff --git a/repos/effect/packages/atom/react/test/index.test.tsx b/packages/atom/react/test/index.test.tsx similarity index 100% rename from repos/effect/packages/atom/react/test/index.test.tsx rename to packages/atom/react/test/index.test.tsx diff --git a/repos/effect/packages/atom/react/tsconfig.json b/packages/atom/react/tsconfig.json similarity index 100% rename from repos/effect/packages/atom/react/tsconfig.json rename to packages/atom/react/tsconfig.json diff --git a/repos/effect/packages/atom/react/vitest.setup.ts b/packages/atom/react/vitest.setup.ts similarity index 100% rename from repos/effect/packages/atom/react/vitest.setup.ts rename to packages/atom/react/vitest.setup.ts diff --git a/repos/effect/packages/atom/solid/CHANGELOG.md b/packages/atom/solid/CHANGELOG.md similarity index 93% rename from repos/effect/packages/atom/solid/CHANGELOG.md rename to packages/atom/solid/CHANGELOG.md index 5b0c6d470..3b20ba862 100644 --- a/repos/effect/packages/atom/solid/CHANGELOG.md +++ b/packages/atom/solid/CHANGELOG.md @@ -1,5 +1,26 @@ # @effect/atom-solid +## 4.0.5 + +### Patch Changes + +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/atom/solid/LICENSE b/packages/atom/solid/LICENSE similarity index 100% rename from repos/effect/packages/atom/solid/LICENSE rename to packages/atom/solid/LICENSE diff --git a/repos/effect/packages/atom/solid/README.md b/packages/atom/solid/README.md similarity index 100% rename from repos/effect/packages/atom/solid/README.md rename to packages/atom/solid/README.md diff --git a/repos/effect/packages/atom/solid/package.json b/packages/atom/solid/package.json similarity index 97% rename from repos/effect/packages/atom/solid/package.json rename to packages/atom/solid/package.json index 80949332f..9f614f0cd 100644 --- a/repos/effect/packages/atom/solid/package.json +++ b/packages/atom/solid/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-solid", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "SolidJS bindings for the Effect Atom modules", @@ -69,6 +69,6 @@ "@testing-library/jest-dom": "^7.0.1", "effect": "workspace:^", "jsdom": "~30.0.1", - "solid-js": "^1.9.15" + "solid-js": "^1.9.17" } } diff --git a/repos/effect/packages/atom/solid/src/Hooks.ts b/packages/atom/solid/src/Hooks.ts similarity index 100% rename from repos/effect/packages/atom/solid/src/Hooks.ts rename to packages/atom/solid/src/Hooks.ts diff --git a/repos/effect/packages/atom/solid/src/RegistryContext.ts b/packages/atom/solid/src/RegistryContext.ts similarity index 100% rename from repos/effect/packages/atom/solid/src/RegistryContext.ts rename to packages/atom/solid/src/RegistryContext.ts diff --git a/repos/effect/packages/atom/solid/src/index.ts b/packages/atom/solid/src/index.ts similarity index 100% rename from repos/effect/packages/atom/solid/src/index.ts rename to packages/atom/solid/src/index.ts diff --git a/repos/effect/packages/atom/solid/test/index.test.tsx b/packages/atom/solid/test/index.test.tsx similarity index 100% rename from repos/effect/packages/atom/solid/test/index.test.tsx rename to packages/atom/solid/test/index.test.tsx diff --git a/repos/effect/packages/atom/solid/tsconfig.json b/packages/atom/solid/tsconfig.json similarity index 100% rename from repos/effect/packages/atom/solid/tsconfig.json rename to packages/atom/solid/tsconfig.json diff --git a/repos/effect/packages/atom/solid/vitest.setup.ts b/packages/atom/solid/vitest.setup.ts similarity index 100% rename from repos/effect/packages/atom/solid/vitest.setup.ts rename to packages/atom/solid/vitest.setup.ts diff --git a/repos/effect/packages/atom/vue/CHANGELOG.md b/packages/atom/vue/CHANGELOG.md similarity index 93% rename from repos/effect/packages/atom/vue/CHANGELOG.md rename to packages/atom/vue/CHANGELOG.md index 14c477bcf..1cf87edc1 100644 --- a/repos/effect/packages/atom/vue/CHANGELOG.md +++ b/packages/atom/vue/CHANGELOG.md @@ -1,5 +1,26 @@ # @effect/atom-vue +## 4.0.5 + +### Patch Changes + +- Updated dependencies [[`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1), [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2), [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f), [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c), [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82), [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02), [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba), [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23), [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741), [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227), [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0), [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e), [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d), [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7), [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80), [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584), [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217), [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664), [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f), [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220), [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b), [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff), [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2), [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866), [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1), [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88), [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69), [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb), [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97), [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87), [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe), [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0), [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d), [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263), [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a), [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e), [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a), [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331), [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6), [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc), [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6)]: + - effect@4.0.5 + +## 4.0.4 + +### Patch Changes + +- Updated dependencies [[`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8), [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98), [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c), [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236), [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5), [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7), [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be), [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a), [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64), [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177), [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f), [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f), [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04), [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7), [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6), [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925), [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94), [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e), [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013), [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa), [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3), [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491), [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447), [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163), [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708), [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06), [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852)]: + - effect@4.0.4 + +## 4.0.3 + +### Patch Changes + +- Updated dependencies [[`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02), [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7), [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f), [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a), [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681), [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5), [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0), [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f), [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81), [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52), [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e), [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa), [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e), [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0), [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee), [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60), [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303), [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa), [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6), [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a), [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02), [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7), [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e), [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0), [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb), [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503), [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a), [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545), [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c), [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911), [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5), [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112), [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7), [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df), [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b), [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50), [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a), [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530), [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1), [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d), [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c), [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68), [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6), [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08), [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318), [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e)]: + - effect@4.0.3 + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/atom/vue/LICENSE b/packages/atom/vue/LICENSE similarity index 100% rename from repos/effect/packages/atom/vue/LICENSE rename to packages/atom/vue/LICENSE diff --git a/repos/effect/packages/atom/vue/README.md b/packages/atom/vue/README.md similarity index 100% rename from repos/effect/packages/atom/vue/README.md rename to packages/atom/vue/README.md diff --git a/repos/effect/packages/atom/vue/package.json b/packages/atom/vue/package.json similarity index 98% rename from repos/effect/packages/atom/vue/package.json rename to packages/atom/vue/package.json index 6d7ba713a..d983ef8ba 100644 --- a/repos/effect/packages/atom/vue/package.json +++ b/packages/atom/vue/package.json @@ -1,6 +1,6 @@ { "name": "@effect/atom-vue", - "version": "4.0.2", + "version": "4.0.5", "type": "module", "license": "MIT", "description": "Vue bindings for the Effect Atom modules", diff --git a/repos/effect/packages/atom/vue/src/index.ts b/packages/atom/vue/src/index.ts similarity index 100% rename from repos/effect/packages/atom/vue/src/index.ts rename to packages/atom/vue/src/index.ts diff --git a/repos/effect/packages/atom/vue/test/index.test.ts b/packages/atom/vue/test/index.test.ts similarity index 100% rename from repos/effect/packages/atom/vue/test/index.test.ts rename to packages/atom/vue/test/index.test.ts diff --git a/repos/effect/packages/atom/vue/tsconfig.json b/packages/atom/vue/tsconfig.json similarity index 100% rename from repos/effect/packages/atom/vue/tsconfig.json rename to packages/atom/vue/tsconfig.json diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/cli/.madgerc b/packages/cli/.madgerc deleted file mode 100644 index b407c6b4b..000000000 --- a/packages/cli/.madgerc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "detectiveOptions": { - "ts": { - "skipTypeImports": true - } - } -} diff --git a/packages/cli/.npmignore b/packages/cli/.npmignore deleted file mode 100644 index e4c612bb4..000000000 --- a/packages/cli/.npmignore +++ /dev/null @@ -1,14 +0,0 @@ -*.test.js -*.test.d.ts -tsconfig.build.json -*.log -tslint.json -.eslintrc.js -.eslintignore -.madgerc -.swcrc -.cache -coverage/ -.tsbuildinfo -.build/ -!dist/ diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md deleted file mode 100644 index 7313e09d9..000000000 --- a/packages/cli/CHANGELOG.md +++ /dev/null @@ -1,1787 +0,0 @@ -# @effect-app/cli - -## 2.1.0 - -### Minor Changes - -- 07dcd75: Add `effa sync` / `effa sync-diff` / `effa sync-push` subcommands for syncing content (architecture docs, e2e helpers, ts-plugins) from `effect-app/shared` into consuming projects per a project-side `.shared.json` lockfile. - - - `effa sync` — clone/checkout the shared repo at the pinned ref into `~/.cache/effa/shared/`, then copy the artifact map's files into the project (honoring `exclude`). - - `effa sync-diff` — sha256 compare each tracked file against the cache copy; reports `M` (modified locally), `D` (missing from project), `E` (excluded). - - `effa sync-push [--pr] [-m msg] [--branch name]` — branch in the cache off the pinned ref, copy modified project files in, commit, push. Optional `--pr` opens a PR via `gh pr create`. - - Lockfile shape: - - ```json - { - "repo": "github.com/effect-app/shared", - "ref": "", - "artifacts": { "": "" }, - "exclude": [""] - } - ``` - -- 97207c0: Add `sync-effect` CLI command to sync the Effect git subtree to the version pinned in package.json manifests. - -### Patch Changes - -- 1f9d7da: fix missing toCodecJson usages -- 32f05c8: Remove unused dependency declarations from package manifests. -- 6cfd83d: update effect to latest beta -- 8434b5f: preserve trailing newline when writing package.json files -- b9312df: Prevent subtree sync commands from importing upstream git tags. -- 68f1b39: Make `effa ue` update Effect and Effect App packages to the highest versions allowed by their existing semver ranges, including compatible prerelease updates. -- 505bfa9: Add concurrent decode helper APIs and migrate decode callsites to use them. - - - Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. - - Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. - - Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. - - Keep schema constructors free of hardcoded parse concurrency overrides. - -- 52b0b01: Beta25 -- 10b55ff: update packages -- 7b28548: Add `effa sync --update [--ref ]` to bump the pinned `ref` in `.shared.json` to the latest sha before syncing. Without `--ref` it resolves the shared repo's default branch HEAD; with `--ref` it resolves that ref (branches resolved against `origin/` so you get the latest remote commit). The resolved sha is written back to the lockfile. Omitting `--update` keeps the existing pin-and-sync behaviour. -- 18bae5b: Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - - Breaking API updates from this RC: - - - PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) - - `Config.Record` now returns a Config - - Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` - - `SchemaTransformation.transformOrFail` renamed to `transformEffect` - - `Fiber.currentSpan` moved to `fiber.cache.span` - - HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` - - Object JSON Schema now emits `additionalProperties: true` - - Schema `Union` AST stores matching as `options.mode` instead of `mode` - -- e2c9ebd: Require an explicit `EFFA_GIST_CACHE_ID`, remove cache Gist discovery and automatic creation, and initialize missing company pointer files once. -- 55c6572: update packages -- c991be1: update packages -- 8c645d5: update to latest effect -- a37aa38: Update to effect beta 43 -- c1e73de: -- d867272: the return of `Context` -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. -- 52b0b01: Update Effect dependencies to 4.0.0-beta.31 across workspace packages. -- eb28ea5: bogus -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- 52b0b01: Update Effect dependencies to 4.0.0-beta.28 across workspace packages. -- d31253f: Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. -- cec026d: update packages -- 8267e7d: update to latest effect -- 52b0b01: Update Effect dependencies to 4.0.0-beta.29 across workspace packages. -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. -- b8b9080: update packages -- e7ad78c: Add `sync-effect-app` to sync vendored Effect App libs by pinned package version. - The command prefers package-version git tags and supports `--ref latest` for main. -- e7ad78c: Sync Effect subtrees from a repository URL without requiring a local git remote. -- 6138bc7: `effa sync-push`: propagate locally-deleted synced files. Previously, files removed from the consuming project were silently skipped (the loop bailed when `destAbs` didn't exist). Now they're recorded as deletions, `git rm`'d in the cache branch, and listed in the PR body alongside modifications. -- 52b0b01: update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it -- fac725d: update effect to latest beta -- 52b0b01: update all teh tings -- c3299f7: update packages -- 6b57330: Upgrade Effect packages to npm `4.0.0`. -- e2d00b5: Upgrade Effect packages to npm `4.0.1`. -- 547e2e1: Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - - API adaptations for beta.107: - - - `concurrency: "inherit"` → `"unbounded"` - - `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` - - `Schema.LazyArbitrary` → `Schema.Arbitrary` - - `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) - - `SchemaIssue` constructors no longer take `Option` (annotations + input) - - filter meta via `annotations.representation` instead of `annotations.meta` - - `context.defaultValue` → `context.constructorDefault` (single Link) - - Class detection via `~constructor` + static `identifier` - - Redacted detection via `representation.id` - - localized StandardSchema hooks updated for new issue/input model - - provide `NodeCrypto.layer` for cluster sqlite tests - - default `sync-effect` subtree URL → `Effect-TS/effect` - -- 52b0b01: update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined -- Update effect packages to 4.0.0-beta.52 -- 3e855bc: Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - - Adapt the infra workflow engines to beta.83 API changes: - - - `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). - - `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). - -- 78d076a: Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). -- ffd140f: Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. -- e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. -- b6dda09: Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. -- 8bd5bfe: Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - - API adaptations for rc.112: - - - cluster encoded driver `resetAddress` → batched `resetAddresses` - - Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) - - Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization - - `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) - - JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) - -- 2231ef8: Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. - -## 2.1.0-beta.45 - -### Patch Changes - -- e2d00b5: Upgrade Effect packages to npm `4.0.1`. - -## 2.1.0-beta.44 - -### Patch Changes - -- 6b57330: Upgrade Effect packages to npm `4.0.0`. - -## 2.1.0-beta.43 - -### Patch Changes - -- 2231ef8: Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. - -## 2.1.0-beta.42 - -### Patch Changes - -- 18bae5b: Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - - Breaking API updates from this RC: - - - PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) - - `Config.Record` now returns a Config - - Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` - - `SchemaTransformation.transformOrFail` renamed to `transformEffect` - - `Fiber.currentSpan` moved to `fiber.cache.span` - - HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` - - Object JSON Schema now emits `additionalProperties: true` - - Schema `Union` AST stores matching as `options.mode` instead of `mode` - -## 2.1.0-beta.41 - -### Patch Changes - -- 8bd5bfe: Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - - API adaptations for rc.112: - - - cluster encoded driver `resetAddress` → batched `resetAddresses` - - Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) - - Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization - - `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) - - JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) - -## 2.1.0-beta.40 - -### Patch Changes - -- 547e2e1: Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - - API adaptations for beta.107: - - - `concurrency: "inherit"` → `"unbounded"` - - `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` - - `Schema.LazyArbitrary` → `Schema.Arbitrary` - - `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) - - `SchemaIssue` constructors no longer take `Option` (annotations + input) - - filter meta via `annotations.representation` instead of `annotations.meta` - - `context.defaultValue` → `context.constructorDefault` (single Link) - - Class detection via `~constructor` + static `identifier` - - Redacted detection via `representation.id` - - localized StandardSchema hooks updated for new issue/input model - - provide `NodeCrypto.layer` for cluster sqlite tests - - default `sync-effect` subtree URL → `Effect-TS/effect` - -## 2.1.0-beta.39 - -### Patch Changes - -- 68f1b39: Make `effa ue` update Effect and Effect App packages to the highest versions allowed by their existing semver ranges, including compatible prerelease updates. - -## 2.1.0-beta.38 - -### Patch Changes - -- e2c9ebd: Require an explicit `EFFA_GIST_CACHE_ID`, remove cache Gist discovery and automatic creation, and initialize missing company pointer files once. - -## 2.1.0-beta.37 - -### Patch Changes - -- b6dda09: Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 2.1.0-beta.36 - -### Patch Changes - -- e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. - -## 2.1.0-beta.35 - -### Patch Changes - -- ffd140f: Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 2.1.0-beta.34 - -### Patch Changes - -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. - -## 2.1.0-beta.33 - -### Patch Changes - -- 32f05c8: Remove unused dependency declarations from package manifests. - -## 2.1.0-beta.32 - -### Patch Changes - -- b9312df: Prevent subtree sync commands from importing upstream git tags. - -## 2.1.0-beta.31 - -### Patch Changes - -- e7ad78c: Add `sync-effect-app` to sync vendored Effect App libs by pinned package version. - The command prefers package-version git tags and supports `--ref latest` for main. -- e7ad78c: Sync Effect subtrees from a repository URL without requiring a local git remote. - -## 2.1.0-beta.30 - -### Patch Changes - -- 78d076a: Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). - -## 2.1.0-beta.29 - -### Patch Changes - -- 3e855bc: Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - - Adapt the infra workflow engines to beta.83 API changes: - - - `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). - - `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). - -## 2.1.0-beta.28 - -### Patch Changes - -- 55c6572: update packages - -## 2.1.0-beta.27 - -### Patch Changes - -- 7b28548: Add `effa sync --update [--ref ]` to bump the pinned `ref` in `.shared.json` to the latest sha before syncing. Without `--ref` it resolves the shared repo's default branch HEAD; with `--ref` it resolves that ref (branches resolved against `origin/` so you get the latest remote commit). The resolved sha is written back to the lockfile. Omitting `--update` keeps the existing pin-and-sync behaviour. - -## 2.1.0-beta.26 - -### Patch Changes - -- bogus - -## 2.1.0-beta.25 - -### Patch Changes - -- 6138bc7: `effa sync-push`: propagate locally-deleted synced files. Previously, files removed from the consuming project were silently skipped (the loop bailed when `destAbs` didn't exist). Now they're recorded as deletions, `git rm`'d in the cache branch, and listed in the PR body alongside modifications. - -## 2.1.0-beta.24 - -### Minor Changes - -- 07dcd75: Add `effa sync` / `effa sync-diff` / `effa sync-push` subcommands for syncing content (architecture docs, e2e helpers, ts-plugins) from `effect-app/shared` into consuming projects per a project-side `.shared.json` lockfile. - - - `effa sync` — clone/checkout the shared repo at the pinned ref into `~/.cache/effa/shared/`, then copy the artifact map's files into the project (honoring `exclude`). - - `effa sync-diff` — sha256 compare each tracked file against the cache copy; reports `M` (modified locally), `D` (missing from project), `E` (excluded). - - `effa sync-push [--pr] [-m msg] [--branch name]` — branch in the cache off the pinned ref, copy modified project files in, commit, push. Optional `--pr` opens a PR via `gh pr create`. - - Lockfile shape: - - ```json - { - "repo": "github.com/effect-app/shared", - "ref": "", - "artifacts": { "": "" }, - "exclude": [""] - } - ``` - -## 2.1.0-beta.23 - -### Patch Changes - -- b8b9080: update packages - -## 2.1.0-beta.22 - -### Patch Changes - -- 6cfd83d: update effect to latest beta - -## 2.1.0-beta.21 - -### Patch Changes - -- c991be1: update packages - -## 2.1.0-beta.20 - -### Patch Changes - -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. - -## 2.1.0-beta.19 - -### Patch Changes - -- 505bfa9: Add concurrent decode helper APIs and migrate decode callsites to use them. - - - Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. - - Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. - - Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. - - Keep schema constructors free of hardcoded parse concurrency overrides. - -## 2.1.0-beta.18 - -### Patch Changes - -- c1e73de: - -## 2.1.0-beta.17 - -### Patch Changes - -- 8434b5f: preserve trailing newline when writing package.json files - -## 2.1.0-beta.16 - -### Patch Changes - -- d31253f: Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. - -## 2.1.0-beta.15 - -### Patch Changes - -- c3299f7: update packages - -## 2.1.0-beta.14 - -### Patch Changes - -- Update effect packages to 4.0.0-beta.52 - -## 2.1.0-beta.13 - -### Minor Changes - -- 97207c0: Add `sync-effect` CLI command to sync the Effect git subtree to the version pinned in package.json manifests. - -## 2.0.1-beta.12 - -### Patch Changes - -- fac725d: update effect to latest beta - -## 2.0.1-beta.11 - -### Patch Changes - -- 8267e7d: update to latest effect - -## 2.0.1-beta.10 - -### Patch Changes - -- 1f9d7da: fix missing toCodecJson usages - -## 2.0.1-beta.9 - -### Patch Changes - -- cec026d: update packages - -## 2.0.1-beta.8 - -### Patch Changes - -- d867272: the return of `Context` - -## 2.0.1-beta.7 - -### Patch Changes - -- 10b55ff: update packages - -## 2.0.1-beta.6 - -### Patch Changes - -- a37aa38: Update to effect beta 43 - -## 2.0.1-beta.5 - -### Patch Changes - -- 8c645d5: update to latest effect - -## 2.0.1-beta.4 - -### Patch Changes - -- 601a1ff: update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it - -## 2.0.1-beta.3 - -### Patch Changes - -- 75c72ee: update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined - -## 2.0.1-beta.2 - -### Patch Changes - -- 1c3c541: Update Effect dependencies to 4.0.0-beta.31 across workspace packages. -- de2359d: Update Effect dependencies to 4.0.0-beta.28 across workspace packages. -- 0d72f34: Update Effect dependencies to 4.0.0-beta.29 across workspace packages. - -## 2.0.1-beta.1 - -### Patch Changes - -- 01c70d0: update all teh tings - -## 2.0.1-beta.0 - -### Patch Changes - -- 64786af: Beta25 - -## 2.0.0 - -### Major Changes - -- 7e9e02b: Update to Effect v4 - -## 1.29.2 - -### Patch Changes - -- 5880020: bootstrap - -## 1.29.1 - -### Patch Changes - -- 5d043d3: propagate exit code - -## 1.29.0 - -### Minor Changes - -- 1a102e2: update packages - -## 1.28.1 - -### Patch Changes - -- 358782e: update packages - -## 1.28.0 - -### Minor Changes - -- 07f1de5: update packages and tsconfigs - -## 1.27.0 - -### Minor Changes - -- b8f5e30: update packages - -## 1.26.5 - -### Patch Changes - -- 7da965e: better multi-tenant handling (multi env, different caches) - -## 1.26.4 - -### Patch Changes - -- 36aba87: move gh presence check when logging in because of freakin' services initializations - -## 1.26.3 - -### Patch Changes - -- 25d353c: proper login - -## 1.26.2 - -### Patch Changes - -- 73288cc: move GH check - -## 1.26.1 - -### Patch Changes - -- d1a6901: check gh presence - -## 1.26.0 - -### Minor Changes - -- 2bcc323: add cli gist command to work with github gist files - -## 1.25.0 - -### Minor Changes - -- 6d8c512: update packages - -## 1.24.0 - -### Minor Changes - -- 3d49d81: update packages - -## 1.23.25 - -### Patch Changes - -- e31d54f: remove out of date wiki commands - -## 1.23.24 - -### Patch Changes - -- f35da3c: porting more to eslint shared config package - -## 1.23.23 - -### Patch Changes - -- 8096576: new eslint shared config package - -## 1.23.22 - -### Patch Changes - -- a3ac3bb: fix export (?) - -## 1.23.21 - -### Patch Changes - -- f9aee77: some fixes - -## 1.23.20 - -### Patch Changes - -- f6a3328: export base eslint files so that we import them in projects - -## 1.23.19 - -### Patch Changes - -- 539e2f2: minor wiki command enhancements - -## 1.23.18 - -### Patch Changes - -- 84be367: add command to update all but effect/effect-app packages - -## 1.23.17 - -### Patch Changes - -- d1dd883: other fml fixes - -## 1.23.16 - -### Patch Changes - -- ccbf803: fml - -## 1.23.15 - -### Patch Changes - -- 8994599: fix some wiki commands - -## 1.23.14 - -### Patch Changes - -- 2cad28a: some fixes to wiki commands - -## 1.23.13 - -### Patch Changes - -- d9280c7: fix init wiki - -## 1.23.12 - -### Patch Changes - -- 9ff7964: add wiki-init - -## 1.23.11 - -### Patch Changes - -- 7c2e428: pnpm effa wiki update - -## 1.23.10 - -### Patch Changes - -- 8133014: cli doc always fetch remote to not miss any update (like github wiki interface ones) - -## 1.23.9 - -### Patch Changes - -- 35f8488: fix sync cli command - -## 1.23.8 - -### Patch Changes - -- 5576081: cli command for interacting with wiki (v1) - -## 1.23.7 - -### Patch Changes - -- 8cb893c: dprint rules the world - -## 1.23.6 - -### Patch Changes - -- e81f093: remove watch add nuke - -## 1.23.5 - -### Patch Changes - -- de35a0f: add monitors for packagejson -- 8e1543e: add wrapping of child commands - -## 1.23.4 - -### Patch Changes - -- ca792d7: add some debug prints - -## 1.23.3 - -### Patch Changes - -- 62b36b0: fix monitors blocking next commands by forking them - -## 1.23.2 - -### Patch Changes - -- c12a49b: effect app cli to effect cli - -## 1.23.1 - -### Patch Changes - -- 20293b1: add ncu:effect and ncu:effect-app commands - -## 1.23.0 - -### Minor Changes - -- 0f2b2cf: update packages - -## 1.22.1 - -### Patch Changes - -- b095bb5: tscp - -## 1.22.0 - -### Minor Changes - -- 2c217ce: update packages - -## 1.21.1 - -### Patch Changes - -- b771967: update packages - -## 1.21.0 - -### Minor Changes - -- eac9c67: update packages - -## 1.20.1 - -### Patch Changes - -- 49d93a5: update packages - -## 1.20.0 - -### Minor Changes - -- b1d4af4: housekeeping: update packages - -## 1.19.0 - -### Minor Changes - -- 8cb33d5: update packages - -## 1.18.5 - -### Patch Changes - -- 1e1f32b: housekeeping: update packages - -## 1.18.4 - -### Patch Changes - -- update packages - -## 1.18.3 - -### Patch Changes - -- 0fcab38: update packages - -## 1.18.2 - -### Patch Changes - -- ae71bd9: add missing link for vue-componentsy - -## 1.18.1 - -### Patch Changes - -- fdb1107: filter dir - -## 1.18.0 - -### Minor Changes - -- 14ea804: housekeeping: update packages - -## 1.17.1 - -### Patch Changes - -- 4c8dfb4: update packages - -## 1.17.0 - -### Minor Changes - -- e42d5fb: update to effect 3.14 with native rpc2 - -## 1.16.2 - -### Patch Changes - -- 36996ed: housekeeping: update packages - -## 1.16.1 - -### Patch Changes - -- 63e966a: update packages - -## 1.16.0 - -### Minor Changes - -- 8be2984: housekeeping: update packages - -## 1.15.0 - -### Minor Changes - -- 1721761: improve: only update nearest controllers/routes.ts - -## 1.14.0 - -### Minor Changes - -- 9b2e385: update packages - -## 1.13.2 - -### Patch Changes - -- 9d11dfb: housekeeping: update packages - -## 1.13.1 - -### Patch Changes - -- ab9d1ea: housekeeping: update packages - -## 1.13.0 - -### Minor Changes - -- c8ebfb4: update packages - -## 1.12.0 - -### Minor Changes - -- 67023f8: kill \_cjs - -## 1.11.4 - -### Patch Changes - -- 9f504c6: update packages - -## 1.11.3 - -### Patch Changes - -- 750b46a: update packages - -## 1.11.2 - -### Patch Changes - -- 1bfc3d8: update packages - -## 1.11.1 - -### Patch Changes - -- 5ef6481: update packages - -## 1.11.0 - -### Minor Changes - -- 29a1a65: update packages - -## 1.10.0 - -### Minor Changes - -- d84aef7: update packages - -## 1.9.0 - -### Minor Changes - -- cc2c43e: update packages - -## 1.8.0 - -### Minor Changes - -- bump - -## 1.7.1 - -### Patch Changes - -- c971d73: update packages - -## 1.7.0 - -### Minor Changes - -- update - -## 1.6.0 - -### Minor Changes - -- f19762b: update packages - -## 1.5.1 - -### Patch Changes - -- 31c79d7: exit - -## 1.5.0 - -### Minor Changes - -- 4728346: add 'sync' command - -## 1.4.1 - -### Patch Changes - -- 412cb3d: update packages - -## 1.4.0 - -### Minor Changes - -- 590e5bf: update packages - -## 1.3.0 - -### Minor Changes - -- d4ab73f: update packages - -## 1.2.1 - -### Patch Changes - -- 19ea035: update packages - -## 1.2.0 - -### Minor Changes - -- d5623a6: update packages - -## 1.1.2 - -### Patch Changes - -- f68dbd2: update packages - -## 1.1.1 - -### Patch Changes - -- a66651b: m - -## 1.1.0 - -### Minor Changes - -- 964b0d2: refactor: merge adapters into infra - -## 1.0.0 - -### Major Changes - -- 13abe12: refactor: merge core into prelude - -## 0.90.0 - -### Minor Changes - -- d0471b1: refactor: merge @effect-app/schema into core - -## 0.89.0 - -### Minor Changes - -- 3872061: migrate to effect/Schema - -## 0.88.0 - -### Minor Changes - -- 480f0ef: add new controller setup support - -## 0.87.1 - -### Patch Changes - -- cda1477: update packages - -## 0.87.0 - -### Minor Changes - -- update packages - -## 0.86.0 - -### Minor Changes - -- c56949e: drop fluent extensions - -## 0.85.0 - -### Minor Changes - -- b9b64b8: housekeeping: update packages - -## 0.84.0 - -### Minor Changes - -- 65dcc73: update packages - -## 0.83.0 - -### Minor Changes - -- 4d2b804: housekeeping: update packages - -## 0.82.0 - -### Minor Changes - -- 190b139: update packages - -## 0.81.1 - -### Patch Changes - -- c710953: update packagesy - -## 0.81.0 - -### Minor Changes - -- 1018e58: update packages - -## 0.80.1 - -### Patch Changes - -- 11982eb: fix cli - -## 0.80.0 - -### Minor Changes - -- c5f5223: update packages - -## 0.79.3 - -### Patch Changes - -- e6eecd2: update packages - -## 0.79.2 - -### Patch Changes - -- ff - -## 0.79.1 - -### Patch Changes - -- fix - -## 0.79.0 - -### Minor Changes - -- 2143dd9: update packages - -## 0.78.1 - -### Patch Changes - -- f6066ac: update packages - -## 0.78.0 - -### Minor Changes - -- 79f58af: update packages - -## 0.77.2 - -### Patch Changes - -- b3ebc41: update packages - -## 0.77.1 - -### Patch Changes - -- f569681: update packages - -## 0.77.0 - -### Minor Changes - -- 613c148: update packages - -## 0.76.0 - -### Minor Changes - -- 8d0c511: update packages - -## 0.75.0 - -### Minor Changes - -- 9cc6f86: update packages - -## 0.74.1 - -### Patch Changes - -- 2c661c1: update packages - -## 0.74.0 - -### Minor Changes - -- 9ac9416: update packages - -## 0.73.2 - -### Patch Changes - -- 3411e78: update packages - -## 0.73.1 - -### Patch Changes - -- 128f636: update packages - -## 0.73.0 - -### Minor Changes - -- 2e29520: update packages - -## 0.72.1 - -### Patch Changes - -- c4dbb6c: update packages - -## 0.72.0 - -### Minor Changes - -- 0d1b378: update to latest schema - -## 0.71.0 - -### Minor Changes - -- 290a709: update packages - -## 0.70.0 - -### Minor Changes - -- ecb05f6: update packages - -## 0.69.0 - -### Minor Changes - -- revert form changes - -## 0.68.0 - -### Minor Changes - -- a9a81a1: cleanup ext - -## 0.67.2 - -### Patch Changes - -- fix - -## 0.67.1 - -### Patch Changes - -- 8dc2b62: improve link - -## 0.67.0 - -### Minor Changes - -- 7a3f05c: update packages - -## 0.66.0 - -### Minor Changes - -- bbaa558: bs - -## 0.65.0 - -### Minor Changes - -- e5b0d8e: update packages -- 2e2fa98: update packages - -## 0.64.0 - -### Minor Changes - -- 494842c: update packages - -## 0.63.1 - -### Patch Changes - -- 0334285: update packages - -## 0.63.0 - -### Minor Changes - -- 51253a2: update packages - -## 0.62.0 - -### Minor Changes - -- update packages - -## 0.61.0 - -### Minor Changes - -- d0a1774: update ts to 5.4.1-rc - -## 0.60.0 - -### Minor Changes - -- 7346691: update packages - -## 0.59.0 - -### Minor Changes - -- e8e8ee2: update packages - -## 0.58.0 - -### Minor Changes - -- 117be64: bump - -## 0.57.0 - -### Minor Changes - -- A new day - -## 0.56.0 - -### Minor Changes - -- c070e0d: update effect - -## 0.55.1 - -### Patch Changes - -- b7daa54: improve supported commands prompt - -## 0.55.0 - -### Minor Changes - -- db67c43: update bins - -## 0.54.0 - -### Minor Changes - -- 57fcfef: update packages - -## 0.53.2 - -### Patch Changes - -- 1168eb4: add link/unlink - -## 0.53.1 - -### Patch Changes - -- c550140: fix - -## 0.53.0 - -### Minor Changes - -- 25123dc: improve cli frontend - -## 0.52.0 - -### Minor Changes - -- 6e5c606: cl - -## 0.51.0 - -### Minor Changes - -- de336f6: src - -## 0.50.1 - -### Patch Changes - -- f10e630: update packages - -## 0.50.0 - -### Minor Changes - -- 47f46f0: use actual action name casing so that editor go to works. - -## 0.49.1 - -### Patch Changes - -- 7b40fa7: use full path - -## 0.49.0 - -### Minor Changes - -- m - -## 0.48.0 - -### Minor Changes - -- bs - -## 0.47.0 - -### Minor Changes - -- c8a07bc: fix path - -## 0.46.0 - -### Minor Changes - -- e5cdd42: update packages - -## 0.45.0 - -### Minor Changes - -- update to latest Effect "minor" - -## 0.44.0 - -### Minor Changes - -- c29b6ff: bump - -## 0.43.0 - -### Minor Changes - -- bump - -## 0.42.1 - -### Patch Changes - -- 0a898f8: update packages - -## 0.42.0 - -### Minor Changes - -- 449b343: update packages - -## 0.41.0 - -### Minor Changes - -- 2fd03d3: update packages - -## 0.40.0 - -### Minor Changes - -- a30db88: update packages - -## 0.39.0 - -### Minor Changes - -- 3eb4057: update packages - -## 0.38.0 - -### Minor Changes - -- 4c915ea: update packages - -## 0.37.0 - -### Minor Changes - -- a894a63: update packages - -## 0.36.0 - -### Minor Changes - -- 7945f38: update packages - -## 0.35.0 - -### Minor Changes - -- 63425a7: update packages - -## 0.34.0 - -### Minor Changes - -- 82f0f0d: update packages - -## 0.33.1 - -### Patch Changes - -- cf6ae3c: update packages - -## 0.33.0 - -### Minor Changes - -- 46bd7be: update packages - -## 0.32.0 - -### Minor Changes - -- a681ab6: update packages - -## 0.31.0 - -### Minor Changes - -- a9e8b8f: update packages - -## 0.30.0 - -### Minor Changes - -- align v - -## 0.29.0 - -### Minor Changes - -- align v - -## 0.28.0 - -### Minor Changes - -- 1f6b9a1: update packages - -## 0.27.0 - -### Minor Changes - -- c528c33: initial alignment of Schema with effect/schema - -## 0.26.0 - -### Minor Changes - -- 6c180253: cleanup - -## 0.25.1 - -### Patch Changes - -- 7ff1f6f9: update packages - -## 0.25.0 - -### Minor Changes - -- 0a4bfbc7: update packages - -## 0.24.0 - -### Minor Changes - -- 90af6a41: update packages - -## 0.23.1 - -### Patch Changes - -- aa3c98d1: update packages - -## 0.23.0 - -### Minor Changes - -- update packages - -## 0.22.1 - -### Patch Changes - -- 780a6718: update packages - -## 0.22.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.21.2 - -### Patch Changes - -- 838eeb10: update packages - -## 0.21.1 - -### Patch Changes - -- fix tsplus.config for pnpm - -## 0.21.0 - -### Minor Changes - -- update packages - -## 0.20.0 - -### Minor Changes - -- add match and update packages - -## 0.19.0 - -### Minor Changes - -- update packages - -## 0.18.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.17.0 - -### Minor Changes - -- c80b9e7a: Updated to latest Effect - -## 0.16.0 - -### Minor Changes - -- housekeeping: update packages - -## 0.15.0 - -### Minor Changes - -- Update packages - -## 0.14.0 - -### Minor Changes - -- Update depdendencies - -## 0.13.0 - -### Minor Changes - -- update dependencies - -## 0.12.0 - -### Minor Changes - -- Update dependencies - -## 0.11.0 - -### Minor Changes - -- Update packages - -## 0.10.0 - -### Minor Changes - -- update dependencies - -## 0.9.0 - -### Minor Changes - -- Update packages - -## 0.8.0 - -### Minor Changes - -- Update to latest effect - -## 0.7.0 - -### Minor Changes - -- Update packages - -## 0.6.0 - -### Minor Changes - -- update packages - -## 0.5.0 - -### Minor Changes - -- Update tsplus with companion instance extensions - -## 0.4.0 - -### Minor Changes - -- Update Effect - -## 0.3.1 - -### Patch Changes - -- 02997e8f: FUP - -## 0.3.0 - -### Minor Changes - -- 4d49f303: Upgrade to @effect/data - -## 0.2.7 - -### Patch Changes - -- 82a54d23: Cleanup - -## 0.2.6 - -### Patch Changes - -- 89ee2cc5: Add root index.ts support - -## 0.2.5 - -### Patch Changes - -- 9ae82030: fix index - -## 0.2.4 - -### Patch Changes - -- fe896f8c: Fix m - -## 0.2.3 - -### Patch Changes - -- 5a37df3f: Improve cli - -## 0.2.2 - -### Patch Changes - -- 1cd7bbd9: write package.json at start - -## 0.2.1 - -### Patch Changes - -- 847c1eb0: Improve cli dirs - -## 0.2.0 - -### Minor Changes - -- Update annotations and leverage ForceLazy on Effect \_\_call - -## 0.1.6 - -### Patch Changes - -- 912ba66c: Update packages - -## 0.1.5 - -### Patch Changes - -- 2cc4390c: Fix cli - -## 0.1.4 - -### Patch Changes - -- dccdfe2a: cli: add packagejson levels - -## 0.1.3 - -### Patch Changes - -- 2946e475: Fix - -## 0.1.2 - -### Patch Changes - -- 5fd3e5f5: Add package json exports support - -## 0.1.1 - -### Patch Changes - -- f8d2dea7: Add index commands - -## 0.1.0 - -### Minor Changes - -- 9150d615: Prepare CLI for multi-command - -## 0.0.2 - -### Patch Changes - -- Initial release diff --git a/packages/cli/bin.js b/packages/cli/bin.js deleted file mode 100755 index c37c4113d..000000000 --- a/packages/cli/bin.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -import "./src/index.ts" diff --git a/packages/cli/package.json b/packages/cli/package.json deleted file mode 100644 index 2f6f6d1c7..000000000 --- a/packages/cli/package.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "name": "@effect-app/cli", - "version": "2.1.0", - "license": "MIT", - "type": "module", - "repository": { - "type": "git", - "url": "https://github.com/effect-app/libs.git", - "directory": "packages/cli" - }, - "bin": { - "effect-app": "./bin.js", - "effa": "./bin.js", - "effect-app-cli": "./bin.js" - }, - "dependencies": { - "@effect/platform-node": "4.0.2", - "effect": "4.0.2", - "js-yaml": "4.2.0" - }, - "devDependencies": { - "@types/js-yaml": "^4.0.9", - "@types/node": "25.9.1", - "typescript": "npm:@typescript/native-preview@beta", - "vitest": "^4.1.7" - }, - "typesVersions": { - "*": { - "*": [ - "src/*" - ] - } - }, - "exports": { - ".": { - "types": "./src/index.ts", - "default": "./src/index.ts" - }, - "./argv-patch": { - "types": "./src/argv-patch.ts", - "default": "./src/argv-patch.ts" - }, - "./extract": { - "types": "./src/extract.ts", - "default": "./src/extract.ts" - }, - "./gist": { - "types": "./src/gist.ts", - "default": "./src/gist.ts" - }, - "./os-command": { - "types": "./src/os-command.ts", - "default": "./src/os-command.ts" - }, - "./shared": { - "types": "./src/shared.ts", - "default": "./src/shared.ts" - }, - "./sync-effect-subtree": { - "types": "./src/sync-effect-subtree.ts", - "default": "./src/sync-effect-subtree.ts" - }, - "./sync-effect-app-subtree": { - "types": "./src/sync-effect-app-subtree.ts", - "default": "./src/sync-effect-app-subtree.ts" - }, - "./sync-shared": { - "types": "./src/sync-shared.ts", - "default": "./src/sync-shared.ts" - } - }, - "scripts": { - "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && pnpm check", - "check": "tsgo --build", - "build": "pnpm build:tsc", - "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsgo -w", - "clean": "rm -rf dist", - "clean-dist": "sh ../../scripts/clean-dist.sh", - "circular": "pnpm circular:src && pnpm circular:dist", - "circular:src": "madge --circular --ts-config ./tsconfig.json --extensions ts ./src", - "circular:dist": "madge --circular --extensions js ./dist", - "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsgo --noEmit", - "lint": "oxlint --quiet --type-aware ./src && pnpm exec dprint check --config ../../dprint.jsonc .", - "lint-fix": "oxlint --quiet --type-aware --fix ./src && pnpm exec dprint fmt --config ../../dprint.jsonc .", - "test": "vitest", - "test:run": "pnpm run test run --passWithNoTests", - "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", - "ncu": "ncu", - "pub": "pnpm publish --access public", - "prepack": "node ../../scripts/stagePublishPackage.mjs prepack", - "postpack": "node ../../scripts/stagePublishPackage.mjs postpack", - "postpublish": "node ../../scripts/stagePublishPackage.mjs postpublish" - }, - "gitHead": "bd8e27eea3eff97db8739d577d67e7336c078d28", - "sideEffects": [ - "./dist/faker.js", - "./faker", - "./src/faker", - "./dist/faker", - "./dist/src/faker.js", - "./dist/faker.js" - ], - "publishConfig": { - "directory": ".publish", - "linkDirectory": false, - "bin": { - "effect-app": "./dist/index.js", - "effa": "./dist/index.js", - "effect-app-cli": "./dist/index.js" - }, - "typesVersions": { - "*": { - "*": [ - "dist/*" - ] - } - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./argv-patch": { - "types": "./dist/argv-patch.d.ts", - "default": "./dist/argv-patch.js" - }, - "./extract": { - "types": "./dist/extract.d.ts", - "default": "./dist/extract.js" - }, - "./gist": { - "types": "./dist/gist.d.ts", - "default": "./dist/gist.js" - }, - "./os-command": { - "types": "./dist/os-command.d.ts", - "default": "./dist/os-command.js" - }, - "./shared": { - "types": "./dist/shared.d.ts", - "default": "./dist/shared.js" - }, - "./sync-effect-subtree": { - "types": "./dist/sync-effect-subtree.d.ts", - "default": "./dist/sync-effect-subtree.js" - }, - "./sync-effect-app-subtree": { - "types": "./dist/sync-effect-app-subtree.d.ts", - "default": "./dist/sync-effect-app-subtree.js" - }, - "./sync-shared": { - "types": "./dist/sync-shared.d.ts", - "default": "./dist/sync-shared.js" - } - } - } -} diff --git a/packages/cli/src/argv-patch.ts b/packages/cli/src/argv-patch.ts deleted file mode 100644 index 242c56132..000000000 --- a/packages/cli/src/argv-patch.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Join wrap args into a single argv element so that -// `effect-app-cli index-multi tsc --build` works without quoting. -// The new effect/cli lexer classifies --flags as LongOption tokens -// and discards unrecognized ones at the subcommand level. -// By joining all args after the subcommand into one string, the lexer -// sees a single Value token instead of separate LongOption tokens. - -const wrapCommands = new Set(["index-multi", "packagejson", "packagejson-packages"]) - -export const patchArgvForWrapCommands = (argv: Array): void => { - const subIdx = argv.findIndex((a, i) => i >= 2 && wrapCommands.has(a)) - if (subIdx === -1 || subIdx + 1 >= argv.length) return - - const wrapArgs = argv.splice(subIdx + 1) - argv.push(wrapArgs.join(" ")) -} diff --git a/packages/cli/src/extract.ts b/packages/cli/src/extract.ts deleted file mode 100644 index ecc940ae2..000000000 --- a/packages/cli/src/extract.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as EffectArray from "effect/Array" -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import { pipe } from "effect/Function" -import * as Order from "effect/Order" -import * as Path from "effect/Path" -import type * as PlatformError from "effect/PlatformError" - -/** - * Generates package.json exports mappings for TypeScript modules - * This function finds all .ts files in src/ (excluding test files) and generates - * JSON export entries that map source files to their compiled .js and .d.ts outputs - * - * Example output: - * "./utils/helper": { "types": "./dist/utils/helper.d.ts", "default": "./dist/utils/helper.js" }, - * - * This allows users to import individual modules instead of the entire package: - * import { helper } from 'package/utils/helper' instead of 'package' - */ -export const ExtractExportMappingsService = Effect.fn("effa-cli.extractExportMappings")(function*() { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - - return Effect.fn("effa-cli.extractExportMappings.service")(function*(cwd: string) { - // @effect-diagnostics-next-line effectFnOpportunity:off - const findTsFiles = (dir: string): Effect.Effect => - Effect.gen(function*() { - const entries = yield* fs.readDirectory(dir) - - const results = yield* Effect.all( - entries.map(Effect.fnUntraced(function*(entry) { - const fullPath = path.join(dir, entry) - const stat = yield* fs.stat(fullPath) - - if (stat.type === "Directory") { - return yield* findTsFiles(fullPath) - } else if (entry.endsWith(".ts") && !entry.includes(".test.")) { - return [fullPath] - } - return [] - })) - ) - - return EffectArray.flatten(results) - }) - - const srcDir = path.join(cwd, "src") - - // Check if src directory exists - const srcExists = yield* fs.exists(srcDir) - if (!srcExists) { - return "" - } - - const tsFiles = yield* findTsFiles(srcDir) - - const exportMappings = tsFiles.map((filePath) => { - // Get relative path from src directory (like bash script's cut -c 5-) - const relativePath = path.relative(srcDir, filePath) - const exportKey = `./${relativePath.replace(/\.ts$/, "")}` - const distPath = `./dist/${relativePath.replace(/\.ts$/, ".js")}` - const typesPath = `./dist/${relativePath.replace(/\.ts$/, ".d.ts")}` - - return `"${exportKey}": { "types": "${typesPath}", "default": "${distPath}" }` - }) - - const sortedMappings = pipe( - exportMappings, - EffectArray.sort(Order.String), - EffectArray.join(",\n") - ) - - return sortedMappings - }) -})() diff --git a/packages/cli/src/gist.ts b/packages/cli/src/gist.ts deleted file mode 100644 index f1c5ca7bb..000000000 --- a/packages/cli/src/gist.ts +++ /dev/null @@ -1,748 +0,0 @@ -/* eslint-disable no-constant-binary-expression */ -/* eslint-disable no-empty-pattern */ -// import necessary modules from the libraries -import * as Array from "effect/Array" -import * as Config from "effect/Config" -import * as Context from "effect/Context" -import * as Data from "effect/Data" -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import { pipe } from "effect/Function" -import * as Layer from "effect/Layer" -import * as Option from "effect/Option" -import * as Path from "effect/Path" -import * as Redacted from "effect/Redacted" -import * as Result from "effect/Result" -import * as Schema from "effect/Schema" -import * as SchemaIssue from "effect/SchemaIssue" -import * as SchemaTransformation from "effect/SchemaTransformation" -import * as SynchronizedRef from "effect/SynchronizedRef" - -import * as yaml from "js-yaml" -import path from "path" -import { RunCommandService } from "./os-command.ts" - -export class GistError extends Data.TaggedError("GistError")<{ message: string }> {} - -// -// -// Schemas -// - -export class GistEntry extends Schema.Opaque()(Schema.Struct({ - description: Schema.String, - public: Schema.Boolean, - company: Schema.String, - files: Schema.Array(Schema.String) -})) {} - -/** - * Extended gist entry that validates file_name uniqueness and extracts base filenames. - * - * GitHub Gists have a flat file structure and do not support directories/folders. - * All files within a gist must exist in the same namespace, meaning that files - * with identical names will collide, even if they originate from different local - * directories. When multiple files share the same basename, GitHub will either: - * - Reject the gist creation - * - Silently overwrite files (last one wins) - * - Display unpredictable behavior - * - * This validation prevents such collisions by detecting when multiple file paths - * would result in the same file_name when flattened to the gist structure. - * - * @example - * // These paths would collide in a gist - * ["src/config.json", "dist/config.json"] // Both become "config.json" - * - * @see {@link https://docs.github.com/articles/creating-gists | GitHub Gist Documentation} - * @see {@link https://github.com/orgs/community/discussions/29584 | Community Discussion on Gist Folder Support} - */ -export class GistEntryDecoded extends Schema.Opaque()( - GistEntry.pipe( - Schema.decodeTo( - Schema.Struct({ - description: Schema.String, - public: Schema.Boolean, - company: Schema.String, - files: Schema.Array(Schema.String), - files_with_name: Schema.Array(Schema.Struct({ - path: Schema.String, - name: Schema.String - })) - }), - SchemaTransformation.transformEffect({ - decode: Effect.fnUntraced(function*(entry) { - const files_with_name = entry.files.map((file) => ({ - path: file, - name: path.basename(file) // <-- I'm using Node's path module here so that this schema works without requirements on Effect's Path module - })) - - // check for duplicate file names - const nameMap = new Map() - for (const { name, path: filePath } of files_with_name) { - if (!nameMap.has(name)) { - nameMap.set(name, []) - } - nameMap.get(name)!.push(filePath) - } - - // find duplicates and collect all collision messages - const messages: string[] = [] - for (const [fileName, paths] of nameMap.entries()) { - if (paths.length > 1) { - messages.push( - `Duplicate file name detected: "${fileName}". Colliding paths: ${paths.join(", ")}` - ) - } - } - - // if there are any collisions, fail with a combined message - if (messages.length > 0) { - return yield* Effect.fail( - new SchemaIssue.InvalidValue({ message: messages.join("; ") }, entry.files) - ) - } - - return yield* Effect.succeed({ ...entry, files_with_name }) - }), - encode: ({ files_with_name: _, ...entry }) => Effect.succeed(entry) - }) - ) - ) -) {} - -export class GistYAML extends Schema.Opaque()(Schema.Struct({ - gists: Schema.optional(Schema.NullOr( - Schema.Record(Schema.String, GistEntryDecoded) - )), - settings: Schema.Struct({ - token_env: Schema.String, - base_directory: Schema.String - }) -})) {} - -/** - * Cache entry representing a gist mapping. - * Each entry contains the gist's human-readable name and GitHub ID. - */ -export class GistCacheEntry extends Schema.Opaque()(Schema.Struct({ - name: Schema.String, - id: Schema.String -})) {} - -export const GistCacheEntries = Schema.Array(GistCacheEntry) -export interface GistCacheEntries extends Schema.Schema.Type {} - -/** - * Gist cache mapping YAML configuration names to GitHub gist IDs with company awareness. - * - * Since GitHub gists don't have user-defined names, we maintain a cache - * that maps the human-readable names from our YAML config to actual gist IDs. - * Each cache entry is associated with a company, enabling multi-tenant operations. - * This allows us to: - * - Update existing gists instead of creating duplicates - * - Clean up obsolete entries when gists are removed from config - * - Persist the name->ID mapping across CLI runs - * - Isolate gist operations by company context - * - * The cache itself is stored as a secret GitHub gist for persistence. - */ -export class GistCache { - entries: GistCacheEntries - gist_id: string - - company: string - - constructor({ company, entries, gist_id }: { entries: GistCacheEntries; gist_id: string; company: string }) { - this.entries = entries - this.gist_id = gist_id - this.company = company - } -} - -// -// -// Errors -// -class GistYAMLError extends Data.TaggedError("GistYAMLError")<{ - readonly message: string -}> {} - -// -// -// Services -// - -export class GHGistService extends Context.Service()("GHGistService", { - make: Effect.gen(function*() { - const { runGetExitCode, runGetString } = yield* RunCommandService - - // the client cannot recover from PlatformErrors, so we convert failures into defects to clean up the signatures - const runGetExitCodeSuppressed = (...args: Parameters) => { - return runGetExitCode(...args).pipe( - Effect.mapError((e) => `Command failed: ${args.join(" ")}\nError: ${e.message}`), - Effect.orDie, - Effect.asVoid - ) - } - - // the client cannot recover from PlatformErrors, so we convert failures into defects to clean up the signatures - const runGetStringSuppressed = (...args: Parameters) => { - return runGetString(...args).pipe( - Effect.mapError((e) => `Command failed: ${args.join(" ")}\nError: ${e.message}`), - Effect.orDie - ) - } - - /** - * Extracts the Gist ID from a given GitHub Gist URL: https://gist.github.com/user/ID - * @param url - The full URL of the GitHub Gist. - * @returns An Option containing the Gist ID if extraction is successful, otherwise None. - */ - function extractGistIdFromUrl(url: string) { - const gist_id = url.trim().split("/").pop() - return gist_id && gist_id.length > 0 ? Option.some(gist_id) : Option.none() - } - - const loadGistCache = Effect.fn("effa-cli.gist.loadGistCache")( - function*(cacheGistId: string, company: string) { - const filesInCache = yield* runGetStringSuppressed(`gh gist view ${cacheGistId} --files`).pipe( - Effect.map((files) => - files - .trim() - .split("\n") - .map((f) => f.trim()) - ) - ) - - if (!filesInCache.includes(`${company}.json`)) { - yield* Effect.logInfo(`Cache for company ${company} not found, creating company-specific cache file...`) - yield* runGetStringSuppressed( - `echo "[]" | gh gist edit ${cacheGistId} -a ${company}.json -` - ) - return new GistCache({ entries: [], gist_id: cacheGistId, company }) - } - - const cacheContent = yield* runGetStringSuppressed(`gh gist view ${cacheGistId} -f "${company}.json"`) - - const entries = yield* pipe( - cacheContent, - Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.toCodecJson(GistCacheEntries))), - Effect.orDie - ) - - return new GistCache({ entries, gist_id: cacheGistId, company }) - } - ) - - const saveGistCache = Effect.fn("effa-cli.gist.saveGistCache")( - function*(cache: GistCache) { - const cacheJson = yield* pipe( - cache.entries, - Schema.encodeUnknownEffect(Schema.fromJsonString(Schema.toCodecJson(GistCacheEntries))), - // cannot recover from parse errors in any case, better to die here instead of cluttering the signature - Effect.orDie - ) - - yield* runGetExitCodeSuppressed( - `echo '${cacheJson}' | gh gist edit ${cache.gist_id} -f ${cache.company}.json -` - ) - } - ) - - const createGistWithFiles = Effect.fn("GHGistService.createGistWithFiles")( - function*({ description, env, files, gist_name, is_public }: { - gist_name: string - description: string - files: { - path: string - name: string - }[] - is_public: boolean - env: string - }) { - yield* Effect.logInfo(`Creating gist ${gist_name} with ${files.length} file(s)`) - - const ghCommand = [ - "gh", - "gist", - "create", - `--desc="${description}"`, - is_public ? "--public" : "", - ...files.map((file) => `"${file.path}"`) - ] - .filter((x) => !!x) - .join(" ") - - // create and capture the created gist URL - const gistUrl = yield* runGetStringSuppressed(ghCommand) - - // extract ID from URL - const gistNameId = yield* pipe( - gistUrl, - extractGistIdFromUrl, - Option.match({ - onNone: () => Effect.die(`Failed to extract gist ID from URL: ${gistUrl}`), - onSome: (id) => - Effect - .succeed( - { name: gist_name, id } - ) - .pipe(Effect.tap(Effect.logInfo(`Created gist with ID ${id}`))) - }) - ) - - // rename all files to include environment prefix for multi-environment support - for (const file of files) { - const originalName = file.name - const name_with_env = `${env}.${originalName}` - const ghRenameCommand = [ - "gh", - "gist", - "rename", - gistNameId.id, - originalName, - name_with_env - ] - .join(" ") - - yield* Effect.logInfo(`Renaming file ${originalName} to ${name_with_env} in gist ${gist_name}`) - yield* runGetStringSuppressed(ghRenameCommand) - } - - return gistNameId - } - ) - - const getGistFileNames = Effect.fn("getGistFileNames")( - function*({ env, gist_id, gist_name }: { - gist_id: string - gist_name: string - env: string - }) { - yield* Effect.logInfo(`Retrieving file names from gist ${gist_name} with ID ${gist_id}`) - const output = yield* runGetStringSuppressed(`gh gist view ${gist_id} --files`) - - // filter file names by environment prefix and remove the prefix - // files in gists are prefixed with "env." to support multiple environments - return Array.filterMap(output.trim().split("\n"), (fn) => { - const fnTrimmed = fn.trim() - if (!fnTrimmed.startsWith(env + ".")) return Result.fail(fn) - return Result.succeed(fnTrimmed.substring(env.length + 1)) // remove env prefix and dot - }) - } - ) - - const removeFileFromGist = Effect.fn("removeFileFromGist")( - function*({ env, file_name, gist_id, gist_name }: { - gist_id: string - gist_name: string - file_name: string - env: string - }) { - const name_with_env = `${env}.${file_name}` - yield* Effect.logInfo(`Removing file ${name_with_env} from gist ${gist_name}`) - return yield* runGetExitCodeSuppressed(`gh gist edit ${gist_id} --remove "${name_with_env}"`) - } - ) - - const updateFileOfGist = Effect.fn("updateFileOfGist")( - function*({ env, file_name, file_path, gist_id, gist_name }: { - gist_id: string - gist_name: string - file_name: string - file_path: string - env: string - }) { - const name_with_env = `${env}.${file_name}` - yield* Effect.logInfo(`Updating file ${name_with_env} located at ${file_path} of gist ${gist_name}`) - - // it seems this does not require renaming the file - const editCommand = [ - "gh", - "gist", - "edit", - gist_id, - "-f", - name_with_env, - `"${file_path}"` - ] - .join(" ") - - return yield* runGetExitCodeSuppressed(editCommand) - } - ) - - const addFileToGist = Effect.fn("addFileToGist")( - function*({ env, file, gist_id, gist_name }: { - gist_id: string - gist_name: string - file: { - path: string - name: string - } - env: string - }) { - yield* Effect.logInfo(`Adding file ${file.path} to gist ${gist_name}`) - const editCommand = [ - "gh", - "gist", - "edit", - gist_id, - "-a", - `"${file.path}"` - ] - .join(" ") - - yield* runGetExitCodeSuppressed(editCommand) - - const renameCommand = [ - "gh", - "gist", - "rename", - gist_id, - file.name, - `${env}.${file.name}` - ] - .join(" ") - - yield* Effect.logInfo(`Renaming file ${file.name} to ${env}.${file.name} in gist ${gist_name}`) - return yield* runGetExitCodeSuppressed(renameCommand) - } - ) - - const deleteGist = Effect.fn("deleteGist")( - function*({ gist_id, gist_name }: { gist_id: string; gist_name: string }) { - yield* Effect.logInfo(`Deleting gist ${gist_name} with ID ${gist_id}`) - return yield* runGetExitCodeSuppressed(`gh gist delete ${gist_id}`) - } - ) - - const login = Effect.fn("GHGistService.login")(function*(token: string) { - if ((yield* runGetExitCode("gh --version").pipe(Effect.orDie)) !== 0) { - return yield* Effect.die( - "GitHub CLI (gh) is not installed or not found in PATH. Please install it to use the gist command." - ) - } - - const isLogged = yield* runGetExitCode(`echo ${token} | gh auth login --with-token`).pipe(Effect.orDie) - if (isLogged !== 0) { - return yield* new GistError({ message: "Failed to log in to GitHub CLI with provided token" }) - } else { - yield* Effect.logInfo("Successfully logged in to GitHub CLI") - } - }) - - return { - /** Logs into GitHub using the GitHub CLI. - * This is a prerequisite for other gist operations. - * @param token - The GitHub personal access token with gist permissions - * - * @returns An Effect that succeeds when login is successful - */ - login, - - /** - * Loads company mappings from the configured cache Gist. - * A missing company file is initialized once with an empty mapping. - * - * @returns An Effect that yields the loaded company cache - */ - loadGistCache, - - /** - * Saves the current gist cache state to the GitHub cache gist. - * Updates the existing cache gist with the current mappings of names to gist IDs. - * - * @param cache - The GistCache instance to save - * @returns An Effect that succeeds when the cache is successfully saved - */ - saveGistCache, - - /** - * Creates a new GitHub gist with the specified files and renames them with environment prefixes. - * Generates a GitHub CLI command to create the gist, extracts the resulting gist ID, - * and renames all files with environment prefixes for multi-environment support. - * - * @param gist_name - The human-readable name for this gist (used in cache mapping) - * @param description - The description for the GitHub gist - * @param files - Array of file objects with path and name properties to include in the gist - * @param is_public - Whether the gist should be public or private - * @param env - Environment prefix to prepend to file names (e.g., "local-dev", "prod") - * @returns An Effect that yields a gist entry object with name and id properties - */ - createGistWithFiles, - - /** - * Retrieves file names from a GitHub gist, filtered by environment prefix. - * Fetches the list of files contained in the specified gist and returns only - * those that match the current environment, with the environment prefix removed. - * - * @param gist_id - The GitHub gist ID to retrieve file names from - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param env - Environment prefix to filter files by (e.g., "local-dev", "prod") - * @returns An Effect that yields an array of file names with environment prefix removed - */ - getGistFileNames, - - /** - * Removes a file from a specified GitHub gist. - * The file name is automatically prefixed with the environment when removing. - * @param gist_id - The ID of the gist to modify - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param file_name - The base name of the file to remove (without environment prefix) - * @param env - Environment prefix that was used when the file was added - * @returns An Effect that succeeds when the file is removed - */ - removeFileFromGist, - - /** - * Updates a file in a specified GitHub gist. - * The file name is automatically prefixed with the environment when updating. - * @param gist_id - The ID of the gist to modify - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param file_name - The base name of the file to update (without environment prefix) - * @param file_path - The local path of the file to update in the gist - * @param env - Environment prefix that was used when the file was added - * @returns An Effect that succeeds when the file is updated - */ - updateFileOfGist, - - /** - * Adds a new file to a specified GitHub gist. - * The file is automatically renamed with an environment prefix for multi-environment support. - * @param gist_id - The ID of the gist to modify - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @param file - The file object containing path and name properties - * @param env - Environment prefix to prepend to the file name - * @returns An Effect that succeeds when the file is added and renamed - */ - addFileToGist, - - /** - * Deletes a specified GitHub gist by its ID. - * @param gist_id - The ID of the gist to delete - * @param gist_name - The human-readable name of the gist (for logging purposes) - * @returns An Effect that succeeds when the gist is deleted - */ - deleteGist - } - }) -}) { - static DefaultWithoutDependencies = Layer.effect(this, this.make) - static Default = this.DefaultWithoutDependencies.pipe( - Layer.provide(RunCommandService.Default) - ) -} - -export class GistHandler extends Context.Service()("GistHandler", { - make: Effect.gen(function*() { - const GH = yield* GHGistService - - // I prefer to provide these two only once during the main CLI pipeline setup - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - - return { - handler: Effect.fn("effa-cli.gist.GistHandler")(function*({ YAMLPath }: { YAMLPath: string }) { - // load company and environment from environment variables - const CONFIG = yield* Config.all({ - company: Config.String("COMPANY"), - env: Config.String("ENV").pipe(Config.withDefault("local-dev")), - gistCacheId: Config.NonEmptyString("EFFA_GIST_CACHE_ID") - }) - - yield* Effect.logInfo(`Company: ${CONFIG.company}, ENV: ${CONFIG.env}`) - - yield* Effect.logInfo(`Reading configuration from ${YAMLPath}`) - - const configExists = yield* fs.exists(YAMLPath) - if (!configExists) { - return yield* new GistError({ message: `Configuration file not found: ${YAMLPath}` }) - } - - const configFromYaml = yield* pipe( - YAMLPath, - fs.readFileString, - Effect.andThen((content) => - Effect.try({ - try: () => yaml.load(content), - catch(error) { - return new GistYAMLError({ message: `Failed to parse YAML: ${(error as Error).message}` }) - } - }) - ), - Effect.andThen(Schema.decodeUnknownEffect(GistYAML)) - ) - - // load GitHub token securely from environment variable - const redactedToken = yield* Config.Redacted(configFromYaml.settings.token_env) - - yield* Effect.logInfo(`Using GitHub token from environment variable: ${configFromYaml.settings.token_env}`) - yield* Effect.logInfo(`Token loaded: ${redactedToken}`) // this will show in logs - - yield* GH.login(Redacted.value(redactedToken)) - - const cache = yield* SynchronizedRef.make( - yield* GH.loadGistCache(CONFIG.gistCacheId, CONFIG.company) - ) - - // filter YAML gists by company to ensure isolation between different organizations - // this prevents cross-company gist operations and maintains data separation - const thisCompanyGistsFromYaml = Object - .entries(configFromYaml.gists ?? {}) - .filter(([, v]) => v.company === CONFIG.company) - - for ( - const [name, gistConfig] of thisCompanyGistsFromYaml - ) { - const { description, files_with_name, public: is_public } = gistConfig - - yield* Effect.logInfo(`Processing gist ${name}`) - - const filesOnDiskWithFullPath = yield* Effect - .all( - files_with_name.map(Effect.fnUntraced(function*(f) { - const fullPath = path.join(configFromYaml.settings.base_directory, f.path) - const fileExists = yield* fs.exists(fullPath) - - if (!fileExists) { - yield* Effect.logWarning(`File not found: ${fullPath}, skipping...`) - return Option.none() - } - - return Option.some({ - path: fullPath, - name: f.name - }) - })), - { - concurrency: "unbounded" - } - ) - .pipe(Effect.map(Array.getSomes)) - - const gistFromCache = (yield* SynchronizedRef.get(cache)).entries.find((_) => _.name === name) - - // if the gist's name exists in cache, update the existing gist - // otherwise, create a new gist and update the local cache - if (gistFromCache) { - yield* Effect.logInfo(`Updating existing gist ${gistFromCache.name} with ID ${gistFromCache.id}`) - - // get current files in the gist to detect removed files - const gistFileNames = new Set( - yield* GH.getGistFileNames({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - env: CONFIG.env - }) - ) - - const expectedFiles = new Set(filesOnDiskWithFullPath.map(({ name }) => name)) - - // remove files that are no longer in YAML configuration - for (const gf of gistFileNames) { - if (!expectedFiles.has(gf)) { - yield* GH.removeFileFromGist({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - file_name: gf, - env: CONFIG.env - }) - } - } - - // update/add files from configuration - for (const f of filesOnDiskWithFullPath) { - if (gistFileNames.has(f.name)) { - yield* GH.updateFileOfGist({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - file_name: f.name, - file_path: f.path, - env: CONFIG.env - }) - } else { - yield* GH.addFileToGist({ - gist_id: gistFromCache.id, - gist_name: gistFromCache.name, - file: f, - env: CONFIG.env - }) - } - } - } else { - if (filesOnDiskWithFullPath.length !== 0) { - yield* SynchronizedRef.getAndUpdateEffect( - cache, - Effect.fnUntraced(function*(cache) { - return new GistCache({ - gist_id: cache.gist_id, - entries: [ - ...cache.entries, - { - ...(yield* GH.createGistWithFiles({ - gist_name: name, - description, - is_public, - - files: filesOnDiskWithFullPath, - env: CONFIG.env - })) - } - ], - company: cache.company - }) - }) - ) - } else { - yield* Effect.logWarning(`No valid files found for gist ${name}, skipping creation...`) - } - } - } - - // cache cleanup: remove gists that are no longer in YAML configuration - // only affects entries for the current company to maintain isolation - const configGistNames = new Set( - thisCompanyGistsFromYaml - .map(([name]) => name) - ) - - const newCache = yield* SynchronizedRef.updateAndGetEffect( - cache, - Effect.fnUntraced(function*(cache) { - const newEntries = [...cache.entries] - - // remove obsolete cache entries for current company only - // this ensures gists from other companies remain untouched - for (let i = newEntries.length - 1; i >= 0; i--) { - const cacheEntry = newEntries[i] - if (cacheEntry && !configGistNames.has(cacheEntry.name)) { - // delete the actual gist from GitHub - yield* GH.deleteGist({ - gist_id: cacheEntry.id, - gist_name: cacheEntry.name - }) - yield* Effect.logInfo( - `Obsolete gist ${cacheEntry.name} of company ${CONFIG.company} with ID ${cacheEntry.id}) will be removed from cache` - ) - newEntries.splice(i, 1) - } - } - - return { ...cache, entries: newEntries } - }) - ) - - yield* GH.saveGistCache(newCache) - - yield* Effect.logInfo("Gist operations completed") - }) - } - }) -}) { - static DefaultWithoutDependencies = Layer.effect(this, this.make) - static Default = this.DefaultWithoutDependencies.pipe( - Layer.provide(GHGistService.Default) - ) -} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts deleted file mode 100644 index 3f5e0177a..000000000 --- a/packages/cli/src/index.ts +++ /dev/null @@ -1,904 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable no-constant-binary-expression */ -/* eslint-disable no-empty-pattern */ -// import necessary modules from the libraries -import { NodeRuntime, NodeServices } from "@effect/platform-node" -import { Argument, Command, Flag, Prompt } from "effect/cli" - -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Layer from "effect/Layer" -import * as Option from "effect/Option" -import * as Path from "effect/Path" -import * as Stream from "effect/Stream" -import { ExtractExportMappingsService } from "./extract.ts" -import { GistHandler } from "./gist.ts" -import { RunCommandService } from "./os-command.ts" -import { packages } from "./shared.ts" - -import { patchArgvForWrapCommands } from "./argv-patch.ts" -import { syncEffectAppSubtree } from "./sync-effect-app-subtree.ts" -import { syncEffectSubtree } from "./sync-effect-subtree.ts" -import { syncDiff, syncPush, syncShared } from "./sync-shared.ts" -import { makeNcuUpdateCommand } from "./update-packages.ts" - -patchArgvForWrapCommands(process.argv) - -NodeRuntime.runMain( - Effect - .fn("effa-cli")(function*() { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const extractExportMappings = yield* ExtractExportMappingsService - const { runGetExitCode } = yield* RunCommandService - - yield* Effect.addFinalizer(() => Effect.logInfo(`CLI has finished executing`)) - - /** - * Updates effect-app packages within their existing semver ranges using npm-check-updates. - * Runs both at workspace root and recursively in all workspace packages. - */ - const updateEffectAppPackages = Effect.fn("effa-cli.ue.updateEffectAppPackages")(function*() { - const filters = ["effect-app", "@effect-app/*"] - for (const filter of filters) { - yield* runGetExitCode(makeNcuUpdateCommand(filter)) - yield* runGetExitCode(makeNcuUpdateCommand(filter, { recursive: true })) - } - })() - - /** - * Updates Effect ecosystem packages within their existing semver ranges using npm-check-updates. - * Covers core Effect packages, Effect ecosystem packages, and Effect Atom packages. - * Runs both at workspace root and recursively in all workspace packages. - */ - const updateEffectPackages = Effect.fn("effa-cli.ue.updateEffectPackages")(function*() { - const effectFilters = ["effect", "@effect/*", "@effect-atom/*"] - for (const filter of effectFilters) { - yield* runGetExitCode(makeNcuUpdateCommand(filter)) - yield* runGetExitCode(makeNcuUpdateCommand(filter, { recursive: true })) - } - })() - - /** - * Updates all packages except Effect and Effect-App ecosystem packages to their latest versions using npm-check-updates. - * Excludes core Effect packages, Effect ecosystem packages, Effect Atom packages, and Effect-App packages. - * Preserves existing rejections from .ncurc.json configuration. - * Runs both at workspace root and recursively in all workspace packages. - */ - const updatePackages = Effect.fn("effa-cli.update-packages.updatePackages")(function*() { - const effectFilters = ["effect", "@effect/*", "@effect-atom/*", "effect-app", "@effect-app/*"] - - // read existing .ncurc.json to preserve existing reject patterns - let existingRejects: string[] = [] - const ncurcPath = "./.ncurc.json" - - if (yield* fs.exists(ncurcPath)) { - const ncurcContent = yield* fs.readFileString(ncurcPath) - const ncurc = JSON.parse(ncurcContent) - if (ncurc.reject && Array.isArray(ncurc.reject)) { - existingRejects = ncurc.reject - } - } - - const allRejects = [...existingRejects, ...effectFilters] - yield* Effect.logInfo(`Excluding packages from update: ${allRejects.join(", ")}`) - const rejectArgs = allRejects.map((filter) => `--reject "${filter}"`).join(" ") - - yield* runGetExitCode(`pnpm exec ncu -u ${rejectArgs}`) - yield* runGetExitCode(`pnpm -r exec ncu -u ${rejectArgs}`) - })() - - /** - * Links local effect-app packages by adding file resolutions to package.json. - * Updates the package.json with file: protocol paths pointing to the local effect-app-libs directory, - * then runs pnpm install to apply the changes. - * - * @param effectAppLibsPath - Path to the local effect-app-libs directory - * @returns An Effect that succeeds when linking is complete - */ - const linkPackages = Effect.fnUntraced(function*(effectAppLibsPath: string) { - yield* Effect.logInfo("Linking local effect-app packages...") - - const packageJsonPath = "./package.json" - const packageJsonContent = yield* fs.readFileString(packageJsonPath) - const trailingNewline = packageJsonContent.endsWith("\n") ? "\n" : "" - const pj = JSON.parse(packageJsonContent) - - const resolutions = { - ...pj.resolutions, - "@effect-app/eslint-codegen-model": "file:" + effectAppLibsPath + "/packages/eslint-codegen-model", - "effect-app": "file:" + effectAppLibsPath + "/packages/effect-app", - "@effect-app/infra": "file:" + effectAppLibsPath + "/packages/infra", - "@effect-app/vue": "file:" + effectAppLibsPath + "/packages/vue", - "@effect-app/vue-components": "file:" + effectAppLibsPath + "/packages/vue-components", - "@effect-app/eslint-shared-config": "file:" + effectAppLibsPath + "/packages/eslint-shared-config", - ...packages.reduce((acc, p) => { - acc[p] = `file:${effectAppLibsPath}/node_modules/${p}` - return acc - }, {} as Record) - } - - pj.resolutions = resolutions - - yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2) + trailingNewline) - yield* Effect.logInfo("Updated package.json with local file resolutions") - - yield* runGetExitCode("pnpm i") - - yield* Effect.logInfo("Successfully linked local packages") - }) - - /** - * Unlinks local effect-app packages by removing file resolutions from package.json. - * Filters out all effect-app related file: protocol resolutions from package.json, - * then runs pnpm install to restore registry packages. - * - * @returns An Effect that succeeds when unlinking is complete - */ - const unlinkPackages = Effect.fnUntraced(function*() { - yield* Effect.logInfo("Unlinking local effect-app packages...") - - const packageJsonPath = "./package.json" - const packageJsonContent = yield* fs.readFileString(packageJsonPath) - const trailingNewline = packageJsonContent.endsWith("\n") ? "\n" : "" - const pj = JSON.parse(packageJsonContent) - - const filteredResolutions = Object.entries(pj.resolutions as Record).reduce( - (acc, [k, v]) => { - if (k.startsWith("@effect-app/") || k === "effect-app" || packages.includes(k)) return acc - acc[k] = v - return acc - }, - {} as Record - ) - - pj.resolutions = filteredResolutions - - yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2) + trailingNewline) - yield* Effect.logInfo("Removed effect-app file resolutions from package.json") - - yield* runGetExitCode("pnpm i") - yield* Effect.logInfo("Successfully unlinked local packages") - })() - - /** - * Monitors controller files for changes and runs eslint on related controllers.ts/routes.ts files. - * Watches for .controllers. files and triggers eslint fixes on parent directory's controller files. - * - * @param watchPath - The path to watch for controller changes - * @param debug - Whether to enable debug logging - * @returns An Effect that sets up controller file monitoring - */ - const monitorChildIndexes = Effect.fn("effa-cli.index-multi.monitorChildIndexes")( - function*(watchPath: string) { - yield* Effect.logInfo(`Starting controller monitoring for: ${watchPath}`) - - const watchStream = fs.watch(watchPath) - - yield* watchStream - .pipe( - Stream.runForEach( - Effect.fn("effa-cli.monitorChildIndexes.handleEvent")(function*(event) { - const pathParts = event.path.split("/") - const fileName = pathParts[pathParts.length - 1] - const isController = fileName?.toLowerCase().includes(".controllers.") - - if (!isController) return - - let i = 1 - const reversedParts = pathParts.toReversed() - - while (i < reversedParts.length) { - const candidateFiles = ["controllers.ts", "routes.ts"] - .map((f) => [...pathParts.slice(0, pathParts.length - i), f].join("/")) - - const existingFiles: string[] = [] - for (const file of candidateFiles) { - const exists = yield* fs.exists(file) - if (exists) existingFiles.push(file) - } - - if (existingFiles.length > 0) { - yield* Effect.logInfo( - `Controller change detected: ${event.path}, fixing files: ${existingFiles.join(", ")}` - ) - - const eslintArgs = existingFiles.map((f) => `"../${f}"`).join(" ") - yield* runGetExitCode(`cd api && pnpm eslint --fix ${eslintArgs}`) - break - } - i++ - } - }) - ), - Effect.andThen( - Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring child indexes in: ${watchPath}`)) - ), - Effect.forkScoped - ) - } - ) - - /** - * Monitors a directory for changes and runs eslint on the specified index file. - * Triggers eslint fixes when any file in the directory changes (except the index file itself). - * - * @param watchPath - The path to watch for changes - * @param indexFile - The index file to run eslint on when changes occur - * @param debug - Whether to enable debug logging - * @returns An Effect that sets up root index monitoring - */ - const monitorRootIndexes = Effect.fn("effa-cli.index-multi.monitorRootIndexes")( - function*(watchPath: string, indexFile: string) { - yield* Effect.logInfo(`Starting root index monitoring for: ${watchPath} -> ${indexFile}`) - - const watchStream = fs.watch(watchPath) - - yield* watchStream - .pipe( - Stream.runForEach( - Effect.fn("effa-cli.index-multi.monitorRootIndexes.handleEvent")(function*(event) { - if (event.path.endsWith(indexFile)) return - - yield* Effect.logInfo(`Root change detected: ${event.path}, fixing: ${indexFile}`) - - yield* runGetExitCode(`pnpm eslint --fix "${indexFile}"`) - }) - ), - Effect.andThen( - Effect.addFinalizer(() => - Effect.logInfo(`Stopped monitoring root indexes in: ${watchPath} -> ${indexFile}`) - ) - ), - Effect.forkScoped - ) - } - ) - - /** - * Sets up comprehensive index monitoring for a given path. - * Combines both child controller monitoring and root index monitoring. - * - * @param watchPath - The path to monitor - * @param debug - Whether to enable debug logging - * @returns An Effect that sets up all index monitoring for the path - */ - const monitorIndexes = Effect.fn("effa-cli.index-multi.monitorIndexes")( - function*(watchPath: string) { - yield* Effect.logInfo(`Setting up index monitoring for path: ${watchPath}`) - - const indexFile = watchPath + "/index.ts" - - const monitors = [monitorChildIndexes(watchPath)] - - if (yield* fs.exists(indexFile)) { - monitors.push(monitorRootIndexes(watchPath, indexFile)) - } else { - yield* Effect.logWarning(`Index file ${indexFile} does not exist`) - } - - yield* Effect.logInfo(`Starting ${monitors.length} monitor(s) for ${watchPath}`) - - yield* Effect.all(monitors, { concurrency: monitors.length }) - } - ) - - /** - * Updates a package.json file with generated exports mappings for TypeScript modules. - * Scans TypeScript source files and creates export entries that map module paths - * to their compiled JavaScript and TypeScript declaration files. - * - * @param startDir - The starting directory path for resolving relative paths - * @param p - The package directory path to process - * @param levels - Optional depth limit for export filtering (0 = no limit) - * @returns An Effect that succeeds when the package.json is updated - */ - const packagejsonUpdater = Effect.fn("effa-cli.packagejsonUpdater")( - function*(startDir: string, p: string, levels = 0) { - yield* Effect.logInfo(`Generating exports for ${p}`) - - const exportMappings = yield* extractExportMappings(path.resolve(startDir, p)) - - // if exportMappings is empty skip export generation - if (exportMappings === "") { - yield* Effect.logInfo(`No src directory found for ${p}, skipping export generation`) - return - } - - const sortedExportEntries = JSON.parse( - `{ ${exportMappings} }` - ) as Record< - string, - unknown - > - - const filteredExportEntries = levels - ? Object - .keys(sortedExportEntries) - // filter exports by directory depth - only include paths up to specified levels deep - .filter((_) => _.split("/").length <= (levels + 1 /* `./` */)) - .reduce( - (prev, cur) => { - prev[cur] = sortedExportEntries[cur] - return prev - }, - {} as Record - ) - : sortedExportEntries - - const packageExports = { - ...((yield* fs.exists(p + "/src/index.ts")) - && { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }), - ...Object - .keys(filteredExportEntries) - .reduce( - (prev, cur) => { - // exclude index files and internal modules from package exports: - // - skip "./index" to avoid conflicts with the main "." export - // - skip "/internal/" paths to keep internal modules private - if (cur !== "./index" && !cur.includes("/internal/")) { - prev[cur] = filteredExportEntries[cur] - } - return prev - }, - {} as Record - ) - } - - const pkgJsonContent = yield* fs.readFileString(p + "/package.json", "utf-8") - const trailingNewline = pkgJsonContent.endsWith("\n") ? "\n" : "" - const pkgJson = JSON.parse(pkgJsonContent) - - pkgJson.exports = packageExports - - yield* Effect.logInfo(`Writing updated package.json for ${p}`) - - return yield* fs.writeFileString( - p + "/package.json", - JSON.stringify(pkgJson, null, 2) + trailingNewline - ) - } - ) - - /** - * Monitors a directory for TypeScript file changes and automatically updates package.json exports. - * Generates initial package.json exports, then watches the src directory for changes to regenerate exports. - * - * @param watchPath - The directory path containing the package.json and src to monitor - * @param levels - Optional depth limit for export filtering (0 = no limit) - * @returns An Effect that sets up package.json monitoring - */ - const monitorPackageJson = Effect.fn("effa-cli.monitorPackageJson")( - function*(startDir: string, watchPath: string, levels = 0) { - yield* packagejsonUpdater(startDir, watchPath, levels) - - const srcPath = watchPath === "." ? "./src" : `${watchPath}/src` - - if (!(yield* fs.exists(srcPath))) { - yield* Effect.logWarning(`Source directory ${srcPath} does not exist - skipping monitoring`) - return - } - - const watchStream = fs.watch(srcPath) - - yield* watchStream.pipe( - Stream.runForEach( - Effect.fn("effa-cli.monitorPackageJson.handleEvent")(function*(_) { - yield* packagejsonUpdater(startDir, watchPath, levels) - }) - ), - Effect.andThen( - Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring package.json for: ${watchPath}`)) - ), - Effect.forkScoped - ) - } - ) - - /* - * CLI - */ - - const WrapAsOption = Flag.String("wrap").pipe( - Flag.withAlias("w"), - Flag.optional, - Flag.withDescription( - "Wrap child bash command: the lifetime of the CLI command will be tied to the child process" - ) - ) - - // has prio over WrapAsOption - const WrapAsArg = Argument - .String("wrap") - .pipe( - Argument.atLeast(1), - Argument.optional, - Argument.withDescription( - "Wrap child bash command: the lifetime of the CLI command will be tied to the child process" - ) - ) - - /** - * Creates a command that automatically includes wrap functionality for executing child bash commands. - * Combines both option-based (--wrap) and argument-based wrap parameters, giving priority to arguments. - * If a wrap command is provided, it will be executed **after** the main command handler. - * - * @param name - The command name - * @param config - The command configuration (options, args, etc.) - * @param handler - The main command handler function - * @param completionMessage - Optional message to log when the command completes - * @returns A Command with integrated wrap functionality - */ - const makeCommandWithWrap = ( - name: Name, - config: Config, - handler: (_: any) => Effect.Effect, - completionMessage?: string - ) => - Command.make( - name, - { ...config, wo: WrapAsOption, wa: WrapAsArg }, - Effect.fn("effa-cli.withWrapHandler")(function*(_) { - const { wa, wo, ...cfg } = _ as unknown as { - wo: Option.Option - wa: Option.Option> - } - - if (completionMessage) { - yield* Effect.addFinalizer(() => Effect.logInfo(completionMessage)) - } - - const wrapOption = Option.orElse(wa, () => wo) - - yield* handler(cfg as any) - - if (Option.isSome(wrapOption)) { - const val: string = Array.isArray(wrapOption.value) - ? wrapOption.value.join(" ") - : wrapOption.value as string - - yield* Effect.logInfo(`Spawning child command: ${val}`) - const exitCode = yield* runGetExitCode(val) - if (exitCode !== 0) { - return yield* Effect.sync(() => process.exit(exitCode)) - } - } - - return - }, (_) => Effect.scoped(_)) - ) - - const EffectAppLibsPath = Argument - .Directory("effect-app-libs-path", { mustExist: true }) - .pipe( - Argument.withDefault("../../effect-app/libs"), - Argument.withDescription("Path to the effect-app-libs directory") - ) - - const link = Command - .make( - "link", - { effectAppLibsPath: EffectAppLibsPath }, - Effect.fn("effa-cli.link")(function*({ effectAppLibsPath }) { - return yield* linkPackages(effectAppLibsPath) - }) - ) - .pipe(Command.withDescription("Link local effect-app packages using file resolutions")) - - const unlink = Command - .make( - "unlink", - {}, - Effect.fn("effa-cli.unlink")(function*({}) { - return yield* unlinkPackages - }) - ) - .pipe(Command.withDescription("Remove effect-app file resolutions and restore npm registry packages")) - - const ue = Command - .make( - "ue", - {}, - Effect.fn("effa-cli.ue")(function*({}) { - yield* Effect.logInfo("Update effect-app and/or effect packages") - - const prompted = yield* Prompt.Select({ - choices: [ - { - title: "effect-app", - description: "Update only effect-app packages", - value: "effect-app" - }, - { - title: "effect", - description: "Update only effect packages", - value: "effect" - }, - { - title: "both", - description: "Update both effect-app and effect packages", - value: "both" - } - ], - message: "Select an option" - }) - - switch (prompted) { - case "effect-app": - return yield* updateEffectAppPackages.pipe( - Effect.andThen(runGetExitCode("pnpm i")) - ) - - case "effect": - return yield* updateEffectPackages.pipe( - Effect.andThen(runGetExitCode("pnpm i")) - ) - case "both": - return yield* updateEffectPackages.pipe( - Effect.andThen(updateEffectAppPackages), - Effect.andThen(runGetExitCode("pnpm i")) - ) - } - }) - ) - .pipe(Command.withDescription("Update effect-app and/or effect packages")) - - const up = Command - .make( - "up", - {}, - Effect.fn("effa-cli.update-packages")(function*({}) { - yield* Effect.logInfo("Updating all packages except Effect/Effect-App ecosystem packages...") - - return yield* updatePackages.pipe( - Effect.andThen(runGetExitCode("pnpm i")) - ) - }) - ) - .pipe(Command.withDescription("Update all packages except Effect/Effect-App ecosystem packages")) - - const indexMulti = makeCommandWithWrap( - "index-multi", - {}, - Effect.fn("effa-cli.index-multi")(function*({}) { - yield* Effect.logInfo("Starting multi-index monitoring") - - const dirs = ["./api/src"] - - const existingDirs: string[] = [] - for (const dir of dirs) { - const dirExists = yield* fs.exists(dir) - if (dirExists) { - existingDirs.push(dir) - } else { - yield* Effect.logWarning(`Directory ${dir} does not exist - skipping`) - } - } - - const monitors = existingDirs.map((dir) => monitorIndexes(dir)) - yield* Effect.all(monitors, { concurrency: monitors.length }) - }), - "Stopped multi-index monitoring" - ) - .pipe( - Command.withDescription( - "Monitor multiple directories for index and controller file changes" - ) - ) - - const packagejson = makeCommandWithWrap( - "packagejson", - {}, - Effect.fn("effa-cli.packagejson")(function*({}) { - // https://nodejs.org/api/path.html#pathresolvepaths - const startDir = path.resolve() - - return yield* monitorPackageJson(startDir, ".") - }), - "Stopped monitoring root package.json exports" - ) - .pipe( - Command.withDescription("Generate and update root-level package.json exports mappings for TypeScript modules") - ) - - const packagejsonPackages = makeCommandWithWrap( - "packagejson-packages", - {}, - Effect.fn("effa-cli.packagejson-packages")(function*({}) { - // https://nodejs.org/api/path.html#pathresolvepaths - const startDir = path.resolve() - - const packagesDir = path.join(startDir, "packages") - - const packagesExists = yield* fs.exists(packagesDir) - if (!packagesExists) { - return yield* Effect.logWarning("No packages directory found") - } - - // get all package directories - const packageDirs = yield* fs.readDirectory(packagesDir) - - const validPackages: string[] = [] - - // filter packages that have package.json and src directory - for (const packageName of packageDirs) { - const packagePath = path.join(packagesDir, packageName) - const packageJsonExists = yield* fs.exists(path.join(packagePath, "package.json")) - const srcExists = yield* fs.exists(path.join(packagePath, "src")) - - const shouldExclude = false - || packageName.endsWith("eslint-codegen-model") - || packageName.endsWith("eslint-shared-config") - || packageName.endsWith("vue-components") - - if (packageJsonExists && srcExists && !shouldExclude) { - validPackages.push(packagePath) - } - } - - yield* Effect.logInfo(`Found ${validPackages.length} packages to update`) - - // update each package sequentially - yield* Effect.all( - validPackages.map( - Effect.fnUntraced(function*(packagePath) { - const relativePackagePath = path.relative(startDir, packagePath) - yield* Effect.logInfo(`Updating ${relativePackagePath}`) - return yield* monitorPackageJson(startDir, relativePackagePath) - }) - ) - ) - - yield* Effect.logInfo("All packages updated successfully") - }), - "Stopped monitoring package.json exports for all packages" - ) - .pipe( - Command.withDescription("Generate and update package.json exports mappings for all packages in monorepo") - ) - - const gist = Command - .make( - "gist", - { - config: Flag.File("config").pipe( - Flag.withDefault("gists.yaml"), - Flag.withDescription("Path to YAML configuration file") - ) - }, - Effect.fn("effa-cli.gist")(function*({ config }) { - const gh = yield* GistHandler - return yield* gh.handler({ - YAMLPath: config - }) - }) - ) - .pipe(Command.withDescription("Create GitHub gists from files specified in YAML configuration")) - - const nuke = Command - .make( - "nuke", - { - dryRun: Flag.Boolean("dry-run").pipe( - Flag.withDescription("Show what would be done without making changes") - ), - storePrune: Flag.Boolean("store-prune").pipe( - Flag.withDescription("Prune the package manager store") - ) - }, - Effect.fn("effa-cli.nuke")(function*({ dryRun, storePrune }) { - yield* Effect.logInfo(dryRun ? "Performing dry run cleanup..." : "Performing nuclear cleanup...") - - if (dryRun) { - yield* runGetExitCode( - "find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -print \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -print \\)" - ) - } else { - yield* runGetExitCode( - "find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -exec rm -rf -- {} + \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -delete \\)" - ) - - if (storePrune) { - yield* runGetExitCode( - "pnpm store prune" - ) - } - } - - yield* Effect.logInfo("Cleanup operation completed") - }) - ) - .pipe(Command.withDescription("Nuclear cleanup command: removes all generated files and cleans the workspace")) - - const syncEffect = Command - .make( - "sync-effect", - { - manifests: Flag.String("manifests").pipe( - Flag.withAlias("m"), - Flag.optional, - Flag.withDescription( - "Comma-separated list of package.json paths to scan (default: package.json)" - ) - ), - prefix: Flag.String("prefix").pipe( - Flag.optional, - Flag.withDescription("Subtree prefix (default: repos/effect)") - ), - url: Flag.String("url").pipe( - Flag.optional, - Flag.withDescription( - "Git repository URL (default: https://github.com/Effect-TS/effect.git)" - ) - ) - }, - Effect.fn("effa-cli.sync-effect")(function*({ manifests, prefix, url }) { - yield* syncEffectSubtree({ - manifestPaths: Option.match(manifests, { - onNone: () => ["package.json"], - onSome: (m) => m.split(",").map((p) => p.trim()) - }), - subtreePrefix: Option.getOrElse(prefix, () => "repos/effect"), - url: Option.getOrElse(url, () => "https://github.com/Effect-TS/effect.git") - }) - }) - ) - .pipe(Command.withDescription("Sync the Effect subtree to the version pinned in package.json")) - - const syncEffectApp = Command - .make( - "sync-effect-app", - { - manifests: Flag.String("manifests").pipe( - Flag.withAlias("m"), - Flag.optional, - Flag.withDescription( - "Comma-separated list of package.json paths to scan (default: package.json)" - ) - ), - prefix: Flag.String("prefix").pipe( - Flag.optional, - Flag.withDescription("Subtree prefix (default: repos/libs)") - ), - url: Flag.String("url").pipe( - Flag.optional, - Flag.withDescription( - "Git repository URL (default: https://github.com/effect-app/libs.git)" - ) - ), - ref: Flag.String("ref").pipe( - Flag.optional, - Flag.withDescription("Ref escape hatch (branch/tag/sha/latest); latest means main") - ) - }, - Effect.fn("effa-cli.sync-effect-app")(function*({ manifests, prefix, ref, url }) { - yield* syncEffectAppSubtree({ - manifestPaths: Option.match(manifests, { - onNone: () => ["package.json"], - onSome: (m) => m.split(",").map((p) => p.trim()) - }), - ...Option.match(ref, { - onNone: () => ({}), - onSome: (ref) => ({ ref }) - }), - subtreePrefix: Option.getOrElse(prefix, () => "repos/libs"), - url: Option.getOrElse(url, () => "https://github.com/effect-app/libs.git") - }) - }) - ) - .pipe(Command.withDescription("Sync the Effect App libs subtree to the version pinned in package.json")) - - const SharedLockfileFlag = Flag.File("lockfile").pipe( - Flag.optional, - Flag.withDescription("Path to lockfile (default: .shared.json)") - ) - - const sync = Command - .make( - "sync", - { - lockfile: SharedLockfileFlag, - update: Flag.Boolean("update").pipe( - Flag.withDescription("Bump the pinned ref to the latest sha before syncing") - ), - ref: Flag.String("ref").pipe( - Flag.optional, - Flag.withDescription("Ref (branch/tag/sha) to update to; default: remote default branch HEAD") - ) - }, - Effect.fn("effa-cli.sync")(function*({ lockfile, ref, update }) { - yield* syncShared({ - lockfilePath: Option.getOrElse(lockfile, () => ".shared.json"), - update, - ref: Option.getOrUndefined(ref) - }) - }) - ) - .pipe( - Command.withDescription("Sync shared docs / e2e helpers / plugins from effect-app/shared per .shared.json") - ) - - const syncDiffCmd = Command - .make( - "sync-diff", - { lockfile: SharedLockfileFlag }, - Effect.fn("effa-cli.sync-diff")(function*({ lockfile }) { - yield* syncDiff({ - lockfilePath: Option.getOrElse(lockfile, () => ".shared.json") - }) - }) - ) - .pipe(Command.withDescription("Report drift between local synced files and the pinned shared ref")) - - const syncPushCmd = Command - .make( - "sync-push", - { - lockfile: SharedLockfileFlag, - message: Flag.String("message").pipe( - Flag.withAlias("m"), - Flag.optional, - Flag.withDescription("Commit message for the push") - ), - branch: Flag.String("branch").pipe( - Flag.optional, - Flag.withDescription("Branch name in shared repo (default: auto-generated)") - ), - pr: Flag.Boolean("pr").pipe( - Flag.withDescription("Open a PR via `gh pr create` after pushing") - ) - }, - Effect.fn("effa-cli.sync-push")(function*({ branch, lockfile, message, pr }) { - yield* syncPush({ - lockfilePath: Option.getOrElse(lockfile, () => ".shared.json"), - message: Option.getOrUndefined(message), - branch: Option.getOrUndefined(branch), - pr - }) - }) - ) - .pipe(Command.withDescription("Push locally-modified synced files to the shared repo on a new branch")) - - // configure CLI - return yield* Command.run( - Command - .make("effa") - .pipe(Command.withSubcommands([ - ue, - up, - link, - unlink, - indexMulti, - packagejson, - packagejsonPackages, - gist, - nuke, - syncEffect, - syncEffectApp, - sync, - syncDiffCmd, - syncPushCmd - ])), - { - version: "v1.0.0" - } - ) - })() - .pipe( - Effect.scoped, - Effect.provide( - Layer.provideMerge( - Layer.merge( - GistHandler.Default, - RunCommandService.Default - ), - NodeServices.layer - ) - ) - ) -) diff --git a/packages/cli/src/os-command.ts b/packages/cli/src/os-command.ts deleted file mode 100644 index 9982a34cc..000000000 --- a/packages/cli/src/os-command.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable no-constant-binary-expression */ -/* eslint-disable no-empty-pattern */ -import * as Context from "effect/Context" -import * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import { ChildProcess } from "effect/process" -import { ChildProcessSpawner } from "effect/process/ChildProcessSpawner" - -/** - * Service for executing shell commands using the Effect platform's Command API. - * Provides methods to run shell commands with different output handling strategies. - * All commands are executed through the system shell (/bin/sh) for proper command parsing. - */ -export class RunCommandService extends Context.Service()("RunCommandService", { - make: Effect.gen(function*() { - // will be provided by the main CLI pipeline setup - const spawner = yield* ChildProcessSpawner - - /** - * Executes a shell command using Command API with inherited stdio streams. - * The command is run through the system shell (/bin/sh) for proper command parsing. - * - * @param cmd - The shell command to execute - * @param cwd - Optional working directory to execute the command in - * @returns An Effect that succeeds with the exit code or fails with a PlatformError - */ - const runGetExitCode = (cmd: string, cwd?: string) => - spawner - .exitCode( - ChildProcess.make("sh", ["-c", cmd], { stdout: "inherit", stderr: "inherit", cwd }) - ) - - /** - * Executes a shell command using Command API and returns the output as a string. - * The command is run through the system shell (/bin/sh) for proper command parsing. - * - * @param cmd - The shell command to execute - * @param cwd - Optional working directory to execute the command in - * @returns An Effect that succeeds with the command's stdout output as string or fails with a PlatformError - */ - const runGetString = (cmd: string, cwd?: string) => - spawner - .string( - ChildProcess.make("sh", ["-c", cmd], { cwd }) - ) - - return { - runGetExitCode, - runGetString - } - }) -}) { - static Default = Layer.effect(this, this.make) -} diff --git a/packages/cli/src/shared.ts b/packages/cli/src/shared.ts deleted file mode 100644 index 6aade80bb..000000000 --- a/packages/cli/src/shared.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const packages = ["effect", "@effect/platform", "@effect/platform-node"] - -export const EFFECT_APP_LIBS = process.env["EFFECT_APP_LIBS"] || "../../effect-app/libs" diff --git a/packages/cli/src/sync-effect-app-subtree.ts b/packages/cli/src/sync-effect-app-subtree.ts deleted file mode 100644 index 00aa657ec..000000000 --- a/packages/cli/src/sync-effect-app-subtree.ts +++ /dev/null @@ -1,307 +0,0 @@ -import * as Arr from "effect/Array" -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Option from "effect/Option" -import * as Path from "node:path" -import { RunCommandService } from "./os-command.ts" - -export interface SyncEffectAppConfig { - readonly manifestPaths: ReadonlyArray - readonly subtreePrefix: string - readonly url: string - readonly ref?: string -} - -interface VersionMention { - readonly manifestPath: string - readonly section: string - readonly name: string - readonly range: string - readonly normalized: string -} - -const depSections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"] as const - -const packageManifestPaths: Record = { - "effect-app": "packages/effect-app/package.json", - "@effect-app/infra": "packages/infra/package.json", - "@effect-app/vue": "packages/vue/package.json", - "@effect-app/vue-components": "packages/vue-components/package.json", - "@effect-app/cli": "packages/cli/package.json", - "@effect-app/eslint-codegen-model": "packages/eslint-codegen-model/package.json", - "@effect-app/eslint-shared-config": "packages/eslint-shared-config/package.json" -} - -const packagePreference = Object.keys(packageManifestPaths) - -const isRecord = (value: unknown): value is Record => - typeof value === "object" && value !== null && !Array.isArray(value) - -const isEffectAppPackage = (name: string) => name === "effect-app" || name.startsWith("@effect-app/") - -const isNonEmptyString = (value: string) => value !== "" - -const shellQuote = (value: string) => `'${value.replaceAll("'", "'\\''")}'` - -const normalizeVersion = (range: string) => { - const match = range.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/) - return match?.[0] -} - -const isFullSha = (ref: string) => /^[0-9a-f]{40}$/i.test(ref) - -const resolveRequestedRef = (ref: string) => ref === "latest" ? "main" : ref - -const parsePackageVersion = (source: string, packagePath: string) => { - const parsed: unknown = JSON.parse(source) - if (!isRecord(parsed) || typeof parsed["version"] !== "string") { - throw new Error(`Could not read version from ${packagePath}`) - } - return parsed["version"] -} - -const parseDefaultBranch = (source: string) => { - const line = source - .split("\n") - .find((line) => line.startsWith("ref: refs/heads/") && line.endsWith("\tHEAD")) - - if (!line) { - throw new Error("Could not resolve default branch for effect-app libs repository") - } - - return line.slice("ref: refs/heads/".length, -"\tHEAD".length) -} - -const selectVersionMentions = (mentions: Arr.NonEmptyArray) => { - for (const packageName of packagePreference) { - const selected = mentions.filter((mention) => mention.name === packageName) - if (Arr.isArrayNonEmpty(selected)) return selected - } - - return mentions -} - -const describeMentions = (mentions: ReadonlyArray) => - mentions - .map((mention) => `${mention.manifestPath} (${mention.section}) ${mention.name}: ${mention.range}`) - .join("\n") - -export const syncEffectAppSubtree = Effect.fnUntraced(function*(config: SyncEffectAppConfig) { - const fs = yield* FileSystem.FileSystem - const { runGetExitCode, runGetString } = yield* RunCommandService - - const run = Effect.fnUntraced(function*(command: string) { - const exitCode = yield* runGetExitCode(command) - if (exitCode !== 0) { - return yield* Effect.fail(new Error(`Command failed with exit code ${exitCode}: ${command}`)) - } - }) - - const fetchRemoteRef = Effect.fnUntraced(function*(remoteRef: string, localRef: string) { - const rawRef = `${localRef}-raw` - - yield* run( - `git fetch --no-tags ${shellQuote(config.url)} ${shellQuote(`+${remoteRef}:${rawRef}`)}` - ) - - const commit = (yield* runGetString(`git rev-list -n1 ${shellQuote(rawRef)}`)).trim() - yield* run(`git update-ref ${shellQuote(localRef)} ${shellQuote(commit)}`) - }) - - const fetchRequestedRef = Effect.fnUntraced(function*(requestedRef: string, localRef: string) { - if (isFullSha(requestedRef)) { - const localCommit = yield* runGetString( - `git rev-parse --verify ${shellQuote(`${requestedRef}^{commit}`)}` - ) - .pipe(Effect.option) - - if (Option.isSome(localCommit)) { - return yield* run(`git update-ref ${shellQuote(localRef)} ${shellQuote(localCommit.value.trim())}`) - } - } - - const remoteRefs = requestedRef.startsWith("refs/") - ? [requestedRef] - : [`refs/heads/${requestedRef}`, `refs/tags/${requestedRef}`, requestedRef] - - for (const remoteRef of remoteRefs) { - const exists = yield* runGetString( - `git ls-remote --exit-code ${shellQuote(config.url)} ${shellQuote(remoteRef)}` - ) - .pipe(Effect.option) - - if (Option.isSome(exists)) { - return yield* fetchRemoteRef(remoteRef, localRef) - } - } - - return yield* Effect.fail(new Error(`Could not resolve effect-app ref: ${requestedRef}`)) - }) - - if (config.ref) { - const ref = resolveRequestedRef(config.ref) - const syncRef = "refs/effa-sync/effect-app-requested" - yield* Effect.logInfo(`Using effect-app subtree ref: ${ref}`) - yield* Effect.logInfo(`Using effect-app subtree url: ${config.url}`) - yield* fetchRequestedRef(ref, syncRef) - return yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} . ${ - shellQuote(syncRef) - } --squash` - ) - } - - const versionMentions: Array = [] - - for (const manifestPath of config.manifestPaths) { - if (!(yield* fs.exists(manifestPath))) continue - - const manifest: unknown = JSON.parse(yield* fs.readFileString(manifestPath)) - if (!isRecord(manifest)) continue - - for (const section of depSections) { - const deps = manifest[section] - if (!isRecord(deps)) continue - - for (const [name, range] of Object.entries(deps)) { - if (!isEffectAppPackage(name) || typeof range !== "string") continue - - const normalized = normalizeVersion(range) - if (!normalized) continue - - versionMentions.push({ manifestPath, section, name, range, normalized }) - } - } - } - - if (!Arr.isArrayNonEmpty(versionMentions)) { - return yield* Effect.fail( - new Error("Could not find any effect-app/@effect-app dependency versions in manifests") - ) - } - - const selectedMentions = selectVersionMentions(versionMentions) - const versions = [...new Set(selectedMentions.map((mention) => mention.normalized))] - - if (Arr.isArrayNonEmpty(versions.slice(1))) { - return yield* Effect.fail( - new Error(`Found multiple Effect App versions. Please align first:\n${describeMentions(selectedMentions)}`) - ) - } - - const version = selectedMentions[0].normalized - const packageName = selectedMentions[0].name - const packagePath = packageManifestPaths[packageName] - - if (!packagePath) { - return yield* Effect.fail( - new Error(`No effect-app libs package manifest mapping for ${packageName}`) - ) - } - - const tagCandidates = [ - `${packageName}@${version}`, - `v${version}`, - version - ] - - let remoteRef: string | undefined - for (const candidate of tagCandidates) { - const tagResult = yield* runGetString( - `git ls-remote --exit-code --tags ${shellQuote(config.url)} ${shellQuote(`refs/tags/${candidate}`)}` - ) - .pipe(Effect.option) - - if (Option.isSome(tagResult)) { - remoteRef = candidate - break - } - } - - if (remoteRef) { - const syncRef = "refs/effa-sync/effect-app-tag" - yield* Effect.logInfo(`Using effect-app package: ${packageName}@${version}`) - yield* Effect.logInfo(`Using effect-app subtree ref: ${remoteRef}`) - yield* Effect.logInfo(`Using effect-app subtree url: ${config.url}`) - yield* fetchRemoteRef(`refs/tags/${remoteRef}`, syncRef) - return yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} . ${ - shellQuote(syncRef) - } --squash` - ) - } - - const defaultBranchOutput = yield* runGetString(`git ls-remote --symref ${shellQuote(config.url)} HEAD`) - const defaultBranch = yield* Effect.try({ - try: () => parseDefaultBranch(defaultBranchOutput), - catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)) - }) - - const cachePath = (yield* runGetString("git rev-parse --git-path effa-sync/effect-app-libs.git")).trim() - yield* run(`mkdir -p ${shellQuote(Path.dirname(cachePath))}`) - - if (!(yield* fs.exists(cachePath))) { - yield* run(`git init --bare ${shellQuote(cachePath)}`) - } - - yield* run( - `git --git-dir=${shellQuote(cachePath)} fetch --no-tags --prune ${shellQuote(config.url)} ${ - shellQuote(`+refs/heads/${defaultBranch}:refs/heads/${defaultBranch}`) - }` - ) - - const commits = (yield* runGetString( - `git --git-dir=${shellQuote(cachePath)} rev-list ${shellQuote(`refs/heads/${defaultBranch}`)}` - )) - .trim() - .split("\n") - .filter(isNonEmptyString) - - if (!Arr.isArrayNonEmpty(commits)) { - return yield* Effect.fail( - new Error(`No commits found on ${defaultBranch} for ${config.url}`) - ) - } - - let commit: string | undefined - for (const candidate of commits) { - const packageJsonResult = yield* runGetString( - `git --git-dir=${shellQuote(cachePath)} show ${shellQuote(`${candidate}:${packagePath}`)}` - ) - .pipe(Effect.option) - - if (Option.isNone(packageJsonResult)) continue - - const candidateVersion = yield* Effect.try({ - try: () => parsePackageVersion(packageJsonResult.value, packagePath), - catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)) - }) - - if (candidateVersion === version) { - commit = candidate - break - } - } - - if (!commit) { - return yield* Effect.fail( - new Error( - `No commit found on ${defaultBranch} where ${packagePath} has version ${version}. Tried tags: ${ - tagCandidates.join(", ") - }` - ) - ) - } - - const syncRef = "refs/effa-sync/effect-app-selected" - yield* run(`git --git-dir=${shellQuote(cachePath)} update-ref ${shellQuote(syncRef)} ${shellQuote(commit)}`) - - yield* Effect.logInfo(`Using effect-app package: ${packageName}@${version}`) - yield* Effect.logInfo(`Using effect-app subtree commit: ${commit}`) - yield* Effect.logInfo(`Using effect-app subtree url: ${config.url}`) - yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} ${ - shellQuote(cachePath) - } ${shellQuote(syncRef)} --squash` - ) -}) diff --git a/packages/cli/src/sync-effect-subtree.ts b/packages/cli/src/sync-effect-subtree.ts deleted file mode 100644 index a87d823d3..000000000 --- a/packages/cli/src/sync-effect-subtree.ts +++ /dev/null @@ -1,140 +0,0 @@ -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Option from "effect/Option" -import { RunCommandService } from "./os-command.ts" - -export interface SyncEffectConfig { - readonly manifestPaths: ReadonlyArray - readonly subtreePrefix: string - readonly url: string -} - -const depSections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"] as const - -const shellQuote = (value: string) => `'${value.replaceAll("'", "'\\''")}'` - -const normalizeVersion = (range: string) => { - const match = range.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/) - return match?.[0] -} - -export const syncEffectSubtree = Effect.fnUntraced(function*(config: SyncEffectConfig) { - const fs = yield* FileSystem.FileSystem - const { runGetExitCode, runGetString } = yield* RunCommandService - - const run = Effect.fnUntraced(function*(command: string) { - const exitCode = yield* runGetExitCode(command) - if (exitCode !== 0) { - return yield* Effect.fail(new Error(`Command failed with exit code ${exitCode}: ${command}`)) - } - }) - - const fetchRemoteRef = Effect.fnUntraced(function*(remoteRef: string, localRef: string) { - const rawRef = `${localRef}-raw` - - yield* run( - `git fetch --no-tags ${shellQuote(config.url)} ${shellQuote(`+${remoteRef}:${rawRef}`)}` - ) - - const commit = (yield* runGetString(`git rev-list -n1 ${shellQuote(rawRef)}`)).trim() - yield* run(`git update-ref ${shellQuote(localRef)} ${shellQuote(commit)}`) - }) - - const versionMentions: Array<{ - manifestPath: string - section: string - name: string - range: string - normalized: string - }> = [] - - for (const manifestPath of config.manifestPaths) { - if (!(yield* fs.exists(manifestPath))) continue - - const manifest = JSON.parse(yield* fs.readFileString(manifestPath)) - - for (const section of depSections) { - const deps = (manifest[section] ?? {}) as Record - - for (const [name, range] of Object.entries(deps)) { - if (name !== "effect" && !name.startsWith("@effect/")) continue - - const normalized = normalizeVersion(String(range)) - if (!normalized) continue - - versionMentions.push({ manifestPath, section, name, range: String(range), normalized }) - } - } - } - - const effectPackageMentions = versionMentions.filter((e) => e.name === "effect") - - const versionsFromEffectPackage = [...new Set(effectPackageMentions.map((e) => e.normalized))] - - const versionsFromEffectScope = [ - ...new Set( - versionMentions - .filter((e) => e.name.startsWith("@effect/") && e.normalized.startsWith("4.")) - .map((e) => e.normalized) - ) - ] - - const versions = versionsFromEffectPackage.length > 0 - ? versionsFromEffectPackage - : versionsFromEffectScope - - if (versions.length === 0) { - return yield* Effect.fail( - new Error("Could not find any effect/@effect dependency versions in manifests") - ) - } - - if (versions.length > 1) { - const details = (effectPackageMentions.length > 0 ? effectPackageMentions : versionMentions) - .map((e) => `${e.manifestPath} (${e.section}) ${e.name}: ${e.range}`) - .join("\n") - - return yield* Effect.fail( - new Error(`Found multiple Effect versions. Please align first:\n${details}`) - ) - } - - const version = versions[0] - const candidateRefs = [ - `effect@${version}`, - `@effect/effect@${version}`, - `@effect/ai-anthropic@${version}` - ] - - const url = shellQuote(config.url) - - let ref: string | undefined - for (const candidate of candidateRefs) { - const tagResult = yield* runGetString( - `git ls-remote --exit-code --tags ${url} ${shellQuote(`refs/tags/${candidate}`)}` - ) - .pipe(Effect.option) - - if (Option.isSome(tagResult)) { - ref = candidate - break - } - } - - if (!ref) { - return yield* Effect.fail( - new Error(`No matching tag found for version ${version}. Tried: ${candidateRefs.join(", ")}`) - ) - } - - yield* Effect.logInfo(`Using effect version: ${version}`) - yield* Effect.logInfo(`Using subtree ref: ${ref}`) - yield* Effect.logInfo(`Using subtree url: ${config.url}`) - const syncRef = "refs/effa-sync/effect-selected" - yield* fetchRemoteRef(`refs/tags/${ref}`, syncRef) - yield* run( - `git -c status.showUntrackedFiles=no subtree pull --prefix=${shellQuote(config.subtreePrefix)} . ${ - shellQuote(syncRef) - } --squash` - ) -}) diff --git a/packages/cli/src/sync-shared.ts b/packages/cli/src/sync-shared.ts deleted file mode 100644 index 2ad967a93..000000000 --- a/packages/cli/src/sync-shared.ts +++ /dev/null @@ -1,349 +0,0 @@ -import * as Effect from "effect/Effect" -import * as FileSystem from "effect/FileSystem" -import * as Path from "effect/Path" -import { RunCommandService } from "./os-command.ts" - -/** - * Project-side lockfile shape (`.shared.json`). - * - * `artifacts` maps SOURCE path (inside the shared repo) to DEST path (inside the - * consuming project). `exclude` lists source-relative paths to skip during sync. - */ -export interface SharedLockfile { - readonly repo: string - readonly ref: string - readonly artifacts: Record - readonly exclude?: ReadonlyArray - readonly synced_at?: string -} - -const sanitizeRepoSlug = (repo: string) => repo.replace(/[^A-Za-z0-9_.-]+/g, "_") - -const repoCloneUrl = (repo: string) => { - if (repo.startsWith("http") || repo.startsWith("git@")) return repo - // "github.com/effect-app/shared" → "git@github.com:effect-app/shared.git" - const m = repo.match(/^github\.com\/(.+?)\/(.+?)$/) - if (m) return `git@github.com:${m[1]}/${m[2]}.git` - return repo -} - -const joinPosix = (a: string, b: string) => a.endsWith("/") ? a + b : a + "/" + b - -const readLockfile = Effect.fnUntraced(function*(lockfilePath: string) { - const fs = yield* FileSystem.FileSystem - if (!(yield* fs.exists(lockfilePath))) { - return yield* Effect.fail(new Error(`No ${lockfilePath} found in current directory.`)) - } - const content = yield* fs.readFileString(lockfilePath) - return { content, lockfile: JSON.parse(content) as SharedLockfile } -}) - -const ensureCache = Effect.fnUntraced(function*(lockfile: SharedLockfile) { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const { runGetExitCode } = yield* RunCommandService - - const home = process.env["HOME"] ?? process.env["USERPROFILE"] - if (!home) return yield* Effect.fail(new Error("Cannot resolve home directory.")) - - const cacheRoot = path.join(home, ".cache", "effa", "shared") - const cachePath = path.join(cacheRoot, sanitizeRepoSlug(lockfile.repo)) - const cloneUrl = repoCloneUrl(lockfile.repo) - - if (!(yield* fs.exists(cachePath))) { - yield* runGetExitCode(`mkdir -p ${JSON.stringify(cacheRoot)}`) - yield* runGetExitCode(`git clone ${JSON.stringify(cloneUrl)} ${JSON.stringify(cachePath)}`) - } else { - yield* runGetExitCode("git fetch --all --tags --prune", cachePath) - } - - yield* runGetExitCode(`git checkout ${JSON.stringify(lockfile.ref)}`, cachePath) - - return cachePath -}) - -/** - * Resolve `requestedRef` (or the remote default branch HEAD if omitted) to a - * concrete commit sha in the fetched cache. Accepts branch names, tags, or - * shas; bare branch names are resolved against `origin/` first so the result - * is the latest remote commit, not a stale local ref. - */ -const resolveLatestSha = Effect.fnUntraced(function*(cachePath: string, requestedRef?: string) { - const { runGetExitCode, runGetString } = yield* RunCommandService - - const target = requestedRef - ?? (yield* runGetString("git rev-parse --abbrev-ref origin/HEAD", cachePath)).trim() - - const candidates = target.includes("/") ? [target] : [`origin/${target}`, target] - for (const candidate of candidates) { - const rev = JSON.stringify(`${candidate}^{commit}`) - const code = yield* runGetExitCode(`git rev-parse --verify --quiet ${rev}`, cachePath) - if (code === 0) { - return (yield* runGetString(`git rev-parse ${rev}`, cachePath)).trim() - } - } - - return yield* Effect.fail(new Error(`Cannot resolve ref: ${target}`)) -}) - -/** - * Walk artifact files. For each file in the artifact map (respecting excludes), - * yields `{ srcRel, srcAbs, destAbs }`. - */ -const walkArtifacts = Effect.fnUntraced(function*( - lockfile: SharedLockfile, - cachePath: string -) { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const { runGetString } = yield* RunCommandService - - const excludeSet = new Set(lockfile.exclude ?? []) - const results: Array<{ srcRel: string; srcAbs: string; destAbs: string; excluded: boolean }> = [] - - for (const [srcRel, destRel] of Object.entries(lockfile.artifacts)) { - const srcAbs = path.join(cachePath, srcRel) - const destAbs = path.resolve(destRel) - - if (!(yield* fs.exists(srcAbs))) continue - - const stat = yield* fs.stat(srcAbs) - - if (stat.type === "File") { - results.push({ srcRel, srcAbs, destAbs, excluded: excludeSet.has(srcRel) }) - continue - } - - const fileList = yield* runGetString(`find ${JSON.stringify(srcAbs)} -type f`) - for (const fileAbs of fileList.split("\n").filter((l) => l.trim() !== "")) { - const relInArtifact = path.relative(srcAbs, fileAbs) - const srcRelFull = joinPosix(srcRel, relInArtifact) - const destFileAbs = path.join(destAbs, relInArtifact) - results.push({ - srcRel: srcRelFull, - srcAbs: fileAbs, - destAbs: destFileAbs, - excluded: excludeSet.has(srcRelFull) - }) - } - } - - return results -}) - -/** - * Pull artifacts from the shared repo into the consuming project according to - * `.shared.json`. Idempotent — running twice without changes is a no-op. - * - * MVP: overwrites destination files. Caller is expected to inspect `git status` - * after sync to review local changes. Conflict handling lands in a follow-up. - */ -export const syncShared = Effect.fnUntraced(function*( - opts: { lockfilePath?: string; update?: boolean; ref?: string | undefined } = {} -) { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const { runGetExitCode } = yield* RunCommandService - - const lockfilePath = opts.lockfilePath ?? ".shared.json" - const { content: lockfileContent, lockfile: pinned } = yield* readLockfile(lockfilePath) - - yield* Effect.logInfo(`Syncing from ${pinned.repo} @ ${pinned.ref}`) - - const cachePath = yield* ensureCache(pinned) - yield* Effect.logInfo(`Cache: ${cachePath}`) - - let lockfile = pinned - if (opts.update) { - const newRef = yield* resolveLatestSha(cachePath, opts.ref) - if (newRef === pinned.ref) { - yield* Effect.logInfo(`Already at latest: ${newRef.slice(0, 12)}`) - } else { - yield* Effect.logInfo(`Updating ref ${pinned.ref.slice(0, 12)} → ${newRef.slice(0, 12)}`) - yield* runGetExitCode(`git checkout ${JSON.stringify(newRef)}`, cachePath) - } - lockfile = { ...pinned, ref: newRef } - } - - const files = yield* walkArtifacts(lockfile, cachePath) - - let copied = 0 - let skipped = 0 - - for (const { srcRel, srcAbs, destAbs, excluded } of files) { - if (excluded) { - yield* Effect.logInfo(` exclude ${srcRel}`) - skipped++ - continue - } - yield* runGetExitCode(`mkdir -p ${JSON.stringify(path.dirname(destAbs))}`) - yield* runGetExitCode(`cp ${JSON.stringify(srcAbs)} ${JSON.stringify(destAbs)}`) - copied++ - } - - const today = new Date().toISOString().slice(0, 10) - const trailingNewline = lockfileContent.endsWith("\n") ? "\n" : "" - const updated = { ...lockfile, synced_at: today } - yield* fs.writeFileString(lockfilePath, JSON.stringify(updated, null, 2) + trailingNewline) - - yield* Effect.logInfo(`Sync complete: ${copied} copied, ${skipped} excluded.`) - yield* Effect.logInfo("Review changes with: git status") -}) - -/** - * Compare project files to cache files. Reports each file as one of: - * M modified locally (project differs from cache) - * A added locally (project has file, cache does not — for tracked artifacts) - * D deleted locally (cache has file, project does not) - * E excluded (skipped by lockfile) - * - * Files that match cache exactly are not listed. - */ -export const syncDiff = Effect.fnUntraced(function*(opts: { lockfilePath?: string } = {}) { - const fs = yield* FileSystem.FileSystem - const { runGetString } = yield* RunCommandService - - const lockfilePath = opts.lockfilePath ?? ".shared.json" - const { lockfile } = yield* readLockfile(lockfilePath) - - yield* Effect.logInfo(`Diffing against ${lockfile.repo} @ ${lockfile.ref}`) - - const cachePath = yield* ensureCache(lockfile) - const files = yield* walkArtifacts(lockfile, cachePath) - - const changes: Array<{ kind: "M" | "D" | "E"; srcRel: string; destPath: string }> = [] - - for (const { srcRel, srcAbs, destAbs, excluded } of files) { - if (excluded) { - changes.push({ kind: "E", srcRel, destPath: destAbs }) - continue - } - - if (!(yield* fs.exists(destAbs))) { - changes.push({ kind: "D", srcRel, destPath: destAbs }) - continue - } - - const srcHash = (yield* runGetString(`sha256sum ${JSON.stringify(srcAbs)}`)).split(" ")[0] - const destHash = (yield* runGetString(`sha256sum ${JSON.stringify(destAbs)}`)).split(" ")[0] - - if (srcHash !== destHash) { - changes.push({ kind: "M", srcRel, destPath: destAbs }) - } - } - - if (changes.length === 0) { - yield* Effect.logInfo("In sync. No diff.") - return - } - - for (const { kind, srcRel, destPath } of changes) { - yield* Effect.logInfo(`${kind} ${srcRel} -> ${destPath}`) - } - yield* Effect.logInfo("") - yield* Effect.logInfo( - `Summary: ${changes.filter((c) => c.kind === "M").length} modified, ${ - changes - .filter((c) => c.kind === "D") - .length - } missing in project, ${changes.filter((c) => c.kind === "E").length} excluded.` - ) -}) - -/** - * Push locally-modified synced files back to the shared repo on a new branch. - * Optionally opens a PR via `gh pr create`. - * - * Workflow: - * 1. Ensure cache fresh at pinned ref. - * 2. Detect modified files via hash compare (project vs cache). - * 3. Create new branch in cache, copy modified files in, commit, push. - * 4. Optionally run `gh pr create` if `--pr` set. - */ -export const syncPush = Effect.fnUntraced(function*(opts: { - lockfilePath?: string | undefined - message?: string | undefined - branch?: string | undefined - pr?: boolean | undefined -} = {}) { - const { runGetExitCode, runGetString } = yield* RunCommandService - - const lockfilePath = opts.lockfilePath ?? ".shared.json" - const { lockfile } = yield* readLockfile(lockfilePath) - - const cachePath = yield* ensureCache(lockfile) - const files = yield* walkArtifacts(lockfile, cachePath) - - const fs = yield* FileSystem.FileSystem - const modified: Array<{ srcRel: string; srcAbs: string; destAbs: string }> = [] - const deleted: Array<{ srcRel: string; srcAbs: string }> = [] - - for (const { srcRel, srcAbs, destAbs, excluded } of files) { - if (excluded) continue - if (!(yield* fs.exists(destAbs))) { - deleted.push({ srcRel, srcAbs }) - continue - } - const srcHash = (yield* runGetString(`sha256sum ${JSON.stringify(srcAbs)}`)).split(" ")[0] - const destHash = (yield* runGetString(`sha256sum ${JSON.stringify(destAbs)}`)).split(" ")[0] - if (srcHash !== destHash) { - modified.push({ srcRel, srcAbs, destAbs }) - } - } - - if (modified.length === 0 && deleted.length === 0) { - yield* Effect.logInfo("No local modifications to push.") - return - } - - yield* Effect.logInfo(`Pushing ${modified.length} modified, ${deleted.length} deleted file(s):`) - for (const { srcRel } of modified) { - yield* Effect.logInfo(` M ${srcRel}`) - } - for (const { srcRel } of deleted) { - yield* Effect.logInfo(` D ${srcRel}`) - } - - const branch = opts.branch - ?? `sync/from-${sanitizeRepoSlug(process.cwd().split("/").pop() ?? "project")}-${ - new Date().toISOString().slice(0, 10) - }` - - const message = opts.message ?? "sync: propagate local edits" - - // Stash anything in cache (defensive), create branch from pinned ref. - yield* runGetExitCode(`git stash --include-untracked || true`, cachePath) - yield* runGetExitCode(`git checkout -B ${JSON.stringify(branch)} ${JSON.stringify(lockfile.ref)}`, cachePath) - - for (const { srcAbs, destAbs } of modified) { - yield* runGetExitCode(`cp ${JSON.stringify(destAbs)} ${JSON.stringify(srcAbs)}`) - } - for (const { srcAbs } of deleted) { - yield* runGetExitCode(`git rm -f ${JSON.stringify(srcAbs)}`, cachePath) - } - - yield* runGetExitCode(`git add -A`, cachePath) - yield* runGetExitCode( - `git -c commit.gpgsign=false commit -m ${JSON.stringify(message)}`, - cachePath - ) - yield* runGetExitCode(`git push -u origin ${JSON.stringify(branch)}`, cachePath) - - if (opts.pr) { - yield* runGetExitCode( - `gh pr create --title ${JSON.stringify(message)} --body ${ - JSON.stringify(`Propagated from project at ${process.cwd()}.\n\nFiles:\n${ - [ - ...modified.map((m) => `- M ${m.srcRel}`), - ...deleted.map((d) => `- D ${d.srcRel}`) - ] - .join("\n") - }`) - } --head ${JSON.stringify(branch)}`, - cachePath - ) - } - - yield* Effect.logInfo(`Pushed branch ${branch} to shared repo.`) - yield* Effect.logInfo(opts.pr ? "PR opened." : "Open a PR with: gh pr create (from the cache dir)") -}) diff --git a/packages/cli/src/update-packages.ts b/packages/cli/src/update-packages.ts deleted file mode 100644 index ae2c84386..000000000 --- a/packages/cli/src/update-packages.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const makeNcuUpdateCommand = ( - filter: string, - options?: { readonly recursive?: boolean } -) => `pnpm ${options?.recursive ? "-r " : ""}exec ncu -u --target semver --filter "${filter}"` diff --git a/packages/cli/test.gists.yaml b/packages/cli/test.gists.yaml deleted file mode 100644 index 41b4b992b..000000000 --- a/packages/cli/test.gists.yaml +++ /dev/null @@ -1,27 +0,0 @@ -gists: - # "cli-config-banana": - # company: "banana" # acts like a namespace - # description: "Package configuration and main source for Effect-App CLI" - # public: false - # files: - # - "package.json" - # # - "packages/cli/src/index.ts" - "cli-config-mela": - company: "mela" # acts like a namespace - description: "Package configuration and main source for Effect-App CLI" - public: false - files: - - "package.json" - - # "test-files": - # description: "Test YAML files for gist functionality" - # public: false - # files: - # - "gists.yaml" - -settings: - # GitHub token environment variable name - token_env: "GIST_GITHUB_TOKEN" - - # Base directory for files (relative to where the CLI is run) - base_directory: "." diff --git a/packages/cli/test/argv-patch.test.ts b/packages/cli/test/argv-patch.test.ts deleted file mode 100644 index 11c0ea30c..000000000 --- a/packages/cli/test/argv-patch.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { execFileSync } from "node:child_process" -import path from "node:path" -import url from "node:url" -import { describe, expect, it } from "vitest" -import { patchArgvForWrapCommands } from "../src/argv-patch.js" - -describe("patchArgvForWrapCommands", () => { - const make = (...args: Array) => ["node", "effect-app-cli", ...args] - - describe("joins wrap args into a single element", () => { - it("index-multi with tsc --build", () => { - const argv = make("index-multi", "tsc", "--build") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --build")) - }) - - it("packagejson with tsc --build and tsconfig path", () => { - const argv = make("packagejson", "tsc", "--build", "./tsconfig.src.json") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson", "tsc --build ./tsconfig.src.json")) - }) - - it("packagejson-packages with pnpm check", () => { - const argv = make("packagejson-packages", "pnpm", "check") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson-packages", "pnpm check")) - }) - - it("single wrap arg stays as-is", () => { - const argv = make("index-multi", "tsc") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc")) - }) - - it("wrap args with multiple --flags", () => { - const argv = make("index-multi", "tsc", "--build", "--watch", "--verbose") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --build --watch --verbose")) - }) - - it("wrap args with short flags", () => { - const argv = make("packagejson", "pnpm", "-r", "check") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson", "pnpm -r check")) - }) - - it("wrap args with --flag=value syntax", () => { - const argv = make("index-multi", "tsc", "--build=./tsconfig.json") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --build=./tsconfig.json")) - }) - - it("wrap args with --flag=\"quoted value\" syntax", () => { - const argv = make("index-multi", "tsc", "--outDir=\"dist/build\"") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "tsc --outDir=\"dist/build\"")) - }) - - it("wrap args with mixed quoted and unquoted flags", () => { - const argv = make("packagejson", "cmd", "--x=\"abc\"", "--y", "plain") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson", "cmd --x=\"abc\" --y plain")) - }) - - it("wrap args with single-quoted value in flag", () => { - const argv = make("index-multi", "cmd", "--config='my config.json'") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi", "cmd --config='my config.json'")) - }) - - it("wrap args with spaces inside quoted flag value", () => { - const argv = make("packagejson-packages", "cmd", "--msg=\"hello world\"", "--verbose") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("packagejson-packages", "cmd --msg=\"hello world\" --verbose")) - }) - }) - - describe("does nothing for non-wrap subcommands", () => { - it("nuke with flags", () => { - const argv = make("nuke", "--dry-run") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("nuke", "--dry-run")) - }) - - it("gist with --config flag", () => { - const argv = make("gist", "--config", "gists.yaml") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("gist", "--config", "gists.yaml")) - }) - - it("ue", () => { - const argv = make("ue") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("ue")) - }) - - it("link with path arg", () => { - const argv = make("link", "../../effect-app/libs") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("link", "../../effect-app/libs")) - }) - - it("unrecognized command", () => { - const argv = make("unknown", "tsc", "--build") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("unknown", "tsc", "--build")) - }) - }) - - describe("edge cases", () => { - it("no subcommand", () => { - const argv = ["node", "effect-app-cli"] - patchArgvForWrapCommands(argv) - expect(argv).toEqual(["node", "effect-app-cli"]) - }) - - it("subcommand with no trailing args", () => { - const argv = make("index-multi") - patchArgvForWrapCommands(argv) - expect(argv).toEqual(make("index-multi")) - }) - - it("empty argv", () => { - const argv: Array = [] - patchArgvForWrapCommands(argv) - expect(argv).toEqual([]) - }) - - it("single element argv", () => { - const argv = ["node"] - patchArgvForWrapCommands(argv) - expect(argv).toEqual(["node"]) - }) - - it("mutates the original array", () => { - const argv = make("index-multi", "tsc", "--build") - const ref = argv - patchArgvForWrapCommands(argv) - expect(ref).toBe(argv) - expect(ref).toEqual(make("index-multi", "tsc --build")) - }) - - it("different node/script paths", () => { - const argv = ["/usr/local/bin/node", "/home/user/.npm/bin/effa", "packagejson", "pnpm", "check"] - patchArgvForWrapCommands(argv) - expect(argv).toEqual(["/usr/local/bin/node", "/home/user/.npm/bin/effa", "packagejson", "pnpm check"]) - }) - }) -}) - -describe.skip("e2e: CLI spawns wrap command correctly", () => { - const binPath = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "../bin.js") - - const run = (...args: Array) => - execFileSync("node", [binPath, ...args], { - encoding: "utf-8", - timeout: 10_000, - env: { ...process.env, NO_COLOR: "1" } - }) - - it("packagejson spawns 'echo hello' and captures output", () => { - const out = run("packagejson", "echo", "hello") - expect(out).toContain("Spawning child command: echo hello") - expect(out).toContain("hello") - }) - - it("packagejson spawns command with --flags without quoting", () => { - const out = run("packagejson", "echo", "--build-test", "extra") - expect(out).toContain("Spawning child command: echo --build-test extra") - expect(out).toContain("--build-test extra") - }) - - it("packagejson with single quoted arg still works", () => { - const out = run("packagejson", "echo from-quoted") - expect(out).toContain("Spawning child command: echo from-quoted") - expect(out).toContain("from-quoted") - }) - - it("propagates non-zero exit code", () => { - try { - run("packagejson", "exit 42") - expect.unreachable("should have thrown") - } catch (e: any) { - expect(e.status).toBe(42) - } - }) - - it("index-multi spawns wrap command with --flags", () => { - const out = run("index-multi", "echo", "--build", "done") - expect(out).toContain("Spawning child command: echo --build done") - expect(out).toContain("--build done") - }) - - it("packagejson spawns command with --flag=\"value\" syntax", () => { - const out = run("packagejson", "echo", "--x=\"abc\"", "--y") - expect(out).toContain("Spawning child command: echo --x=\"abc\" --y") - }) -}) diff --git a/packages/cli/test/gist-cache.test.ts b/packages/cli/test/gist-cache.test.ts deleted file mode 100644 index 902e71424..000000000 --- a/packages/cli/test/gist-cache.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Effect, Layer } from "effect" -import { describe, expect, it } from "vitest" -import { GHGistService } from "../src/gist.js" -import { RunCommandService } from "../src/os-command.js" - -describe("GHGistService.loadGistCache", () => { - it("loads the company cache from the configured Gist ID without discovery", async () => { - const commands: Array = [] - const runCommand = Layer.succeed(RunCommandService)({ - runGetExitCode: (command) => { - commands.push(command) - return Effect.die(`Unexpected command: ${command}`) - }, - runGetString: (command) => { - commands.push(command) - - switch (command) { - case "gh gist view configured-cache-id --files": - return Effect.succeed("effa-gist.cache\nmako.json\n") - case "gh gist view configured-cache-id -f \"mako.json\"": - return Effect.succeed("[{\"name\":\"mako\",\"id\":\"content-gist-id\"}]") - default: - return Effect.die(`Unexpected command: ${command}`) - } - } - }) - - const cache = await Effect.runPromise( - Effect - .gen(function*() { - const gh = yield* GHGistService - return yield* gh.loadGistCache("configured-cache-id", "mako") - }) - .pipe( - Effect.provide(GHGistService.DefaultWithoutDependencies), - Effect.provide(runCommand) - ) - ) - - expect(cache.gist_id).toBe("configured-cache-id") - expect(cache.company).toBe("mako") - expect(cache.entries.map(({ id, name }) => ({ id, name }))).toEqual([ - { id: "content-gist-id", name: "mako" } - ]) - expect(commands).toEqual([ - "gh gist view configured-cache-id --files", - "gh gist view configured-cache-id -f \"mako.json\"" - ]) - }) - - it("creates a missing company cache file once without retrying", async () => { - const commands: Array = [] - const runCommand = Layer.succeed(RunCommandService)({ - runGetExitCode: (command) => { - commands.push(command) - return Effect.die(`Unexpected command: ${command}`) - }, - runGetString: (command) => { - commands.push(command) - - switch (command) { - case "gh gist view configured-cache-id --files": - return Effect.succeed("effa-gist.cache\n") - case "echo \"[]\" | gh gist edit configured-cache-id -a mako.json -": - return Effect.succeed("") - default: - return Effect.die(`Unexpected command: ${command}`) - } - } - }) - - const cache = await Effect.runPromise( - Effect - .gen(function*() { - const gh = yield* GHGistService - return yield* gh.loadGistCache("configured-cache-id", "mako") - }) - .pipe( - Effect.provide(GHGistService.DefaultWithoutDependencies), - Effect.provide(runCommand) - ) - ) - - expect(cache.gist_id).toBe("configured-cache-id") - expect(cache.company).toBe("mako") - expect(cache.entries).toEqual([]) - expect(commands).toEqual([ - "gh gist view configured-cache-id --files", - "echo \"[]\" | gh gist edit configured-cache-id -a mako.json -" - ]) - }) -}) diff --git a/packages/cli/test/update-packages.test.ts b/packages/cli/test/update-packages.test.ts deleted file mode 100644 index 9cca50be2..000000000 --- a/packages/cli/test/update-packages.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe, expect, it } from "vitest" -import { makeNcuUpdateCommand } from "../src/update-packages.js" - -describe("makeNcuUpdateCommand", () => { - it("targets the highest version allowed by the existing semver range", () => { - expect(makeNcuUpdateCommand("@effect-app/*")).toBe( - "pnpm exec ncu -u --target semver --filter \"@effect-app/*\"" - ) - expect(makeNcuUpdateCommand("@effect/*", { recursive: true })).toBe( - "pnpm -r exec ncu -u --target semver --filter \"@effect/*\"" - ) - }) -}) diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json deleted file mode 100644 index 6326ef26c..000000000 --- a/packages/cli/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": [ - "esnext" - ], - "moduleResolution": "Node16", - "outDir": "./dist", - "rootDir": "./src", - "verbatimModuleSyntax": true, - "tsBuildInfoFile": "./dist/.tsbuildinfo" - }, - "include": [ - "./src/**/*.ts" - ], - "references": [] -} diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts deleted file mode 100644 index 85903fa64..000000000 --- a/packages/cli/vitest.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -import { defineConfig } from "vitest/config" -import makeConfig from "../../vite.config.base" - -export default defineConfig(makeConfig(__dirname)) diff --git a/packages/e2e/CHANGELOG.md b/packages/e2e/CHANGELOG.md deleted file mode 100644 index 181bd43ec..000000000 --- a/packages/e2e/CHANGELOG.md +++ /dev/null @@ -1,881 +0,0 @@ -# @effect-app/e2e - -## 4.0.1 - -### Patch Changes - -- Updated dependencies [222e619] - - effect-app@4.0.1 - - @effect-app/infra@4.0.1 - - @effect-app/vue@4.0.1 - -## 4.0.0 - -### Patch Changes - -- Updated dependencies [eddda2e] -- Updated dependencies [07dd7b9] -- Updated dependencies [439cbeb] -- Updated dependencies [da83c1f] -- Updated dependencies [b2df3fa] -- Updated dependencies [199e9a5] -- Updated dependencies [b035b1c] -- Updated dependencies [664e83d] -- Updated dependencies [abb9c99] -- Updated dependencies [71a0719] -- Updated dependencies [aa436d3] -- Updated dependencies [a4dff57] -- Updated dependencies [ba4bdc3] -- Updated dependencies [52b0b01] -- Updated dependencies [52b0b01] -- Updated dependencies [eaf0115] -- Updated dependencies [eceb3a3] -- Updated dependencies [9d3495e] -- Updated dependencies [3436d44] -- Updated dependencies [52b0b01] -- Updated dependencies [f317c5e] -- Updated dependencies [e585c9c] -- Updated dependencies [e4ff9a6] -- Updated dependencies [f4b58cd] -- Updated dependencies [33b0544] -- Updated dependencies [6821fc5] -- Updated dependencies [38289e0] -- Updated dependencies [99c43c4] -- Updated dependencies [52b0b01] -- Updated dependencies [08d092c] -- Updated dependencies [947fe20] -- Updated dependencies [1f9d7da] -- Updated dependencies [21ac90a] -- Updated dependencies [a74a894] -- Updated dependencies [d1fc90a] -- Updated dependencies [32f05c8] -- Updated dependencies [50ce7e6] -- Updated dependencies [6cfd83d] -- Updated dependencies [1c858d3] -- Updated dependencies [52b0b01] -- Updated dependencies [6a3d364] -- Updated dependencies [6884a32] -- Updated dependencies [aa5ef5c] -- Updated dependencies [82f66f1] -- Updated dependencies [08d30af] -- Updated dependencies [8ff0bf9] -- Updated dependencies [992d9fa] -- Updated dependencies [fb93244] -- Updated dependencies [aac6308] -- Updated dependencies [505bfa9] -- Updated dependencies [939bebc] -- Updated dependencies [2b4c324] -- Updated dependencies [fd67f0b] -- Updated dependencies [ba789a2] -- Updated dependencies [ab289d4] -- Updated dependencies [c31c673] -- Updated dependencies [bac9f1a] -- Updated dependencies [3eb66e9] -- Updated dependencies [5aed2e3] -- Updated dependencies [48f1457] -- Updated dependencies [de9fb83] -- Updated dependencies [0520dd4] -- Updated dependencies [13c2fd9] -- Updated dependencies [52b0b01] -- Updated dependencies [fdb9cb3] -- Updated dependencies [c5e348f] -- Updated dependencies [99a2e9b] -- Updated dependencies [8eb6737] -- Updated dependencies [4a7d95a] -- Updated dependencies [e0c4835] -- Updated dependencies [27bf9b6] -- Updated dependencies [8753c52] -- Updated dependencies [32dbc54] -- Updated dependencies [0a0030f] -- Updated dependencies [0263827] -- Updated dependencies [0263827] -- Updated dependencies [d23e3f6] -- Updated dependencies [aeb17bc] -- Updated dependencies [6613f3d] -- Updated dependencies [c0e5a1b] -- Updated dependencies [10b55ff] -- Updated dependencies [14aba14] -- Updated dependencies [a0075b8] -- Updated dependencies [f052d38] -- Updated dependencies [f233f3d] -- Updated dependencies [18bae5b] -- Updated dependencies [f313973] -- Updated dependencies [edc52e4] -- Updated dependencies [ea1bd46] -- Updated dependencies [85a8275] -- Updated dependencies [50b022e] -- Updated dependencies [52b0b01] -- Updated dependencies [47e3742] -- Updated dependencies [52b0b01] -- Updated dependencies [4149577] -- Updated dependencies [04fc985] -- Updated dependencies [458bb1b] -- Updated dependencies [3365758] -- Updated dependencies [54ec1ef] -- Updated dependencies [f21190c] -- Updated dependencies [1176240] -- Updated dependencies [3cf0c42] -- Updated dependencies [b6cb25b] -- Updated dependencies [fce6ee9] -- Updated dependencies [65835a6] -- Updated dependencies [be3300d] -- Updated dependencies [0d4e0b8] -- Updated dependencies [31739d7] -- Updated dependencies [0b21a02] -- Updated dependencies [eed57c6] -- Updated dependencies [bbaa67e] -- Updated dependencies [1015582] -- Updated dependencies [a211c12] -- Updated dependencies [ebffba8] -- Updated dependencies [29c39d2] -- Updated dependencies [fbf47b8] -- Updated dependencies [47e53f5] -- Updated dependencies [347af48] -- Updated dependencies [70f1f27] -- Updated dependencies [d786b91] -- Updated dependencies [dfd5562] -- Updated dependencies [2d85646] -- Updated dependencies [2f862a4] -- Updated dependencies [c80e781] -- Updated dependencies [fe2caba] -- Updated dependencies [9a9e46c] -- Updated dependencies [5a709d1] -- Updated dependencies [b4ffee0] -- Updated dependencies [1ab3cf7] -- Updated dependencies [d195003] -- Updated dependencies [7519318] -- Updated dependencies [1b38043] -- Updated dependencies [07a57b6] -- Updated dependencies [bd26832] -- Updated dependencies [3053760] -- Updated dependencies [f28fce5] -- Updated dependencies [c9e0c44] -- Updated dependencies [52b0b01] -- Updated dependencies [55c6572] -- Updated dependencies [f88ea34] -- Updated dependencies [dd239fa] -- Updated dependencies [66fd718] -- Updated dependencies [52b0b01] -- Updated dependencies [c991be1] -- Updated dependencies [d738811] -- Updated dependencies [702d51c] -- Updated dependencies [fc41dcf] -- Updated dependencies [8fffc3c] -- Updated dependencies [52b0b01] -- Updated dependencies [34f6a97] -- Updated dependencies [a788432] -- Updated dependencies [10e90d5] -- Updated dependencies [4b95009] -- Updated dependencies [52b0b01] -- Updated dependencies [178480a] -- Updated dependencies [2e4c018] -- Updated dependencies [e02a258] -- Updated dependencies [c7fbd58] -- Updated dependencies [985176b] -- Updated dependencies [50d7fc1] -- Updated dependencies [f7290aa] -- Updated dependencies [2871841] -- Updated dependencies [dc465e3] -- Updated dependencies [b4d5f55] -- Updated dependencies [f44800c] -- Updated dependencies [a0075b8] -- Updated dependencies [f052d38] -- Updated dependencies [186de3a] -- Updated dependencies [21017d5] -- Updated dependencies [0541f0d] -- Updated dependencies [2ebf8ae] -- Updated dependencies [8c645d5] -- Updated dependencies [8cb3de4] -- Updated dependencies [30c512d] -- Updated dependencies [50b022e] -- Updated dependencies [52b0b01] -- Updated dependencies [b952f19] -- Updated dependencies [a37aa38] -- Updated dependencies [99c43c4] -- Updated dependencies [52b0b01] -- Updated dependencies [28a0b29] -- Updated dependencies [c1e73de] -- Updated dependencies [d867272] -- Updated dependencies [1b57aa4] -- Updated dependencies [24f5dd0] -- Updated dependencies [0c42d67] -- Updated dependencies [774a9b3] -- Updated dependencies [d67d17a] -- Updated dependencies [8f09f77] -- Updated dependencies [50ce7e6] -- Updated dependencies [d71d976] -- Updated dependencies [2aa8e5e] -- Updated dependencies [ed1b8a9] -- Updated dependencies [29a1e57] -- Updated dependencies [186de3a] -- Updated dependencies [52b0b01] -- Updated dependencies [e3ceaba] -- Updated dependencies [52b0b01] -- Updated dependencies [6fff09c] -- Updated dependencies [dab6992] -- Updated dependencies [11422f8] -- Updated dependencies [eb28ea5] -- Updated dependencies [52b0b01] -- Updated dependencies [7d5cefc] -- Updated dependencies [8f1cf6a] -- Updated dependencies [ee9694e] -- Updated dependencies [52a31dd] -- Updated dependencies [3a588e4] -- Updated dependencies [848d26d] -- Updated dependencies [db285e9] -- Updated dependencies [52b0b01] -- Updated dependencies [ca94edf] -- Updated dependencies [d1c15d3] -- Updated dependencies [4bc4a27] -- Updated dependencies [a1b59bc] -- Updated dependencies [dc07df5] -- Updated dependencies [0fe925d] -- Updated dependencies [40585ca] -- Updated dependencies [5ac46cb] -- Updated dependencies [a354345] -- Updated dependencies [52b0b01] -- Updated dependencies [1cee480] -- Updated dependencies [186de3a] -- Updated dependencies [1e4c989] -- Updated dependencies [aeb17bc] -- Updated dependencies [48d9f36] -- Updated dependencies [52b0b01] -- Updated dependencies [52b0b01] -- Updated dependencies [d31253f] -- Updated dependencies [5615e47] -- Updated dependencies [3e46e7b] -- Updated dependencies [18fd1df] -- Updated dependencies [cec026d] -- Updated dependencies [902ca1b] -- Updated dependencies [4622a75] -- Updated dependencies [925214b] -- Updated dependencies [95995d5] -- Updated dependencies [7191fc1] -- Updated dependencies [88838fb] -- Updated dependencies [b3ed68a] -- Updated dependencies [f16e766] -- Updated dependencies [3bae238] -- Updated dependencies [b2e438f] -- Updated dependencies [f150cf9] -- Updated dependencies [0c88f78] -- Updated dependencies [d16845e] -- Updated dependencies [261470f] -- Updated dependencies [beae3a0] -- Updated dependencies [ddd9505] -- Updated dependencies [8792221] -- Updated dependencies [1f103b2] -- Updated dependencies [0054611] -- Updated dependencies [4bc3d05] -- Updated dependencies [ebbb799] -- Updated dependencies [5773159] -- Updated dependencies [ddc5492] -- Updated dependencies [7bd8234] -- Updated dependencies [54bfc59] -- Updated dependencies [3d732b1] -- Updated dependencies [ad0ede6] -- Updated dependencies [818e047] -- Updated dependencies [89d8b3a] -- Updated dependencies [52b0b01] -- Updated dependencies [08d2e70] -- Updated dependencies [9ea024d] -- Updated dependencies [5f9cd6a] -- Updated dependencies [6252808] -- Updated dependencies [e6f2341] -- Updated dependencies [28777c1] -- Updated dependencies [1576688] -- Updated dependencies [821468d] -- Updated dependencies [2495ace] -- Updated dependencies [e33f1eb] -- Updated dependencies [0b3e00e] -- Updated dependencies [52b0b01] -- Updated dependencies [c215db8] -- Updated dependencies [12abb55] -- Updated dependencies [0cff7c1] -- Updated dependencies [ba61aad] -- Updated dependencies [c1a6fdc] -- Updated dependencies [8ae8b53] -- Updated dependencies [a5248a9] -- Updated dependencies [37089ea] -- Updated dependencies [0e824ef] -- Updated dependencies [62627e9] -- Updated dependencies [52b0b01] -- Updated dependencies [eb06b32] -- Updated dependencies [52b0b01] -- Updated dependencies [52b0b01] -- Updated dependencies [52b0b01] -- Updated dependencies [dd73a4a] -- Updated dependencies [8a4a6d2] -- Updated dependencies [dba5779] -- Updated dependencies [025de47] -- Updated dependencies [0fa0e80] -- Updated dependencies [acfdc9e] -- Updated dependencies [1186b09] -- Updated dependencies [9be71e1] -- Updated dependencies [48f1457] -- Updated dependencies [48f1457] -- Updated dependencies [48f1457] -- Updated dependencies [c313c07] -- Updated dependencies [0ddda6f] -- Updated dependencies [bb3f51d] -- Updated dependencies [db7ba34] -- Updated dependencies [a69da09] -- Updated dependencies [3c1f52d] -- Updated dependencies [3a8c710] -- Updated dependencies [f22a026] -- Updated dependencies [186de3a] -- Updated dependencies [1d85785] -- Updated dependencies [b9586f8] -- Updated dependencies [0054611] -- Updated dependencies [9569911] -- Updated dependencies [583393f] -- Updated dependencies [828d264] -- Updated dependencies [61a3931] -- Updated dependencies [8ff0bf9] -- Updated dependencies [7fa3045] -- Updated dependencies [3dc0d2a] -- Updated dependencies [d4bf24a] -- Updated dependencies [b0db40f] -- Updated dependencies [459697f] -- Updated dependencies [738b482] -- Updated dependencies [3eda52e] -- Updated dependencies [fa3533e] -- Updated dependencies [b53c59e] -- Updated dependencies [b52b424] -- Updated dependencies [7c25dbb] -- Updated dependencies [b8b9080] -- Updated dependencies [b90fa30] -- Updated dependencies [57db551] -- Updated dependencies [3613e87] -- Updated dependencies [52b0b01] -- Updated dependencies [7119320] -- Updated dependencies [e944bca] -- Updated dependencies [ffbff5d] -- Updated dependencies [dbcc53b] -- Updated dependencies [18b915f] -- Updated dependencies [74e6d40] -- Updated dependencies [b63f10f] -- Updated dependencies [52b0b01] -- Updated dependencies [6a0e008] -- Updated dependencies [140e192] -- Updated dependencies [9992e70] -- Updated dependencies [43611c7] -- Updated dependencies [24f0a5a] -- Updated dependencies [256ae85] -- Updated dependencies [ac62e48] -- Updated dependencies [1587bfc] -- Updated dependencies [8bd9a11] -- Updated dependencies [52b0b01] -- Updated dependencies [fac725d] -- Updated dependencies [3200fa8] -- Updated dependencies [412f08b] -- Updated dependencies [79eb019] -- Updated dependencies [6ae3050] -- Updated dependencies [f353d48] -- Updated dependencies [0d7d197] -- Updated dependencies [52b0b01] -- Updated dependencies [01bab22] -- Updated dependencies [2a86a17] -- Updated dependencies [52b0b01] -- Updated dependencies [c3299f7] -- Updated dependencies [23a7167] -- Updated dependencies [e71eb78] -- Updated dependencies [8062ad3] -- Updated dependencies [39996f7] -- Updated dependencies [6b57330] -- Updated dependencies [e2d00b5] -- Updated dependencies [547e2e1] -- Updated dependencies [52b0b01] -- Updated dependencies -- Updated dependencies [ea32222] -- Updated dependencies [7ca66ce] -- Updated dependencies [57a1862] -- Updated dependencies [3e855bc] -- Updated dependencies [78d076a] -- Updated dependencies [ffd140f] -- Updated dependencies [e8842aa] -- Updated dependencies [b6dda09] -- Updated dependencies [0263827] -- Updated dependencies [8bd5bfe] -- Updated dependencies [2231ef8] -- Updated dependencies [724da29] -- Updated dependencies [413022d] -- Updated dependencies [f052d38] -- Updated dependencies [fc98fb7] -- Updated dependencies [1df5cf5] -- Updated dependencies [b241ae5] -- Updated dependencies [677821a] -- Updated dependencies [c7bbc41] -- Updated dependencies [52b0b01] -- Updated dependencies [52b0b01] -- Updated dependencies [7328c76] -- Updated dependencies [52b0b01] -- Updated dependencies [7fd35e4] -- Updated dependencies [89b7d2f] -- Updated dependencies [52b0b01] -- Updated dependencies [4bbeb19] - - @effect-app/vue@4.0.0 - - effect-app@4.0.0 - - @effect-app/infra@4.0.0 - -## 4.0.0-beta.333 - -### Patch Changes - -- Updated dependencies [e2d00b5] - - effect-app@4.0.0-beta.333 - - @effect-app/infra@4.0.0-beta.333 - - @effect-app/vue@4.0.0-beta.333 - -## 4.0.0-beta.332 - -### Patch Changes - -- Updated dependencies [6b57330] - - effect-app@4.0.0-beta.332 - - @effect-app/infra@4.0.0-beta.332 - - @effect-app/vue@4.0.0-beta.332 - -## 4.0.0-beta.331 - -### Patch Changes - -- Updated dependencies [65835a6] - - @effect-app/infra@4.0.0-beta.331 - - effect-app@4.0.0-beta.331 - - @effect-app/vue@4.0.0-beta.331 - -## 4.0.0-beta.330 - -### Patch Changes - -- Updated dependencies [48d9f36] - - effect-app@4.0.0-beta.330 - - @effect-app/infra@4.0.0-beta.330 - - @effect-app/vue@4.0.0-beta.330 - -## 4.0.0-beta.329 - -### Patch Changes - -- Updated dependencies [459697f] - - effect-app@4.0.0-beta.329 - - @effect-app/infra@4.0.0-beta.329 - - @effect-app/vue@4.0.0-beta.329 - -## 4.0.0-beta.328 - -### Patch Changes - -- Updated dependencies [3a8c710] - - effect-app@4.0.0-beta.328 - - @effect-app/infra@4.0.0-beta.328 - - @effect-app/vue@4.0.0-beta.328 - -## 4.0.0-beta.327 - -### Patch Changes - -- Updated dependencies [ebffba8] - - @effect-app/infra@4.0.0-beta.327 - - effect-app@4.0.0-beta.327 - - @effect-app/vue@4.0.0-beta.327 - -## 4.0.0-beta.326 - -### Patch Changes - -- Updated dependencies [2ebf8ae] - - effect-app@4.0.0-beta.326 - - @effect-app/infra@4.0.0-beta.326 - - @effect-app/vue@4.0.0-beta.326 - -## 4.0.0-beta.325 - -### Patch Changes - -- Updated dependencies [f233f3d] - - effect-app@4.0.0-beta.325 - - @effect-app/infra@4.0.0-beta.325 - - @effect-app/vue@4.0.0-beta.325 - -## 4.0.0-beta.324 - -### Patch Changes - -- Updated dependencies [a0075b8] -- Updated dependencies [a0075b8] - - effect-app@4.0.0-beta.324 - - @effect-app/infra@4.0.0-beta.324 - - @effect-app/vue@4.0.0-beta.324 - -## 4.0.0-beta.323 - -### Patch Changes - -- Updated dependencies [ab289d4] - - effect-app@4.0.0-beta.323 - - @effect-app/infra@4.0.0-beta.323 - - @effect-app/vue@4.0.0-beta.323 - -## 4.0.0-beta.322 - -### Patch Changes - -- Updated dependencies [2231ef8] - - effect-app@4.0.0-beta.322 - - @effect-app/infra@4.0.0-beta.322 - - @effect-app/vue@4.0.0-beta.322 - -## 4.0.0-beta.321 - -### Patch Changes - -- Updated dependencies [18bae5b] - - effect-app@4.0.0-beta.321 - - @effect-app/infra@4.0.0-beta.321 - - @effect-app/vue@4.0.0-beta.321 - -## 4.0.0-beta.320 - -### Patch Changes - -- effect-app@4.0.0-beta.320 -- @effect-app/infra@4.0.0-beta.320 -- @effect-app/vue@4.0.0-beta.320 - -## 4.0.0-beta.319 - -### Patch Changes - -- effect-app@4.0.0-beta.319 -- @effect-app/infra@4.0.0-beta.319 -- @effect-app/vue@4.0.0-beta.319 - -## 4.0.0-beta.318 - -### Patch Changes - -- Updated dependencies [8bd5bfe] - - effect-app@4.0.0-beta.318 - - @effect-app/infra@4.0.0-beta.318 - - @effect-app/vue@4.0.0-beta.318 - -## 4.0.0-beta.317 - -### Patch Changes - -- Updated dependencies [186de3a] -- Updated dependencies [186de3a] -- Updated dependencies [186de3a] -- Updated dependencies [186de3a] - - @effect-app/infra@4.0.0-beta.317 - - effect-app@4.0.0-beta.317 - - @effect-app/vue@4.0.0-beta.317 - -## 4.0.0-beta.316 - -### Patch Changes - -- effect-app@4.0.0-beta.316 -- @effect-app/infra@4.0.0-beta.316 -- @effect-app/vue@4.0.0-beta.316 - -## 4.0.0-beta.315 - -### Patch Changes - -- effect-app@4.0.0-beta.315 -- @effect-app/infra@4.0.0-beta.315 -- @effect-app/vue@4.0.0-beta.315 - -## 4.0.0-beta.314 - -### Patch Changes - -- Updated dependencies [61a3931] - - @effect-app/vue@4.0.0-beta.314 - - effect-app@4.0.0-beta.314 - - @effect-app/infra@4.0.0-beta.314 - -## 4.0.0-beta.313 - -### Patch Changes - -- Updated dependencies [eddda2e] - - @effect-app/vue@4.0.0-beta.313 - - effect-app@4.0.0-beta.313 - - @effect-app/infra@4.0.0-beta.313 - -## 4.0.0-beta.312 - -### Patch Changes - -- Updated dependencies [9ea024d] - - effect-app@4.0.0-beta.312 - - @effect-app/infra@4.0.0-beta.312 - - @effect-app/vue@4.0.0-beta.312 - -## 4.0.0-beta.311 - -### Patch Changes - -- Updated dependencies [547e2e1] - - effect-app@4.0.0-beta.311 - - @effect-app/infra@4.0.0-beta.311 - - @effect-app/vue@4.0.0-beta.311 - -## 4.0.0-beta.310 - -### Patch Changes - -- Updated dependencies [0fa0e80] - - @effect-app/vue@4.0.0-beta.310 - - effect-app@4.0.0-beta.310 - - @effect-app/infra@4.0.0-beta.310 - -## 4.0.0-beta.309 - -### Patch Changes - -- Updated dependencies [99c43c4] -- Updated dependencies [99c43c4] - - effect-app@4.0.0-beta.309 - - @effect-app/vue@4.0.0-beta.309 - - @effect-app/infra@4.0.0-beta.309 - -## 4.0.0-beta.308 - -### Patch Changes - -- Updated dependencies [b4d5f55] - - @effect-app/infra@4.0.0-beta.308 - - effect-app@4.0.0-beta.308 - - @effect-app/vue@4.0.0-beta.308 - -## 4.0.0-beta.307 - -### Patch Changes - -- Updated dependencies [40585ca] -- Updated dependencies [ad0ede6] - - effect-app@4.0.0-beta.307 - - @effect-app/infra@4.0.0-beta.307 - - @effect-app/vue@4.0.0-beta.307 - -## 4.0.0-beta.306 - -### Patch Changes - -- Updated dependencies [0fe925d] - - effect-app@4.0.0-beta.306 - - @effect-app/infra@4.0.0-beta.306 - - @effect-app/vue@4.0.0-beta.306 - -## 4.0.0-beta.305 - -### Patch Changes - -- Updated dependencies [8bd9a11] - - effect-app@4.0.0-beta.305 - - @effect-app/infra@4.0.0-beta.305 - - @effect-app/vue@4.0.0-beta.305 - -## 4.0.0-beta.304 - -### Patch Changes - -- Updated dependencies [412f08b] - - effect-app@4.0.0-beta.304 - - @effect-app/infra@4.0.0-beta.304 - - @effect-app/vue@4.0.0-beta.304 - -## 4.0.0-beta.303 - -### Patch Changes - -- Updated dependencies [33b0544] - - effect-app@4.0.0-beta.303 - - @effect-app/infra@4.0.0-beta.303 - - @effect-app/vue@4.0.0-beta.303 - -## 4.0.0-beta.302 - -### Patch Changes - -- Updated dependencies [c0e5a1b] - - effect-app@4.0.0-beta.302 - - @effect-app/infra@4.0.0-beta.302 - - @effect-app/vue@4.0.0-beta.302 - -## 4.0.0-beta.301 - -### Patch Changes - -- Updated dependencies [413022d] - - @effect-app/vue@4.0.0-beta.301 - - effect-app@4.0.0-beta.301 - - @effect-app/infra@4.0.0-beta.301 - -## 4.0.0-beta.300 - -### Patch Changes - -- effect-app@4.0.0-beta.300 -- @effect-app/infra@4.0.0-beta.300 -- @effect-app/vue@4.0.0-beta.300 - -## 4.0.0-beta.299 - -### Patch Changes - -- effect-app@4.0.0-beta.299 -- @effect-app/infra@4.0.0-beta.299 -- @effect-app/vue@4.0.0-beta.299 - -## 4.0.0-beta.298 - -### Patch Changes - -- Updated dependencies [08d092c] - - effect-app@4.0.0-beta.298 - - @effect-app/vue@4.0.0-beta.298 - - @effect-app/infra@4.0.0-beta.298 - -## 4.0.0-beta.297 - -### Patch Changes - -- Updated dependencies [30c512d] - - effect-app@4.0.0-beta.297 - - @effect-app/infra@4.0.0-beta.297 - - @effect-app/vue@4.0.0-beta.297 - -## 4.0.0-beta.296 - -### Patch Changes - -- Updated dependencies [e0c4835] - - effect-app@4.0.0-beta.296 - - @effect-app/infra@4.0.0-beta.296 - - @effect-app/vue@4.0.0-beta.296 - -## 4.0.0-beta.295 - -### Patch Changes - -- Updated dependencies [eb06b32] - - effect-app@4.0.0-beta.295 - - @effect-app/infra@4.0.0-beta.295 - - @effect-app/vue@4.0.0-beta.295 - -## 4.0.0-beta.294 - -### Patch Changes - -- Updated dependencies [a211c12] -- Updated dependencies [7519318] - - effect-app@4.0.0-beta.294 - - @effect-app/vue@4.0.0-beta.294 - - @effect-app/infra@4.0.0-beta.294 - -## 4.0.0-beta.293 - -### Patch Changes - -- Updated dependencies [d1c15d3] - - effect-app@4.0.0-beta.293 - - @effect-app/infra@4.0.0-beta.293 - - @effect-app/vue@4.0.0-beta.293 - -## 4.0.0-beta.292 - -### Patch Changes - -- Updated dependencies [4622a75] - - @effect-app/vue@4.0.0-beta.292 - - effect-app@4.0.0-beta.292 - - @effect-app/infra@4.0.0-beta.292 - -## 4.0.0-beta.291 - -### Patch Changes - -- Updated dependencies [848d26d] -- Updated dependencies [fa3533e] - - @effect-app/infra@4.0.0-beta.291 - - @effect-app/vue@4.0.0-beta.291 - - effect-app@4.0.0-beta.291 - -## 4.0.0-beta.290 - -### Patch Changes - -- Updated dependencies [dc07df5] - - effect-app@4.0.0-beta.290 - - @effect-app/infra@4.0.0-beta.290 - - @effect-app/vue@4.0.0-beta.290 - -## 4.0.0-beta.289 - -### Patch Changes - -- Updated dependencies [a1b59bc] - - effect-app@4.0.0-beta.289 - - @effect-app/infra@4.0.0-beta.289 - - @effect-app/vue@4.0.0-beta.289 - -## 4.0.0-beta.288 - -### Patch Changes - -- Updated dependencies [aeb17bc] -- Updated dependencies [aeb17bc] - - effect-app@4.0.0-beta.288 - - @effect-app/vue@4.0.0-beta.288 - - @effect-app/infra@4.0.0-beta.288 - -## 4.0.0-beta.287 - -### Patch Changes - -- Updated dependencies [261470f] - - effect-app@4.0.0-beta.287 - - @effect-app/infra@4.0.0-beta.287 - - @effect-app/vue@4.0.0-beta.287 - -## 4.0.0-beta.286 - -### Patch Changes - -- Updated dependencies [b6dda09] - - effect-app@4.0.0-beta.286 - - @effect-app/infra@4.0.0-beta.286 - - @effect-app/vue@4.0.0-beta.286 - -## 4.0.0-beta.285 - -### Patch Changes - -- Updated dependencies [7bd8234] - - effect-app@4.0.0-beta.285 - - @effect-app/infra@4.0.0-beta.285 - - @effect-app/vue@4.0.0-beta.285 - -## 4.0.0-beta.284 - -### Patch Changes - -- Updated dependencies [dd239fa] - - @effect-app/infra@4.0.0-beta.284 - - @effect-app/vue@4.0.0-beta.284 - - effect-app@4.0.0-beta.284 - -## 4.0.0-beta.283 - -### Patch Changes - -- Updated dependencies [0054611] -- Updated dependencies [0054611] - - effect-app@4.0.0-beta.283 - - @effect-app/infra@4.0.0-beta.283 - - @effect-app/vue@4.0.0-beta.283 diff --git a/packages/e2e/package.json b/packages/e2e/package.json deleted file mode 100644 index 58f86bae4..000000000 --- a/packages/e2e/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@effect-app/e2e", - "version": "4.0.1", - "license": "MIT", - "type": "module", - "private": true, - "dependencies": { - "@effect-app/infra": "workspace:*", - "@effect-app/vue": "workspace:*", - "effect-app": "workspace:*" - }, - "devDependencies": { - "@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.2", - "typescript": "~6.0.3", - "vitest": "^4.1.7", - "vue": "^3.5.35" - }, - "scripts": { - "check": "tsgo --noEmit", - "test": "vitest", - "test:run": "vitest run --passWithNoTests", - "lint": "oxlint --quiet --type-aware ./test && pnpm exec dprint check --config ../../dprint.jsonc .", - "lint-fix": "oxlint --quiet --type-aware --fix ./test && pnpm exec dprint fmt --config ../../dprint.jsonc .", - "testsuite": "pnpm lint && pnpm run test:run", - "clean": "rm -rf dist" - }, - "sideEffects": false -} diff --git a/packages/e2e/test/repoInvalidation.e2e.test.ts b/packages/e2e/test/repoInvalidation.e2e.test.ts deleted file mode 100644 index 5ca7ef1bd..000000000 --- a/packages/e2e/test/repoInvalidation.e2e.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { awaitAtomResult, buildQueryFamily, invalidateAndAwait, makeAtomClientRuntime } from "@effect-app/vue/atomQuery" -import { invalidateQueries } from "@effect-app/vue/mutate" -import { defaultRegistry } from "@effect/atom-vue" -import { NodeHttpServer } from "@effect/platform-node" -import { expect, it } from "@effect/vitest" -import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query" -import { ApiClientFactory, InvalidStateError, makeRpcClient, NotLoggedInError, OptimisticConcurrencyException } from "effect-app/client" -import * as Context from "effect-app/Context" -import * as Effect from "effect-app/Effect" -import { HttpRouter, HttpServer } from "effect-app/http" -import * as Layer from "effect-app/Layer" -import { DefaultGenericMiddlewares } from "effect-app/middleware" -import { makeRepo, RepositoryRegistryLive } from "effect-app/Model" -import { MiddlewareMaker, RpcContextMap, RpcMiddleware } from "effect-app/rpc" -import * as S from "effect-app/Schema" -import { FetchHttpClient } from "effect/http" -import * as ManagedRuntime from "effect/ManagedRuntime" -import * as NetAddress from "effect/net/NetAddress" -import * as Option from "effect/Option" -import * as Reactivity from "effect/reactivity/Reactivity" -import { RpcSerialization } from "effect/rpc" -import * as Scope from "effect/Scope" -import * as Tuple from "effect/Tuple" -import { createServer } from "http" -import { createApp, effectScope, ref } from "vue" -import { RequestContextMiddleware } from "../../infra/src/internal/RequestContextMiddleware.ts" -import { makeRouter } from "../../infra/src/routing.ts" -import { DefaultGenericMiddlewaresLive } from "../../infra/src/routing/middleware.ts" -import { MemoryStoreLive } from "../../infra/src/Store/Memory.ts" -import { makeTanstackQuery, makeTanstackQueryInvalidator } from "../../vue/src/internal/tanstackQuery.ts" - -class RequestContextMap extends RpcContextMap.makeMap({ - allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError) -}) {} - -class AllowAnonymous extends RpcMiddleware.Tag()("AllowAnonymous", { - dynamic: RequestContextMap.get("allowAnonymous") -}) {} - -const AllowAnonymousLive = Layer.effect( - AllowAnonymous, - Effect.succeed(Effect.fnUntraced(function*(effect, { rpc }) { - yield* Scope.Scope - if (!RequestContextMap.getConfig(rpc).allowAnonymous) { - return yield* new NotLoggedInError({ message: "Not logged in" }) - } - return yield* effect - })) -) - -class AppMiddleware extends MiddlewareMaker - .Tag()("AppMiddleware", RequestContextMap) - .middleware(AllowAnonymous) - .middleware(...DefaultGenericMiddlewares) -{ - static Default = this.layer.pipe( - Layer.provide([AllowAnonymousLive, DefaultGenericMiddlewaresLive] as const) - ) -} - -const { Router, matchAll } = makeRouter(AppMiddleware.Default) -const { TaggedRequestFor } = makeRpcClient(AppMiddleware) -const Req = TaggedRequestFor("RepoInvalidation") - -class RepoItem extends S.Class("RepoItem")({ id: S.String, label: S.String }) {} -class OtherItem extends S.Class("OtherItem")({ id: S.String, label: S.String }) {} - -class RepoItems extends Context.Service()("RepoItems", { - make: makeRepo("RepoItem", RepoItem, {}) -}) { - static Default = Layer.effect(this, this.make).pipe( - Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive)) - ) -} - -class OtherItems extends Context.Service()("OtherItems", { - make: makeRepo("OtherItem", OtherItem, {}) -}) { - static Default = Layer.effect(this, this.make).pipe( - Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive)) - ) -} - -class GetRepoCount extends Req.Query()("GetRepoCount", {}, { - allowAnonymous: true, - success: S.Number -}) {} - -class GetRepoCountRuns extends Req.Query()("GetRepoCountRuns", {}, { - allowAnonymous: true, - success: S.Number -}) {} - -class SaveRepoItem extends Req.Command()("SaveRepoItem", { - id: S.String, - label: S.String -}, { - allowAnonymous: true, - error: S.Union([InvalidStateError, OptimisticConcurrencyException]), - success: S.Void -}) {} - -class SaveOtherItem extends Req.Command()("SaveOtherItem", { - id: S.String, - label: S.String -}, { - allowAnonymous: true, - success: S.Void -}) {} - -const RepoRpcs = { GetRepoCount, GetRepoCountRuns, SaveRepoItem, SaveOtherItem } -let repoCountRuns = 0 - -const router = Router(RepoRpcs)({ - dependencies: [RepoItems.Default, OtherItems.Default], - *effect(match) { - const repo = yield* RepoItems - const otherRepo = yield* OtherItems - return match({ - GetRepoCount: () => - Effect - .sync(() => repoCountRuns++) - .pipe(Effect.andThen(repo.all), Effect.map((items) => items.length), Effect.orDie), - GetRepoCountRuns: () => Effect.sync(() => repoCountRuns), - SaveRepoItem: ({ id, label }) => repo.save(new RepoItem({ id, label })).pipe(Effect.orDie), - SaveOtherItem: ({ id, label }) => otherRepo.save(new OtherItem({ id, label })).pipe(Effect.orDie) - }) - } -}) - -const RpcRouterLayer = matchAll({ router }) -const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 }) - -const ServerLayer = HttpRouter - .serve(RpcRouterLayer.pipe(Layer.provide(HttpRouter.middleware(RequestContextMiddleware()).layer))) - .pipe( - Layer.provide(NodeServerLayer), - Layer.provide(RpcSerialization.layerNdjson) - ) - -const ClientLayer = Layer - .unwrap( - Effect.gen(function*() { - const server = yield* HttpServer.HttpServer - const addr = server.address - if (NetAddress.isUnixPathAddress(addr)) { - return yield* Effect.die(new Error("expected inet address")) - } - const host = NetAddress.isUnspecified(addr.address) ? "127.0.0.1" : NetAddress.formatUrlHost(addr.address) - return ApiClientFactory - .layer({ url: `http://${host}:${addr.port}`, headers: Option.none() }) - .pipe(Layer.provide(FetchHttpClient.layer)) - }) - ) - .pipe(Layer.provide(NodeServerLayer)) - -const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer, Reactivity.layer) - -const setup = () => { - repoCountRuns = 0 - const runtime = ManagedRuntime.make(TestLayer) - return Effect - .all( - [ - ApiClientFactory.makeFor(Layer.empty)(RepoRpcs), - Effect.context() - ] as const - ) - .pipe(Effect.map(([client, context]) => Tuple.make(runtime, client, context)), runtime.runPromise) -} - -it("atom engine: rpc repo write invalidates and refetches; unrelated rpc write does not", async () => { - const [runtime, client, context] = await setup() - const reactivity = Context.get(context, Reactivity.Reactivity) - const rt = makeAtomClientRuntime(() => Layer.succeedContext(context), runtime.memoMap) - const invalidator = { - invalidateAndAwait: (keys: ReadonlyArray>) => - invalidateAndAwait(keys).pipe(Effect.provideService(Reactivity.Reactivity, reactivity)) - } - const atom = buildQueryFamily(rt, client.GetRepoCount as any)(undefined) - const unmount = defaultRegistry.mount(atom) - const run = (effect: Effect.Effect) => Effect.runPromise(effect) - const save = (id: string) => - run( - invalidateQueries(client.SaveRepoItem, undefined, invalidator)(client.SaveRepoItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - const saveOther = (id: string) => - run( - invalidateQueries(client.SaveOtherItem, undefined, invalidator)(client.SaveOtherItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - - try { - expect(await run(awaitAtomResult(defaultRegistry, atom) as any)).toBe(0) - - await save("1") - expect(await run(awaitAtomResult(defaultRegistry, atom) as any)).toBe(1) - - const before = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - await saveOther("x") - const after = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - expect(after).toBe(before) - } finally { - unmount() - await runtime.dispose() - } -}, 10_000) - -it("tanstack engine: rpc repo write invalidates and refetches; unrelated rpc write does not", async () => { - const [runtime, client, context] = await setup() - const queryClient = new QueryClient({ - defaultOptions: { queries: { retry: false, gcTime: Infinity, staleTime: 0 } } - }) - const useQuery = makeTanstackQuery(() => context, queryClient) - const invalidator = makeTanstackQueryInvalidator(queryClient) - const app = createApp({ render: () => null }) - app.use(VueQueryPlugin, { queryClient }) - const scope = effectScope(true) - let handle: any - let data: any - const run = (effect: Effect.Effect) => Effect.runPromise(effect) - const save = (id: string) => - run( - invalidateQueries(client.SaveRepoItem, undefined, invalidator)(client.SaveRepoItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - const saveOther = (id: string) => - run( - invalidateQueries(client.SaveOtherItem, undefined, invalidator)(client.SaveOtherItem.handler({ id, label: id }), { - id, - label: id - }) - .pipe(Effect.provide(context)) as any - ) - - app.runWithContext(() => - scope.run(() => { - const tuple = useQuery(client.GetRepoCount as any)(ref(undefined) as any, {} as any) as any - data = tuple[1] - handle = tuple[3] - }) - ) - - try { - expect(await run(handle.refetch())).toBe(0) - - await save("1") - expect(data.value).toBe(1) - - const before = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - await saveOther("x") - const after = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any) - expect(after).toBe(before) - } finally { - scope.stop() - queryClient.clear() - await runtime.dispose() - } -}, 10_000) diff --git a/packages/e2e/tsconfig.json b/packages/e2e/tsconfig.json deleted file mode 100644 index d51e90c5f..000000000 --- a/packages/e2e/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": [ - "esnext", - "DOM" - ], - "moduleResolution": "Node16", - "esModuleInterop": true, - "noEmit": true, - // Leaf test package: it imports workspace packages by source, so disable project-mode (which - // would demand every transitively-imported source file be listed) and emit nothing. - "composite": false, - "incremental": false, - "declarationMap": false, - "rootDir": "../..", - "types": [ - "node", - "vitest/globals" - ] - }, - "include": [ - "./test/**/*.ts" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/packages/e2e/vitest.config.ts b/packages/e2e/vitest.config.ts deleted file mode 100644 index 852ed8889..000000000 --- a/packages/e2e/vitest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// -import { defineConfig } from "vitest/config" -import makeConfig from "../../vite.config.base" - -export default defineConfig({ - ...makeConfig(__dirname), - test: { - environment: "node", - include: ["**/test/**/*.test.{ts,tsx}"], - exclude: ["node_modules/**", "dist/**"], - globals: true - } -}) diff --git a/packages/effect-app/.madgerc b/packages/effect-app/.madgerc deleted file mode 100644 index b407c6b4b..000000000 --- a/packages/effect-app/.madgerc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "detectiveOptions": { - "ts": { - "skipTypeImports": true - } - } -} diff --git a/packages/effect-app/.npmignore b/packages/effect-app/.npmignore deleted file mode 100644 index 9e4be5957..000000000 --- a/packages/effect-app/.npmignore +++ /dev/null @@ -1,14 +0,0 @@ -*.test.js -*.test.d.ts -tsconfig.build.json -*.log -tslint.json -.eslintrc.js -.eslintignore -.madgerc -.swcrc -.cache -coverage/ -.tsbuildinfo -.build/ -!dist/ \ No newline at end of file diff --git a/packages/effect-app/CHANGELOG.md b/packages/effect-app/CHANGELOG.md deleted file mode 100644 index 80bcb6947..000000000 --- a/packages/effect-app/CHANGELOG.md +++ /dev/null @@ -1,9766 +0,0 @@ -# @effect-app/prelude - -## 4.0.1 - -### Patch Changes - -- 222e619: Remove effect-app helpers that Effect already provides, and add `migrateEffectAppSource` to rewrite call sites. - - Deleted aliases and wrappers now live in Effect: `String.capitalize` / `uncapitalize`, `Struct.keys`, `Record.values`, `absurd`, `Tuple.make`, `Option.isSome`, `Option.liftPredicate` for non-empty arrays, `Array.chunksOf`, `Chunk.findFirst` / `findLast` / `partition` / `containsWith`, `Array.dedupeWith`, `Effect.annotateLogsScoped`, `Deferred.await`, `Effect.fromOption`, `Result.fromOption`, and `DateTime.add` / `subtract`. `Schema.DateValid` was an alias of `Schema.Date`. The date-fns calendar wrappers and the `date-fns` dependency are gone. - - `effect-app/migration/nativeEffect` exports the replacement table and `migrateEffectAppSource(source)`, which rewrites those imports in one file. `dropUndefined` and `fromBool` stay, along with `groupByT` (any key, entry list), `randomElement`, `findFirstMap`, and the schema constructors that add construction defaults. - - `effect-app/DateTime` adds `makeZonedLocal` and `pipeZonedLocal`. `makeZonedLocal` attaches the process timezone to a `Date` without moving the instant. `pipeZonedLocal` runs the functions passed to it and returns that instant with `DateTime.toDateUtc`. `DateAdd*` and `DateSub*` migrate to `pipeZonedLocal(date, DateTime.add({ days }))`, which keeps the local calendar. - - `Chunk.containsWith` calls the equivalence as `(needle, element)`. The old `elem` called it as `(element, needle)`, so only a symmetric equivalence is unchanged. `Record.values` expects one value type, so a mapped type whose value depends on its key should use `Object.values`. - -## 4.0.0 - -### Major Changes - -- 52b0b01: Fix Schema->Codec -- 52b0b01: Effect v4 beta - -### Minor Changes - -- b2df3fa: Align `Schema.Void` with TypeScript `void` return-value semantics (effect-smol PR #2475 / `b7d46ab`). - - `S.Void` now accepts **any present value** at runtime and discards it to `undefined`, while keeping the decoded/encoded type as `void` — matching a `void` return whose result callers never observe. This is implemented as an override of the AST node in a new `effect-app/SchemaAST` module (`export * from "effect/SchemaAST"` plus a `Void` subclass whose parser mirrors the PR's `fromAnyToConst(undefined)`), so there is a single canonical `Void` used everywhere, including RPC success schemas. - - - New `effect-app/SchemaAST` module; internal `SchemaAST` imports across the libs now route through it. - - Removed `ForceVoid` from `effect-app/client/makeClient` — use `S.Void` directly, which now carries this behaviour. - - `S.Void_` remains available as effect's original (`undefined`-only) Void. - -- ba789a2: Move core service contracts and runtime-agnostic modules into `effect-app`, keep `infra` and `vue` focused on adapters, and drop the temporary `infra` compatibility re-export paths in favor of the new canonical imports. - - `@effect-app/infra` no longer re-exports moved core modules such as `./Model`, `./Emailer/service`, `./QueueMaker/service`, `./Store/service`, `./adapters/*`, or `./api/*` entrypoints. - -- ab289d4: Add opt-in `autoscaleMaxThroughput` for Cosmos containers created on demand (`StorageConfig`, `ClusterCosmosConfig`, `WorkflowEngineCosmosConfig`). When set, a missing container is created with autoscale at that max RU/s, unless its database has shared throughput (then it keeps sharing the database pool). Existing containers are untouched; unset keeps the Cosmos default of manual 400 RU/s per container. -- aeb17bc: Add `disableQueryInvalidation` flag to Command config for background saves. - - Set `disableQueryInvalidation: true` in a `Req.Command` config (3rd argument) - to suppress all client-side query invalidation for that command — client - `invalidatesQueries` callbacks, server-returned `metadata.invalidateQueries`, - and repository-derived write-dependency matching are all skipped. Use for - background saves (e.g. debounced auto-save) whose writes should not trigger - query refetches. - - - `InvalidationConfig` gains `disableQueryInvalidation?: boolean`. - - `RequestHandlerWithInput` gains `disableQueryInvalidation?: boolean`, - propagated from `Request.config` by `ApiClientFactory.makeFor`. - - `invalidateCache` early-returns `Effect.void` when the flag is set, - applying to both regular and stream mutations. - -- f21190c: Add `copy` property to `Class` and `TaggedClass` for creating modified instances with updated fields. The `copy` method is cached per-class and supports both object and function-based updates, with pipeline support. -- f88ea34: Move `makeQueryKey` into `effect-app/client` and update Vue source and tests to import it from the shared client module. Vue still re-exports `makeQueryKey` from `src/lib` for compatibility. -- 2ebf8ae: `jitM` runs at the store boundary as JSON → JSON, before the `toCodecJson(toEncoded(schema))` decode; repositories decode the Encoded shape only. - - `jitM` used to be applied by the repository _after_ the store had already decoded the document, and was typed `(pm: Encoded) => Encoded` - a lie, since jitMs are written against the stored JSON and `Encoded` holds native `Date`/`Map`/`Set` values. - - The read pipeline is now: raw JSON document → merge `defaultValues` (unchanged: they only fill _absent_ keys, so an explicitly stored `null` reaches `jitM`) → `jitM` (JSON → JSON) → decode `Schema.toCodecJson(Schema.toEncoded(schema))` → Encoded. The repository then decodes Encoded → the domain type with `schema` and no longer applies `jitM` at all. Because `jitM` now runs before any schema decode sees the document, it can repair legacy shapes including explicit `null`s. - - - `StoreConfig.jitM?: (json: JsonRecord) => JsonRecord` is new, where `JsonRecord` (exported from `effect-app/Store`) is `{ readonly [key: string]: Schema.Json }`. It is not applied on the write/encode path, and never receives `_etag`. - - `RepositoryOptions.jitM` changes type from `(pm: Encoded) => Encoded` to `(json: JsonRecord) => JsonRecord` and is forwarded into the store config. - - **Migrating a jitM:** it now receives and must return JSON. A `Date` field arrives as an ISO string (return a string, not a `Date`), a `ReadonlySet` as an array, a `ReadonlyMap` as an array of `[key, value]` pairs. Field access is index-signature based (`json["x"]`) rather than typed property access. - - The JSON→Encoded decode stays strict: a document `jitM` does not repair fails at the store boundary instead of being read back half-decoded. - - `ValidationError.jitMResult` is deprecated: `validateSample` only ever sees the store's output, so it is identical to `rawData`. - -- 8cb3de4: Add command invalidation helpers that preserve query-only resource types and pass mutation input and `Exit` results into invalidation callbacks. Update Vue `clientFor` to merge request-level invalidation config with call-site invalidation and require matching invalidation resources. -- 186de3a: Stop forcing Date/Map/Set Encoded shapes to JSON. - - `Schema.Date` / `ReadonlySet` / `ReadonlyMap` now keep native Encoded types (`Date`, `Set`, `Map`). Use `DateFromString`, `ReadonlySetFromArray`, and `ReadonlyMapFromArray` when the Encoded form must be JSON. The query DSL accepts those native values, including array ops (`includes` / `in` / `includes-any`) on `Date[]` and `ReadonlySet` fields. Memory, Disk, SQL, and Cosmos convert Encoded Date/Map/Set through `Schema.toCodecJson` on write/read; query parameters and defaults lower the same way from the store schema. App types such as DateOnly stay native Encoded and JSON-lower via that schema — not a type registry. - -- 186de3a: Query maps as JSON arrays of `[key, value]` tuples. - - `where("meta", "hasKey" | "hasValue" | "hasKeyValue", ...)` (and `not*` / `*-any` / `*-all` variants) filter `ReadonlyMap` fields. Memory, Disk, SQLite, Postgres, and Cosmos compile those ops against the encoded tuple array. - -- d16845e: Remove `TaggedRequest` from `makeRpcClient`, now only `TaggedRequestFor` is returned. Remove all legacy `meta.moduleName` support — `id` and `moduleName` are now required on `Req` type. Remove `makeRpcGroup` (use `makeRpcGroupFromRequestsAndModuleName` instead). -- beae3a0: Remove `withDefaultConstructor` wrapper, use `S.withConstructorDefault` directly with `Effect.succeed`/`Effect.sync`. -- 1f103b2: Replace `proxify` with explicit service accessor helpers: `accessFn`, `accessEffectFn`, `accessCn`, `accessEffectCn`. -- 0054611: Derive query invalidation from repository read/write dependencies and propagate dependency metadata through RPC clients. -- 89d8b3a: Add Effect RPC `Stream` support to the wrapper. - - - New `Stream` request constructor on `TaggedRequestFor` parallel to `Query`/`Command`. Emits resources with `type: "stream"`. - - Server router (`@effect-app/infra` `routing.ts`) accepts stream resources whose handlers return a `Stream.Stream` (or a function from input to one). Forwards `stream: true` to `Rpc.make` so `RpcSchema.Stream` wrapping is applied. Streams bypass `applyRequestTypeInterruptibility` and the `Effect.withSpan` wrapping (the RPC server adds its own span). - - Client (`apiClientFactory.ts`) detects stream resources, forwards `stream: true` when constructing `RpcGroup`, and exposes the per-request `handler` as a `Stream.Stream` (via `Stream.unwrap` over the `ManagedRuntime` context) instead of an `Effect`. `Invalidation.CommandResponseWithMetaData` continues to apply only to commands. - - New `RequestStreamHandler` / `RequestStreamHandlerWithInput` shapes in `clientFor.ts`; `RequestHandlers` dispatches on `type: "stream"`. - -- 08d2e70: Add `concurrency: "unbounded"` parseOptions annotation to Schema constructors (Struct, Array, NonEmptyArray, Record, TaggedStruct, ReadonlySet, ReadonlyMap, Class, TaggedClass) so all encode/decode operations automatically run with unbounded concurrency. Also override `mapFields` on Struct and Class/TaggedClass to preserve the annotation. -- 5f9cd6a: Schema/Class facades: add `StructFacade` and carry `identifier` on the class facades. - - - Add `StructFacade` = `Omit, "Type" | "Encoded" | "~type.make.in" | "DecodingServices" | "EncodingServices"> & { pinned type-level members }`. A top-level `const X = S.Struct(...)` / `S.TaggedStruct(...)` model faceted by the `.d.ts`-emit compiler is retyped to it: still a real `S.Struct` (so `Workflow.AnyStructSchema`, the `Struct` reconstruction, `Union`, `.fields.x` keep working) while `Type`/`Encoded`/make/services resolve to the named namespace interfaces. - - Add `readonly identifier: string` to `OpaqueClassFacade` and `OpaqueErrorFacadeClass` (an `S.Class` static lost from the bare emitted interface, which the compiler otherwise had to hand-emit per model). `OpaqueFacade` is intentionally left untouched — `S.Opaque` and requests build on `S.Bottom`, not `S.Class`, so they have no `identifier`. - -- 6252808: Add `ReadonlySetFromArray` and `ReadonlyMapFromArray` schema transformations that decode from annotated arrays to Set/Map. Update `ReadonlySet` and `ReadonlyMap` to use these internally. -- 821468d: Add server-driven cache invalidation via RPC response headers. - - - `effect-app/rpc`: new `Invalidation` module with `InvalidationKey` / `InvalidationKeys` schemas, `Invalidates` annotation (for declaring static invalidation on Rpc definitions), `InvalidationSet` reference (request-scoped accumulator), and `makeInvalidationSet` helper. - - `effect-app/middleware`: new `InvalidationMiddleware` RPC middleware tag; included in `DefaultGenericMiddlewares`. - - `effect-app/client`: new `InvalidationKeys` module with `InvalidationKeysFromServer` reference and `makeInvalidationKeysService` helper; `apiClientFactory` now taps HTTP responses to read the `x-invalidate` header and forward keys to `InvalidationKeysFromServer`. - - `@effect-app/infra`: new `InvalidationMiddlewareLive` RPC middleware implementation that owns the full lifecycle — creates a request-scoped `InvalidationSet` (backed by a `Ref`), pre-populates it from the `Invalidates` annotation, provides it to the handler, and after the handler completes registers an HTTP pre-response handler (via `appendPreResponseHandlerUnsafe`) to write the accumulated keys as an `x-invalidate` response header. No separate HTTP middleware is needed. - - `@effect-app/vue`: `invalidateQueries` / `useMutation` now reads server-provided invalidation keys from `InvalidationKeysFromServer` after each mutation and applies them alongside the client-side invalidation. - -- 025de47: Fold the encoded-override support from `ExtendedClass` and `ExtendedTaggedClass` into `Class`, `TaggedClass`, `ErrorClass`, and `TaggedErrorClass`, and update model codegen to detect the new second-generic form. -- 3a8c710: Store document decode fails as a typed `SchemaError` instead of a defect; repositories keep their public error channels by dying at the boundary; `validateSample` reports documents that fail at the store boundary. -- 186de3a: JSON stores lower native Encoded values (Date, Map, Set, and app types such as DateOnly) through the store's document schema. - - `makeRepo` already passes that schema. Adapters encode documents, query parameters, and defaults with `Schema.toCodecJson(toEncoded(schema))` at the field path. No type registry. Schemaless stores still lower Date/Map/Set structurally. - -- 828d264: Stream requests now support an optional `final` schema that models the final success type of the stream. When declared, `mutateStream`'s execute effect resolves with the last emitted value typed as `Final` instead of `void`. - - ```ts - class MyStream extends SomethingStream()( - "MyStream", - { id: S.String }, - { - success: S.Union([OperationProgress, ExportComplete]), - final: ExportComplete, // execute now resolves with ExportComplete - } - ) {} - ``` - -- 7fa3045: V1/V2/V3: stream and command requests carry invalidation metadata - - **V1** – stream final response includes metadata - - - `Invalidation.StreamResponseChunk` wraps each stream item as `{ _tag: "value", value }` and appends `{ _tag: "done", metadata }` at the end carrying all accumulated invalidation keys. - - **V2** – invalidation keys included in failures - - - `Invalidation.CommandFailureWithMetaData` and `Invalidation.StreamFailureChunk` carry keys accumulated up to the point of failure, so clients can invalidate queries even when a command or stream errors. - - `InvalidationMiddlewareLive` wraps command failures; `routing.ts` wraps stream failures. - - `apiClientFactory.ts` unwraps both on the client side, forwarding keys before re-failing with the original error. - - **V3** – mid-stream metadata chunks - - - `Invalidation.StreamResponseChunk` now also includes `{ _tag: "metadata", metadata }` for mid-stream invalidation. - - After each emitted value, the server drains accumulated keys and emits a "metadata" chunk if any keys were collected since the last drain (bucket reset via `InvalidationSet.drain`). - - `apiClientFactory.ts` processes "metadata" chunks the same as "done" chunks, forwarding keys to `InvalidationKeysFromServer` immediately. - - `makeInvalidationKeysService` accepts an optional `onAdded` callback that fires after each key addition, enabling `mutate.ts` to trigger query invalidation mid-stream without waiting for the stream to complete. - -- 3dc0d2a: Add streaming as a `stream: true` config option on `Query` / `Command` instead of a separate request type. - - `TaggedRequestFor` now exposes only `Query` and `Command` factories — the standalone `Stream` factory is removed. To produce a Stream of `success` values, pass `stream: true` in the request config. The request `type` field stays `"command" | "query"`; a new `stream: boolean` field carries the streaming flag (stripped from the stored handler config). - - ```ts - // Query that streams results - Req.Query()("Tag", {}, { stream: true, success: ... }) - - // Command that streams results - Req.Command()("Tag", {}, { stream: true, success: ... }) - ``` - - Vue client mapping (per-handler properties mirror the non-stream API — `.query`, `.fn`, `.mutate`): - - - `query` + `stream: true` → exposes `.query` (read-only streaming, tracked Vue Query). Helper map key: `${name}Query`. - - `command` + `stream: true` → exposes `.fn` and `.mutate` (mutating streaming). - - Plain `query` / `command` unchanged. - - Server routing dispatches via the new `stream` flag (`makeStreamRpc` for streaming commands/queries, `makeCommandRpc` / `Rpc.make` otherwise). - - Also lifts the `Struct` / `TaggedStruct` and `Opaque` definitions in `effect-app/Schema` to use `S.Bottom` / `S.Opaque` directly, exposing `fields`, `mapFields`, and a `MakeIn` that allows `void` when all fields are optional. `TaggedRequestFor` request classes now use `Opaque(TaggedStruct(...))` instead of `TaggedClass`, and decoding/encoding services are derived from `success` / `error` rather than stored on the request. - - **Migration**: replace `Req.Stream` with `Req.Query` or `Req.Command` and add `stream: true` to the config — `Query` for read-only streams, `Command` for mutating streams. - -- 738b482: Add pick/omit to Struct and TaggedStruct -- 3613e87: Add `TaggedRequestFor` helper to `makeRpcClient` that curries a `moduleName`, producing request classes with static `id` and `moduleName` properties. This enables passing request classes directly to `makeQueryKey` without going through `clientFor` first. The `clientFor` function no longer requires a `meta` property on the module when requests carry `moduleName`. The meta codegen preset now generates `Req = TaggedRequestFor(moduleName)`. Original `TaggedRequest` remains for backwards compatibility. -- 7119320: Add `generateGuards` and `generateGuardsFor` to `TaggedUnion` / `ExtendTaggedUnion` for property-scoped type guards. - - - `generateGuards("key")` — generic per-guard, no need to specify the container type - - `generateGuardsFor()("key")` — curried, fixes `A` for concrete guard signatures - - Both return `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. - -- e944bca: Add `generateGuards` and `generateGuardsFor` to `TaggedUnion` / `ExtendTaggedUnion` for property-scoped type guards. - - - `generateGuards("key")` — generic per-guard, no need to specify the container type - - `generateGuardsFor()("key")` — curried, fixes `A` for concrete guard signatures - - Both return `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. - -- 8bd9a11: Support ID-scoped signal dependencies and additive repository dependencies derived from previous and current entities. -- 23a7167: Add a typed, serializable `DatabaseError { message, transient, cause }` for store adapter failures. - - Store adapters wrapped DB calls in bare `Effect.promise` / `.orDie`, so a transient infra failure (request timeout, throttle, 5xx, dropped socket) became a raw-`Error` defect — which `Effect.retry` can't retry and which breaks JSON encoding of a workflow exit cause ("Expected JSON value, got Error"). `DatabaseError` is now exposed on all `Store` and `Repository` method error channels (writes: `OptimisticConcurrencyException | DatabaseError`) and added to `SupportedErrors` so the api/client/FE treat it as a 500-class error. Each adapter wraps its db-call failures into `DatabaseError` with a `transient` flag (timeout/throttle/5xx ⇒ retryable); the `cause` serializes via `Schema.Defect`. Construction/seed/DDL paths stay `orDie`. - -- e71eb78: Tighten typing of query DSL builders so field/path parameters are constrained to actual field paths instead of bare `string`: - - - `relation(path)` now infers the relation's element type and constrains `distinctCount`/`sum`/`collect`/`collectDistinct`/`collectFields`/`collectDistinctFields` and the `unit` of `sumExprBy`/`sumExprNormalized` to `FieldPath`. - - `relation(path).expr` exposes a scope-bound math-expression builder so `expr.field(...)` inside `sumExpr`/`sumExprBy`/`sumExprNormalized` is typed against the relation element. - - Top-level `expr.field` accepts an optional generic for opt-in tightening (`expr.field("x")`). - - `aggregate(schema, build)` accepts a builder callback whose `agg` argument is bound to the source row inferred from the pipe — paths are checked without any explicit generic: `make().pipe(aggregate(schema, ($) => ({ city: $.field("address.city") })))`. Plain `AggregateMap` form still accepted. - - `projectComputed(schema, build, mode?)` accepts a builder callback whose `{ relation }` argument is bound to the source row inferred from the pipe: `make().pipe(projectComputed(out, ({ relation }) => ({ total: relation("items").sum("qty") })))`. Plain `ComputedProjectionMap` form still accepted. - - `agg()` factory remains as an escape hatch when the builder is built outside a pipe. - -### Patch Changes - -- 439cbeb: Adopt module system from effect-smol: replace barrel imports with specific submodule imports (`import * as X from "effect-app/X"` / `import * as X from "effect/X"`). -- da83c1f: Align `InvalidationEntry` (vue) with `InvalidateQueryInstruction` (effect-app). - - `InvalidateQueryInstruction` is now parametrized over `Filters` / `Options` - (defaulting to `Record` so the core stays framework-agnostic). - `@effect-app/vue` exposes `InvalidationEntry` as a narrowed alias substituting - `@tanstack/vue-query`'s `InvalidateQueryFilters` and `InvalidateOptions`. Single - source of truth for the union shape across both packages. - -- 199e9a5: Allow Struct and TaggedStruct make helpers to omit input when every constructor field is optional or defaulted, and preserve widening copy typings through a lighter named public type to improve TypeScript editor responsiveness. -- b035b1c: Isolate per-call `Effect.provide(layers)` / `Stream.provide(layers)` in the - RPC api client factory via `{ local: true }`. - - `layers` here is built per RPC invocation from a caller-supplied - `requestLevelLayers` plus a fresh `RequestName` layer. Today both pieces are - stateless, so no observable bug — but `Effect.provide(layer)` without - `local: true` resolves its `MemoMap` from the ambient fiber context, and on a - long-lived runtime (browser app, server fiber) any stateful layer slipped into - `requestLevelLayers` by a caller would be memoized and shared across every - subsequent RPC call. `{ local: true }` builds the layer fresh per call and - skips the ambient MemoMap entirely. - - Also documents the rule in `AGENTS.md` ("Per-request `Effect.provide(layer)` - must isolate its MemoMap") so the pattern is enforceable in code review and - caught early. Companion to the `provideOnRequestScope` MemoMap fix in - `@effect-app/infra`. - -- 664e83d: Add Atom-native Vue query APIs, stream query pull atoms, and a TanStack-backed legacy query engine toggle. -- a4dff57: Adjust Struct and TaggedStruct copy typing to follow class-style widening constraints while keeping structural copy runtime behavior. -- ba4bdc3: improve: support .ts extension -- 52b0b01: remove hopefully obsolete Struct overrides -- 9d3495e: Preserve field-level schema decode errors for relaxed Class and TaggedClass declarations so decode failures report nested constraints (for example min-length violations) instead of only a generic class-type mismatch. -- 3436d44: Extend `Schema.Opaque` in `effect-app/Schema` to support an optional encoded-type generic while preserving the original single-generic behavior. -- 52b0b01: Update `Effect.allLower` to call `svc.asEffect()` when available, ensuring service entries are normalized before `Effect.all` evaluation. -- f317c5e: Preserve omitted-input make helpers through Schema.Opaque when the wrapped schema allows optional or default-only constructor input. -- e585c9c: fix config nested -- e4ff9a6: Fix the global `Array`/`ReadonlyArray.map` override breaking union-of-array receivers. The previous `this: NonEmptyArray`/`NonEmptyReadonlyArray` overload was selected-then-rejected on a union receiver (e.g. `(readonly A[] | readonly B[]).map(...)`), raising TS2684. Replaced with a single conditional-return signature (`this extends NonEmpty… ? NonEmpty : U[]`) that preserves NonEmpty refinement without a `this` parameter, so union-array `.map` calls type-check again. -- 33b0544: Replace repository codec child spans with parent-span timing attributes and bounded metrics, and record database operation timing for instrumented stores. -- 99c43c4: Derive scoped dependency reads and writes from typed query filters and annotated model relationships, including previous aliases. -- 52b0b01: Fix `Schema.TaggedUnion(...).tags` extraction for class-based members (for example `TaggedClass`) by using a local AST sentinel walker instead of relying on internal effect APIs. - - Add tests covering: - - - `TaggedUnion` with `encodeKeys`-wrapped members - - `TaggedUnion` with `TaggedClass` members - -- 08d092c: Update Atom query caches in memory and accumulate stream invalidations so live events do not cause repeated RPC refetches. -- 947fe20: fix input date -- 21ac90a: Unify generated model facades on `OpaqueFacade` and support class facades through encoded-key schema transforms. -- 32f05c8: Remove unused dependency declarations from package manifests. -- 6cfd83d: update effect to latest beta -- 1c858d3: fix request scope problems -- 52b0b01: fix bs -- 992d9fa: fix more branded types -- 505bfa9: Add concurrent decode helper APIs and migrate decode callsites to use them. - - - Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. - - Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. - - Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. - - Keep schema constructors free of hardcoded parse concurrency overrides. - -- 939bebc: Remove the local `Config.nested` / `ConfigProvider.nested` overrides. They worked around an effect v3 bug where the nested namespace segment bypassed the provider's `mapInput`/`constantCase` transform. effect `4.0.0-beta.84` fixes this upstream (its transformation-compose refactor threads the nested prefix through the provider's lookup transform), and the old overrides relied on internals (`Config.make`, `provider.get`/`mapInput`/`prefix`) that no longer exist. Both modules now re-export effect directly; behaviour for consumers (e.g. `Config.nested("cups")` against a `constantCase` env provider) is unchanged and correct. -- 52b0b01: Beta25 -- e0c4835: revert bs -- 32dbc54: fix stream type when no success specified -- 0a0030f: fix missing overloads -- 0263827: Fix `Date` / `DateValid` default helpers to pipe from their own schema. - - `withConstructorDefault` and `withDecodingDefaultType` on `DateValid` previously piped from `DateFromString`, dropping the `isDateValid()` check (and, after the recent identifier split, the `DateValid` annotations) from the resulting defaulted schema. Both `Date` and `DateValid` now use `extendM` so the helpers attach to the underlying schema (`DateFromString` / `DateValidFromString`). - -- 0263827: Distinguish `Date` and `DateValid` in JSON Schema output. - - - `Date` now emits identifier `DateOrInvalid` with description noting the value may be invalid. - - `DateValid` now emits its own annotated string (identifier `Date`) with description stating a valid ISO 8601 date is required. - -- c0e5a1b: Add `Schema.dropConstructorDefault`, a `Struct.Lambda` that clears a field's `withConstructorDefault`. Compose it with `Struct.omit`/`Struct.map` when deriving a partial-update schema from a "create" schema, so omitted fields' constructor defaults don't resurrect themselves in `.make(...)` output. - - Also, `makeExactOptional` now drops constructor defaults automatically, matching this behavior. - -- 10b55ff: update packages -- 14aba14: fix: clientFor -- a0075b8: `Email` and `PhoneNumber` carry native `arbitraryConstraint` patterns, so `effect/unstable/arbitrary` (and `generateFromSchema`) can sample them instead of exhausting rejection sampling on their `refine` guards. -- f052d38: Replace `(...) => Effect.gen` with `Effect.fnUntraced` in `apiClientFactory`. -- f233f3d: `PositiveNumber` and `NonNegativeNumber` bound native Arbitrary generation to at most 1,000,000 (`arbitraryConstraint` on their existing checks), so generated fixtures no longer overflow derived totals to `Infinity`. Validation and JSON Schema output are unchanged. -- 18bae5b: Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - - Breaking API updates from this RC: - - - PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) - - `Config.Record` now returns a Config - - Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` - - `SchemaTransformation.transformOrFail` renamed to `transformEffect` - - `Fiber.currentSpan` moved to `fiber.cache.span` - - HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` - - Object JSON Schema now emits `additionalProperties: true` - - Schema `Union` AST stores matching as `options.mode` instead of `mode` - -- f313973: fix type make interface -- edc52e4: update -- ea1bd46: fix: prevent expansion of nominal brands -- 85a8275: Expose `make`, `makeOption`, and `makeEffect` static helpers on request classes created via `Query`/`Command`. -- 50b022e: Harden model facades and add `OpaqueErrorFacadeClass`. - - - `OpaqueErrorFacadeClass`: facade `TaggedErrorClass`/`ErrorClass` while keeping - `Cause.YieldableError` on the constructed instance (so `yield* new Err()`, - `Effect.fail`, and `instanceof` keep working through the facade). - - `OpaqueFacadeInput` relaxed to require only the codec service channels, so - transformed schemas (`.pipe(encodeKeys/annotate/filter/...)`) can be facaded; - `fields`/`copy`/`mapFields` flow through `OpaqueFacadeStatics` when present, and - `to` is carried so models that compose via `X.to.fields` keep working. - - Dropped the wide `fields`/`mapFields` overrides on the facade interfaces so the - precise statics win (keeps `Q.project(X.mapFields(...))` typed). - - Codegen (`eslint-codegen-model`): resolver prefers the private `_X` over the - self-referential facade; converges static service types in one run; per-model - classification (facade Opaque models, leave `Class` standard in mixed files); - `Make` emitted as `type X = {...} | void` when the make-input is voidable; - `readonly`-prefixed array/tuple elements parenthesized; value self-references in - the moved `_X` body rewritten to `_X`; instance getters surface on `Self`. - -- 52b0b01: Migrate schema arbitrary annotations to Effect v4 `toArbitrary` and cover `StringId` generation with a fast-check test. -- 47e3742: Preserve Struct.copy through `annotate`, `annotateKey`, and `mapFields` chains, and add tests covering chained copy behavior on Struct schemas. -- 458bb1b: fix type -- 3365758: workaround effect error messages -- 54ec1ef: fix bs -- 0d4e0b8: Fix `isGeneratorFunction` using `isObject` instead of `isFunction`: generator functions have `typeof === "function"`, not `"object"`, so the check always returned `false`. This caused `Command.streamFn` generator-form handlers to silently pass a raw `Generator` object rather than an `Effect`, meaning the mutation was never executed. -- 31739d7: Fix JSON schema output for Email, Date, PhoneNumber, and Url schemas. The `jsonSchema` annotation key is not recognized by Effect v4's JSON schema generator — use proper v4 annotations (`format`, `description`) and built-in checks (`isMinLength`, `isMaxLength`) instead. -- 0b21a02: fix: flatten simple allOf constraints in JSON Schema output for better codegen compatibility -- bbaa67e: Fix `TS2589: Type instantiation is excessively deep and possibly infinite` when querying models that embed `Schema.Defect()` (or any field encoded as `Json`). - - The query path type `Path` (used by `Q.where`/`Q.and`/`Q.order` to type dotted field paths) recursed through object types with no termination for self-referential ones. Under effect `4.0.0-beta.83`, `Schema.Defect()` encodes as `Json`, whose `JsonObject = { readonly [x: string]: Json }` index signature made `Path` descend forever, blowing past TypeScript's instantiation limit on any model with such a field (e.g. carrier-error states carrying `raw: Schema.Defect()`). - - `Path` now threads a `Seen` set of already-entered object types and stops before re-entering one, so self-referential types terminate (the field itself stays a valid leaf path; only the unbounded descent is cut). Finite models are unaffected. - -- a211c12: Fix projection schema typing for union members and computed projections. -- 347af48: Fix schema interface widening regression introduced in 4.0.0-beta.255 (changeset `olive-onions-lose`). - - The original refactor annotated branded schema constants with interfaces extending `S.Codec & WithDefaults<...>`. That widened the underlying `BrandedSchema<...>` chain and dropped phantom slots (`Iso`, `~type.make.in`, `~type.parameters`, `Rebuild`, etc.) that downstream combinators read. In consumer projects this surfaced as `DecodingServices` / `EncodingServices` leaking as `unknown` — for example `Q.project(schema, "project")` failing with `Type 'unknown' is not assignable to type 'never'`, and RPC handler / generator-return shape mismatches against `Effect`. - - Each `*Schema` interface now extends the concrete underlying chain (e.g. `BrandedSchema`) and adds a call signature for `withDefaultMake` (and an explicit `withConstructorDefault` field for the numeric and `StringId` schemas). The runtime values are unchanged and the type-display improvement is preserved. - - Affected: `NonEmptyString*`, `Min3String255`, `StringId`, `Url`, `PositiveInt`, `NonNegativeInt`, `Int`, `PositiveNumber`, `NonNegativeNumber`, and `brandedStringId`. - -- bd26832: Add nested anyOf flattening to JSON Schema post-processing -- 3053760: fix: restore context map container behavior -- 52b0b01: fix TaggedUnion -- 55c6572: update packages -- 52b0b01: Tighten schema helper generics in `Schema/ext.ts` so default-related helpers accept properly named schema type parameters instead of bare `S.Top` inputs. -- c991be1: update packages -- d738811: remove double prefix -- 8fffc3c: cleanup -- 52b0b01: Migrate `Schema/ext` context helpers to Effect v4 by implementing `provide` and `contextFromServices` with proper service subtraction across decoding and encoding requirements. -- a788432: fix circular -- 10e90d5: fix: TaggedRequest should be an Opaque Schema Class -- 4b95009: use Finite instead of Number -- 52b0b01: Replace `ForceVoid` in `makeClient` with a typed codec transformation instead of an unsafe cast. -- 178480a: Fix request handler input classification to use request schema fields instead of `make` parameters, preventing defaulted/nullable input fields from being treated as no-input handlers. -- 985176b: Align request handler input typing with the request's `make` signature. Handlers are now classified as no-input only when the request schema declares no payload fields; any payload (even fully-optional) yields a function handler whose input matches `make`'s first parameter. Adds `HandlerInput` and threads it through `CommandFromRequest`. -- dc465e3: update to latest effect beta -- 21017d5: `InvalidationSet.add` accepts arrays and is exposed as a static shortcut. - - - Single item: `yield* InvalidationSet.add(UserRsc.GetMe)` - - Batch: `yield* InvalidationSet.add([UserRsc.GetMe, ["custom", "key"]])` - - Skips the `.use(_ => _.add(...))` boilerplate. - - Existing `InvalidationSet.use(_ => _.add(...))` form still works; the service - identity is preserved via `Object.assign` so `Effect.provideService` and - `.use` / `.useSync` continue to operate on the same `Context.Reference`. - -- 0541f0d: fix withDefault types -- 8c645d5: update to latest effect -- 30c512d: fix rpc middleware context type issue -- 50b022e: Add generated opaque model facades that expose static Type, Encoded, Make, and Schema declarations without leaking the private struct schema type to downstream project references. -- 52b0b01: adapt isObject change -- b952f19: bye cruft -- a37aa38: Update to effect beta 43 -- 52b0b01: again -- c1e73de: -- d867272: the return of `Context` -- 774a9b3: `MiddlewareMaker.makeMiddlewareBasic` now derives each middleware's effective error from both the static `error` field on the tag AND the `rcm` config entry referenced by `dynamic.key`, rather than relying on the static field alone. - - Middlewares declared with `dynamic: RequestContextMap.get("foo")` (instead of an explicit static `error: ...`) end up with `tag.error = Schema.Never` at runtime — `RpcMiddleware.Tag` defaults the static error to `Never` when not provided. The composite `MiddlewareMaker.Tag(...).middleware(...)` walked `make[*].error` to build its own error union, collapsing to `Union ≡ Never`. - - `Rpc.exitSchema` walks `rpc.middlewares[*].error` when building the wire-level failure union for every rpc kind. Empty-union meant middleware-thrown errors (`NotLoggedInError`, `UnauthorizedError`, etc.) never reached the wire schema. Query/command happened to work because their wire `errorSchema = resource.error` already covered the merge from `makeRpcClient`. Stream rpcs have `errorSchema` force-set to `Schema.Never` by effect-rpc, so the resource-level merge never reached the wire — middleware errors decoded as "Expected never, got X". - - Per middleware, the new logic pushes both the static `_.error` (if non-`Never`) and `rcm[_.dynamic.key].error` (if non-`Never`) into the composite's failure union. - -- d67d17a: Source middleware errors exclusively from the rpc middleware tag, and move command/stream invalidation wrap/unwrap entirely into the routing layer (server) and `apiClientFactory` (client). `InvalidationMiddleware` and `InvalidationMiddlewareLive` are removed. - - ### Resource error schemas - - Three sites that used to fold `RequestContextMap[*].error` into a request's own error schema now stop doing so: - - - `makeRpcClient` / `makeRequestClass` — `failureSchema` is just `config.error` (still merged with the optional `generalErrors` parameter, which is the only remaining error mix on both type and runtime levels). - - `MiddlewareMaker.rpc()` — `error: options.error` only; the previous union with `rcm.config[*].error` is gone. - - Routing and `apiClientFactory.makeRpcGroupFromRequestsAndModuleName` — `Invalidation.makeCommandRpc` is called with `error: resource.error` (no widening with the composite middleware error union). - - Middleware errors reach the client through the rpc's `middlewares[*].error` failure-union channel of `Rpc.exitSchema`, exposed by attaching the middleware tag to the rpc on both sides: - - - **Server**: `makeRouter(middleware)` attaches the live composite tag (existing behavior). - - **Client**: new `middleware` option on `ClientForOptions` / `ApiClientFactory.makeFor(layer, { middleware })` attaches the same tag schema-only (no Live invoked). Threaded through `makeRpcGroupFromRequestsAndModuleName` to `RpcGroup.middleware(tag)`. Without it, stream rpcs (whose top-level `errorSchema` is forced to `Never` by effect-rpc) hit `SchemaError: Expected never | { _tag: "error", ... }` decoding middleware-thrown errors that bypass the in-stream `Stream.catch` wrap. - - **Migration**: handlers that yield errors previously sourced from rcm (e.g. `yield* new UnauthorizedError()`) now require those errors to be declared explicitly on the resource — `Req.Query()("...", fields, { success, error: UnauthorizedError })`. The handler error type no longer auto-includes the rcm union. - - ### Invalidation wrap/unwrap - - - `routing.ts` (server) provides a per-request `InvalidationSet` for commands, wraps the success value as `CommandResponseWithMetaData`, and converts handler-thrown failures into `CommandFailureWithMetaData` so accumulated invalidation keys reach the client on either path. Stream wrap (per-chunk envelope + final `done` chunk) was already in routing and is unchanged. - - `apiClientFactory.ts` (client) `unwrapCommand` strips both envelopes and forwards keys to `InvalidationKeysFromServer`. - - `InvalidationMiddleware` (the tag) and `InvalidationMiddlewareLive` (the layer) are **removed**. The middleware was the previous home of the wrap; with the wrap moved to routing/apiClientFactory, the middleware became a thin pass-through and is no longer needed. `DefaultGenericMiddlewares` and `DefaultGenericMiddlewaresLive` shrink accordingly — no migration needed for callers that used the defaults; callers that referenced `InvalidationMiddleware` / `InvalidationMiddlewareLive` directly should drop those imports. - - Middleware-thrown errors are never wrapped: by definition the handler never ran, so there is nothing to invalidate. They flow raw on the Cause and the client decodes them via the middleware-tag failure-union channel described above. - -- 8f09f77: fix -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. -- d71d976: fix -- 2aa8e5e: `queryInvalidation` / `invalidatesQueries` accept shorthand entries (per-mutation, Command, client-level `QueryInvalidation` maps, and server-side `Req.Command` `invalidatesQueries` callbacks). - - Each entry returned may now be: - - - a raw query key (`string[]`) - - an RPC handler (`{ id, options? }`) — its query key is derived via `makeQueryKey` - - the existing `{ filters, options }` raw tanstack-query invalidation - - ```ts - queryInvalidation: (queryKey) => [queryKey, GetMe, PackListIndex]; - ``` - - equivalent to: - - ```ts - queryInvalidation: (queryKey) => [ - { filters: { queryKey } }, - { filters: { queryKey: makeQueryKey(GetMe) } }, - { filters: { queryKey: makeQueryKey(PackListIndex) } }, - ]; - ``` - -- 29a1e57: Repository `changeFeed` is now namespace-aware. Events carry the `storeId` namespace as a third tuple element (`ChangeFeedEvent = [items, op, namespace]`). `subscribe` accepts `options.namespace` to register a per-namespace handler; omitting it registers a wildcard handler that receives events from every namespace. Per-namespace handler buckets eliminate cross-namespace fan-out and avoid waking handlers for irrelevant tenants. -- 52b0b01: Align effect-app tagged union helpers with Effect v4 by delegating to the native tagged union utilities, exposing v4-style `cases` and `guards`, and preserving the local `tags` helper. -- 52b0b01: Update Effect dependencies to 4.0.0-beta.31 across workspace packages. -- 6fff09c: unify encoded function for when you use encodedKeys -- 11422f8: Update request helper typing and runtime invocation to rely on schema `.make` instead of class constructors, avoiding `new`-based assumptions for request schemas. -- eb28ea5: bogus -- 52b0b01: Change `Schema.Class` and `Schema.TaggedClass` wrappers to default constructor options to `{ disableValidation: true }`. - - This avoids strict class identifier validation by default when constructing wrapper classes (for example passing a compatible view class), while keeping existing behavior when explicit options are provided. - -- 8f1cf6a: able to configure schema concurrency -- ee9694e: fix type issue? -- 52a31dd: Reduce schema type complexity by exposing explicit interfaces for branded string and number schemas. - - Each branded constant in `Schema/strings.ts`, `Schema/moreStrings.ts`, and `Schema/numbers.ts` now has a named interface (e.g. `NonEmptyStringSchema`, `StringIdSchema`, `PositiveIntSchema`, `UrlSchema`) annotating its `export const`. Mirrors the pattern used in `effect/Schema` itself — TypeScript reports the interface name instead of expanding the full pipe/brand/extension chain, which shrinks inferred types in consumer code and speeds up type display. - - No runtime or API surface changes; `brandedStringId` now returns `BrandedStringIdSchema` (same shape as before, just named). - -- 52b0b01: fix withDefault -- ca94edf: fix typo -- d1c15d3: Tighten projectComputed projection schema typing against encoded repo fields and computed output types. -- a1b59bc: Allow query projections to use a repository's full union schema. -- dc07df5: Fix nested DTO subset projection inference for tagged unions. -- 0fe925d: Tag-aware `ProjectableFromDomain` for `projectComputed`: projection Encoded fields must exist on the matching domain tagged state (or be computed). Prevents Overview.List SchemaErrors when cancel states omit workflow lock fields like `activeRequest`. -- 40585ca: Keep tag-aware `ProjectableFromDomain` for `projectComputed` only; restore loose key-presence guard for `project()` so view DTOs with reshaped fields keep typechecking. -- 5ac46cb: Make `Schema.provide` dual (pipeable) -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- 52b0b01: Refactor `Pure` helpers to use typed `useSync` calls instead of unsafe casts. -- 48d9f36: Memory, SQLite, and Postgres `Store.queryRaw` now run over stored JSON documents, not Encoded rows. `Repository.queryRaw` already applies `toCodecJson` to the projector output, so native Encoded `Date` / `Map` / `Set` values no longer fail `Expected JSON value`. Cosmos was already JSON and is unchanged. - - Memory projectors that assumed native Encoded `Date` / `Map` / `Set` values now see the stored JSON form (ISO strings, arrays). - -- 52b0b01: Update Effect dependencies to 4.0.0-beta.28 across workspace packages. -- 52b0b01: fix: isA -- d31253f: Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. -- 5615e47: bs -- 3e46e7b: allow excess props in openapi schemas -- cec026d: update packages -- 88838fb: Remove pick/omit customizations from Class/TaggedClass/Struct/TaggedStruct. Use `Struct.pick(X.fields, [...])` from `effect-app` instead. -- 3bae238: Remove lodash type imports from utils.ts, replacing with native TypeScript equivalents. -- b2e438f: Remove Operations service and repo -- f150cf9: Remove obsolete queue machinery left unused after the move to cluster entities: the SB/SQLite/mem `QueueMaker` implementations (`QueueMaker/{SQLQueue,memQueue,sbqueue}.ts`), their `ServiceBus.ts`/`memQueue.ts` transports, and `RequestFiberSet.ts` (its `setRootParentSpan` helper is inlined into `MainFiberSet`). `effect-app/QueueMaker` keeps only `QueueMeta`; the `QueueBase` interface and empty `QueueMaker` ops object are dropped. -- 0c88f78: Remove `TaggedError` compatibility re-export, use `TaggedErrorClass` directly -- 261470f: Remove the local `Schema.Void` / `SchemaAST.Void` override. effect `4.0.0-beta.90` ships the TypeScript `void` parser semantics upstream (`SchemaAST.Void.getParser()` → `fromAnyToConst(undefined)`, the effect-smol PR #2475 behavior), so `S.Void` already accepts any present value and discards it to `undefined`. `Schema.Void` now re-uses effect's `S.Void` and `SchemaAST` is a plain re-export of `effect/SchemaAST`. The unused `Void_` alias is dropped. No behavior change. -- 8792221: Rename `withDefault` schema extensions to `withConstructorDefault` for clarity. - - Document that `.withConstructorDefault` is **construction-only** (applied during `.make(...)` when a field is omitted) and is **not** applied during decode — it cannot be used to JIT-migrate database fields. Per-property JSDoc on every `.withConstructorDefault` / `.withDecodingDefaultType` exposed by `Schema/ext.ts`, `Schema/numbers.ts`, `Schema/moreStrings.ts`, and `ids.ts` so the caveat is visible on hover. - - Re-export `withConstructorDefault`, `withDecodingDefault`, `withDecodingDefaultKey`, `withDecodingDefaultType`, and `withDecodingDefaultTypeKey` from `effect-app/Schema` with explicit JSDoc. `withDecodingDefault*` is discouraged for persisted data: a missing field may be data corruption rather than an old-shape document, and silently substituting a default hides the problem. Prefer an explicit, preferably versioned migration of database data over decode-time fallbacks. - -- 7bd8234: Use set-backed data dependencies and provide request-scoped dependency services through a shared root-checked helper. -- 54bfc59: Require middleware to flow through `makeRpcClient` and the live layer through `makeRouter`. - - ### `makeRpcClient(middleware, generalErrors?)` - - Signature drops the `rcs` (request-context map wrapper) parameter. `rcs` was only load-bearing on the type side for `RequestConfig` inference; that information is now derived from `middleware.requestContextMap`. `middleware` is required — the previous "rcs + optional middleware" overload is gone. - - **Migration**: - - ```diff - -makeRpcClient(RequestContextMap, undefined, AppMiddleware) - +makeRpcClient(AppMiddleware) - ``` - - For tests/clients without a real middleware, build a minimal stub (`{ requestContextMap, requestContext }`) or pass any value satisfying `ClientMiddleware`. - - ### `makeRouter(middlewareLive)` - - `makeRouter()` no longer infers the live middleware layer from `meta.middleware.Default`. The Live layer is now passed explicitly to `makeRouter`, and the request classes only carry the middleware tag (schema-only). This decouples the router from any assumption that the middleware tag exposes a `Default` static. - - **Migration**: - - ```diff - -export const { Router, matchAll } = makeRouter() - +export const { Router, matchAll } = makeRouter(AppMiddleware.Default) - ``` - -- ad0ede6: Keep tag-aware `ProjectableGuard` on both `project()` and `projectComputed` (no loose key-only paper-over). - - Hardening: - - - single-literal tags allow dual same-tag domain variants (KeysOfUnion of matched members) - - multi-tag / string tags still require keys on every matched member - - optional projection/domain keys checked by key presence only (not optional-assignability) - - Call sites must project domain-owned fields per tagged state. - -- 52b0b01: fix request attr -- 9ea024d: Improve repository schema encode/decode telemetry for event-loop tail analysis. - - - widen `app.schema.{encode,decode}.duration` histogram buckets into multi-second stalls - - count `app.schema.slow` (duration ≥ 100ms) with `app.entity` / operation attributes for alertable rates - - annotate spans with `app.schema.slow` and, on encode, `app.entity.state` from the first item's `_tag` - -- e6f2341: Add SecretURL tests and switch secretURL config to use Config.nonEmptyString -- 2495ace: `InvalidationSet.add` accepts RPC handler shorthand. - - Server-side handlers may now pass an RPC handler object directly to - `InvalidationSet.add`; its query key is derived via `makeQueryKey`. Raw - `InvalidationKey` arrays continue to work. - - ```ts - // before - yield * - Invalidation.InvalidationSet.use((_) => _.add(makeQueryKey(UserRsc.GetMe))); - - // after - yield * Invalidation.InvalidationSet.use((_) => _.add(UserRsc.GetMe)); - ``` - -- 0b3e00e: fix bug -- 52b0b01: fix Req -- c215db8: align TaggedUnion with array arg -- 12abb55: Refine schema copy behavior by keeping class copy constructor-based while using structural copy for Struct and TaggedStruct helpers. -- 0cff7c1: workaround middleware error issue -- c1a6fdc: fix proxify -- 8ae8b53: input mess -- a5248a9: use new schema features -- 0e824ef: add missing Brand -- 52b0b01: fix RequestName -- eb06b32: improve root union select -- 52b0b01: Update Effect dependencies to 4.0.0-beta.29 across workspace packages. -- 52b0b01: switch to NdJson -- acfdc9e: Point development package exports at TypeScript sources while keeping published exports on compiled dist files. -- 1186b09: improve branded types, keep types through Rebuild -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. -- 3c1f52d: improve: class strictness enabled by default again, allow `strict: false` as opt out for now. -- 0054611: Stream RPC: drain read/write data-dependencies per emitted value chunk instead of sending the - cumulative set on every metadata chunk. Each metadata chunk now carries only the delta recorded - since the last chunk, the bucket is cleared for the next segment, and the terminal "done"/"error" - chunks drain the remainder. The emit condition also broadens to include non-empty reads, so stream - queries forward their read-dependencies mid-stream too. The client already accumulates deltas into - its per-call recorder, so no FE change is required. -- 583393f: Default the stream `mutateStream` execute resolved value to the request's success type when no `final` schema is declared. - - Previously the type defaulted to `void`, but the runtime already resolves with the last emitted value. Types now match runtime behaviour: `execute` returns `Final` if a `final` schema is set, otherwise the success type. - -- 459697f: Restore native Arbitrary for the schemas that lost custom fast-check `toArbitrary`. `StringId` uses `toCodecArbitrary` (the native replacement): generate a 210-byte `Uint8Array` and run `customRandom(urlAlphabet, 21, …)` — same as the old `StringIdArb`. JSON stays a branded string via `toCodec`. `Url` is `https://…`. `RequestId` samples unique nanoid-shaped ids. `Finite` generation is capped at ±1e6. `generateFromSchema` jumps its master seed per call so successive `count: 1` draws do not collide on attempt-0 edge strings. -- 3eda52e: Fix `StructFacade` to extend effect-app's own `Struct`, not `effect/Schema`'s. - - `StructFacade` was built on `Omit, ...>` where `S` is `effect/Schema` (core). Scanner / consumer models are effect-app `S.Struct`, which is a distinct type from effect core's `Struct`, so a facade built on the core `Struct` is not assignable where an effect-app struct is expected — e.g. workflow payloads (`Workflow<…, Struct>`) and other effect-app `Struct`-shaped positions failed to type-check. Switch the `Omit` base to effect-app's `Struct` (the `Fields extends S.Struct.Fields` constraint stays effect core, matching effect-app's own `Struct`). - -- b52b424: restore annotations for now -- 7c25dbb: Add relaxed wrapper support for `ErrorClass` and `TaggedErrorClass` in `effect-app/Schema`, matching the existing class wrapper behavior (`copy`, cached `ast`, and unbounded parse concurrency). -- b8b9080: update packages -- b90fa30: Repository `changeFeed` now broadcasts synchronously: `publish` awaits every subscribed handler before resolving. Replaces `PubSub.PubSub` with a `ChangeFeed` interface (`publish` + scoped `subscribe`). Handlers are auto-removed when the subscriber's scope closes, and the full handler set is cleared when the repository's scope closes. Repository construction now requires `Scope` — wire `makeRepo` through `Layer.scoped` / `Effect.scoped`. -- 57db551: Split `TaggedRequestFor` into `Query` and `Command` factories, and mark generated request classes with `type: "query" | "command"`. - - Vue client helpers now expose query-only helpers (`query`, `suspense`, `fetch`) for query requests and mutation-only helpers (`mutate`, `fetch`) for command requests. - -- 52b0b01: remove `fields` constraint from TaggedUnion/tags helpers, extract `_tag` via AST walk instead -- dbcc53b: Refactor command invalidation typing: declare resources via `Command()`, pass `invalidatesQueries` as the optional 4th argument, and enforce exact `clientFor` invalidation resources when required. -- 52b0b01: Configure Changesets fixed versioning for public packages. -- 140e192: Relax invalidation resource value constraints to allow arbitrary values while preserving query-only filtering in invalidation handling. -- 9992e70: pass options -- 24f0a5a: rename Literal to Literals, no longer hiding Schema's singular Literal. -- 256ae85: cleanup -- 52b0b01: update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it -- fac725d: update effect to latest beta -- 412f08b: Include changed entity IDs in repository data dependencies, with explicit query scopes and configurable write aliases, while retaining coarse collection dependencies. -- 79eb019: Remove redundant schema `title` annotations when they duplicate the schema `identifier`. -- 6ae3050: Preserve class annotation parseOptions in relaxed declaration struct decoding so custom parse options (including concurrency defaults) are applied consistently. -- f353d48: Rename the Class/TaggedClass relaxed declaration option to `strict` (default `false`) and apply it to `Class`, `TaggedClass`, `ExtendedClass`, and `ExtendedTaggedClass`. - - When `strict: true`, class decoding keeps strict class-level declaration behavior; by default, decoding remains relaxed and preserves field-level schema errors. - -- 52b0b01: Fix `TaggedRequest` no-config error type inference so requests without a third argument infer the same default error schema as requests with explicit success config. -- 01bab22: Work around tsgo failing to reduce `S.Codec.DecodingServices` (`X extends Top ? X["DecodingServices"] : never`) in generic positions, which left `unknown` and polluted the `R` channel of client handlers and RPC middleware failure context. Since the schemas involved are already constrained to `S.Top`, read `["DecodingServices"]` directly in `RequestHandlerFor`, `TagClass.FailureContext`, and the Vue `MutationExt` / `QueryProjection` types. -- 2a86a17: improve tsgo compat: avoid deferred `Schema.Type`/`Codec.Encoded`/`Codec.DecodingServices`/`Codec.EncodingServices` conditional helpers in generic positions where the type parameter is already constrained to `Schema.Top`. Index the property directly (`X["Type"]`, `X["Encoded"]`, `X["DecodingServices"]`, …) so tsgo doesn't leak `unknown` into `Effect` channels (notably `R`). - - Sites: `client/clientFor.ts` (`RequestHandlerFor`, `FinalTypeOf`, `ExtractResponse`, `ExtractEResponse`), `client/makeClient.ts` (`InputFromPayload`, `OutputFromSuccess`, `InvalidationConfigForCommand`, `TaggedRequestWithMeta` overloads), `rpc/MiddlewareMaker.ts` (`Errors`), `rpc/RpcMiddleware.ts` (`Failure`, `FailureContext`), `Schema/ext.ts` (`ReadonlySetFromArray`, `ReadonlyMapFromArray`), `infra/routing.ts` (`GetSuccessShape`, handlers, route matcher), `vue/makeClient.ts` (`MutationExt.project`, `MutationWithExtensions`, `QueryProjection`), `vue/routeParams.ts` (`parseRouteParams*`). - -- 52b0b01: update all teh tings -- c3299f7: update packages -- 6b57330: Upgrade Effect packages to npm `4.0.0`. -- e2d00b5: Upgrade Effect packages to npm `4.0.1`. -- 547e2e1: Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - - API adaptations for beta.107: - - - `concurrency: "inherit"` → `"unbounded"` - - `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` - - `Schema.LazyArbitrary` → `Schema.Arbitrary` - - `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) - - `SchemaIssue` constructors no longer take `Option` (annotations + input) - - filter meta via `annotations.representation` instead of `annotations.meta` - - `context.defaultValue` → `context.constructorDefault` (single Link) - - Class detection via `~constructor` + static `identifier` - - Redacted detection via `representation.id` - - localized StandardSchema hooks updated for new issue/input model - - provide `NodeCrypto.layer` for cluster sqlite tests - - default `sync-effect` subtree URL → `Effect-TS/effect` - -- 52b0b01: update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined -- Update effect packages to 4.0.0-beta.52 -- ea32222: Update to effect 4.0.0-beta.60 and use native `Rpc.custom` constructors (`makeCommandRpc`, `makeStreamRpc`) for metadata-wrapped RPC schemas instead of manually wrapping/unwrapping schemas inline. -- 7ca66ce: Update to effect 4.0.0-beta.66. Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). -- 57a1862: Update to effect 4.0.0-beta.67. Switch deps from `pkg.pr.new` snapshot back to npm beta tag. -- 3e855bc: Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - - Adapt the infra workflow engines to beta.83 API changes: - - - `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). - - `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). - -- 78d076a: Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). -- ffd140f: Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. -- e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. -- b6dda09: Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. -- 0263827: Update to effect `pkg.pr.new` snapshot at `a42ef66` (4.0.0-beta.66). Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). -- 8bd5bfe: Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - - API adaptations for rc.112: - - - cluster encoded driver `resetAddress` → batched `resetAddresses` - - Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) - - Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization - - `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) - - JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) - -- 2231ef8: Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. -- c7bbc41: fix - class should check type side -- 52b0b01: fix ForceVOid -- 52b0b01: fix ForceVoid for real - -## 4.0.0-beta.333 - -### Patch Changes - -- e2d00b5: Upgrade Effect packages to npm `4.0.1`. - -## 4.0.0-beta.332 - -### Patch Changes - -- 6b57330: Upgrade Effect packages to npm `4.0.0`. - -## 4.0.0-beta.331 - -## 4.0.0-beta.330 - -### Patch Changes - -- 48d9f36: Memory, SQLite, and Postgres `Store.queryRaw` now run over stored JSON documents, not Encoded rows. `Repository.queryRaw` already applies `toCodecJson` to the projector output, so native Encoded `Date` / `Map` / `Set` values no longer fail `Expected JSON value`. Cosmos was already JSON and is unchanged. - - Memory projectors that assumed native Encoded `Date` / `Map` / `Set` values now see the stored JSON form (ISO strings, arrays). - -## 4.0.0-beta.329 - -### Patch Changes - -- 459697f: Restore native Arbitrary for the schemas that lost custom fast-check `toArbitrary`. `StringId` uses `toCodecArbitrary` (the native replacement): generate a 210-byte `Uint8Array` and run `customRandom(urlAlphabet, 21, …)` — same as the old `StringIdArb`. JSON stays a branded string via `toCodec`. `Url` is `https://…`. `RequestId` samples unique nanoid-shaped ids. `Finite` generation is capped at ±1e6. `generateFromSchema` jumps its master seed per call so successive `count: 1` draws do not collide on attempt-0 edge strings. - -## 4.0.0-beta.328 - -### Minor Changes - -- 3a8c710: Store document decode fails as a typed `SchemaError` instead of a defect; repositories keep their public error channels by dying at the boundary; `validateSample` reports documents that fail at the store boundary. - -## 4.0.0-beta.327 - -## 4.0.0-beta.326 - -### Minor Changes - -- 2ebf8ae: `jitM` runs at the store boundary as JSON → JSON, before the `toCodecJson(toEncoded(schema))` decode; repositories decode the Encoded shape only. - - `jitM` used to be applied by the repository _after_ the store had already decoded the document, and was typed `(pm: Encoded) => Encoded` - a lie, since jitMs are written against the stored JSON and `Encoded` holds native `Date`/`Map`/`Set` values. - - The read pipeline is now: raw JSON document → merge `defaultValues` (unchanged: they only fill _absent_ keys, so an explicitly stored `null` reaches `jitM`) → `jitM` (JSON → JSON) → decode `Schema.toCodecJson(Schema.toEncoded(schema))` → Encoded. The repository then decodes Encoded → the domain type with `schema` and no longer applies `jitM` at all. Because `jitM` now runs before any schema decode sees the document, it can repair legacy shapes including explicit `null`s. - - - `StoreConfig.jitM?: (json: JsonRecord) => JsonRecord` is new, where `JsonRecord` (exported from `effect-app/Store`) is `{ readonly [key: string]: Schema.Json }`. It is not applied on the write/encode path, and never receives `_etag`. - - `RepositoryOptions.jitM` changes type from `(pm: Encoded) => Encoded` to `(json: JsonRecord) => JsonRecord` and is forwarded into the store config. - - **Migrating a jitM:** it now receives and must return JSON. A `Date` field arrives as an ISO string (return a string, not a `Date`), a `ReadonlySet` as an array, a `ReadonlyMap` as an array of `[key, value]` pairs. Field access is index-signature based (`json["x"]`) rather than typed property access. - - The JSON→Encoded decode stays strict: a document `jitM` does not repair fails at the store boundary instead of being read back half-decoded. - - `ValidationError.jitMResult` is deprecated: `validateSample` only ever sees the store's output, so it is identical to `rawData`. - -## 4.0.0-beta.325 - -### Patch Changes - -- f233f3d: `PositiveNumber` and `NonNegativeNumber` bound native Arbitrary generation to at most 1,000,000 (`arbitraryConstraint` on their existing checks), so generated fixtures no longer overflow derived totals to `Infinity`. Validation and JSON Schema output are unchanged. - -## 4.0.0-beta.324 - -### Patch Changes - -- a0075b8: `Email` and `PhoneNumber` carry native `arbitraryConstraint` patterns, so `effect/unstable/arbitrary` (and `generateFromSchema`) can sample them instead of exhausting rejection sampling on their `refine` guards. - -## 4.0.0-beta.323 - -### Minor Changes - -- ab289d4: Add opt-in `autoscaleMaxThroughput` for Cosmos containers created on demand (`StorageConfig`, `ClusterCosmosConfig`, `WorkflowEngineCosmosConfig`). When set, a missing container is created with autoscale at that max RU/s, unless its database has shared throughput (then it keeps sharing the database pool). Existing containers are untouched; unset keeps the Cosmos default of manual 400 RU/s per container. - -## 4.0.0-beta.322 - -### Patch Changes - -- 2231ef8: Upgrade Effect packages to npm `4.0.0-rc.115`. Drop the pkg.pr.new pin used while `effect@4.0.0-rc.114` was unpublished. - -## 4.0.0-beta.321 - -### Patch Changes - -- 18bae5b: Upgrade Effect packages to `4.0.0-rc.114` (core `effect` via pkg.pr.new at `cdb3ac91` until npm publishes that version). Also bump independently versioned `@effect/language-service` to `0.87.2` and `@effect/tsgo` to `0.45.0` (requires `oxlint` `1.82.0`). - - Breaking API updates from this RC: - - - PascalCase Config/CLI constructors (`Config.String`, `Flag.File`, `Config.NonEmptyString`, `Config.Redacted`, `Config.Literal`) - - `Config.Record` now returns a Config - - Schema `toArbitrary` (fast-check) replaced by native `effect/unstable/arbitrary` - - `SchemaTransformation.transformOrFail` renamed to `transformEffect` - - `Fiber.currentSpan` moved to `fiber.cache.span` - - HTTP server addresses are `InetAddressV4`/`InetAddressV6` instead of `TcpAddress` - - Object JSON Schema now emits `additionalProperties: true` - - Schema `Union` AST stores matching as `options.mode` instead of `mode` - -## 4.0.0-beta.320 - -## 4.0.0-beta.319 - -## 4.0.0-beta.318 - -### Patch Changes - -- 8bd5bfe: Update effect packages to `4.0.0-rc.112` (from `beta.107`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Sync `repos/effect` subtree from `Effect-TS/effect` at `effect@4.0.0-rc.112`. - - API adaptations for rc.112: - - - cluster encoded driver `resetAddress` → batched `resetAddresses` - - Cosmos `unprocessedMessages` honors optional `limit` / `addresses` (only claimed rows are returned) - - Service Bus `Runners.make` supplies `codecFor` for schema-aware RPC serialization - - `pnpm subtree:effect` passes `--url https://github.com/Effect-TS/effect.git` (published CLI still defaults to effect-smol) - - JSON Schema check constraints are compacted onto the parent (`minLength`/`maxLength` instead of `allOf`) - -## 4.0.0-beta.317 - -### Minor Changes - -- 186de3a: Stop forcing Date/Map/Set Encoded shapes to JSON. - - `Schema.Date` / `ReadonlySet` / `ReadonlyMap` now keep native Encoded types (`Date`, `Set`, `Map`). Use `DateFromString`, `ReadonlySetFromArray`, and `ReadonlyMapFromArray` when the Encoded form must be JSON. The query DSL accepts those native values, including array ops (`includes` / `in` / `includes-any`) on `Date[]` and `ReadonlySet` fields. Memory, Disk, SQL, and Cosmos convert Encoded Date/Map/Set through `Schema.toCodecJson` on write/read; query parameters and defaults lower the same way from the store schema. App types such as DateOnly stay native Encoded and JSON-lower via that schema — not a type registry. - -- 186de3a: Query maps as JSON arrays of `[key, value]` tuples. - - `where("meta", "hasKey" | "hasValue" | "hasKeyValue", ...)` (and `not*` / `*-any` / `*-all` variants) filter `ReadonlyMap` fields. Memory, Disk, SQLite, Postgres, and Cosmos compile those ops against the encoded tuple array. - -- 186de3a: JSON stores lower native Encoded values (Date, Map, Set, and app types such as DateOnly) through the store's document schema. - - `makeRepo` already passes that schema. Adapters encode documents, query parameters, and defaults with `Schema.toCodecJson(toEncoded(schema))` at the field path. No type registry. Schemaless stores still lower Date/Map/Set structurally. - -## 4.0.0-beta.316 - -## 4.0.0-beta.315 - -## 4.0.0-beta.314 - -## 4.0.0-beta.313 - -## 4.0.0-beta.312 - -### Patch Changes - -- 9ea024d: Improve repository schema encode/decode telemetry for event-loop tail analysis. - - - widen `app.schema.{encode,decode}.duration` histogram buckets into multi-second stalls - - count `app.schema.slow` (duration ≥ 100ms) with `app.entity` / operation attributes for alertable rates - - annotate spans with `app.schema.slow` and, on encode, `app.entity.state` from the first item's `_tag` - -## 4.0.0-beta.311 - -### Patch Changes - -- 547e2e1: Update effect packages to `4.0.0-beta.107` (from `beta.90`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`, and `fast-check` to `^4.9.0`. Sync `repos/effect` subtree from `Effect-TS/effect` (effect-smol stopped publishing tags after beta.98). - - API adaptations for beta.107: - - - `concurrency: "inherit"` → `"unbounded"` - - `Schema.ErrorClass` / `TaggedErrorClass` → `Schema.Error` / `TaggedError` - - `Schema.LazyArbitrary` → `Schema.Arbitrary` - - `Schema.DateValid` / `isDateValid` removed (`Schema.Date` rejects invalid dates) - - `SchemaIssue` constructors no longer take `Option` (annotations + input) - - filter meta via `annotations.representation` instead of `annotations.meta` - - `context.defaultValue` → `context.constructorDefault` (single Link) - - Class detection via `~constructor` + static `identifier` - - Redacted detection via `representation.id` - - localized StandardSchema hooks updated for new issue/input model - - provide `NodeCrypto.layer` for cluster sqlite tests - - default `sync-effect` subtree URL → `Effect-TS/effect` - -## 4.0.0-beta.310 - -## 4.0.0-beta.309 - -### Patch Changes - -- 99c43c4: Derive scoped dependency reads and writes from typed query filters and annotated model relationships, including previous aliases. - -## 4.0.0-beta.308 - -## 4.0.0-beta.307 - -### Patch Changes - -- 40585ca: Keep tag-aware `ProjectableFromDomain` for `projectComputed` only; restore loose key-presence guard for `project()` so view DTOs with reshaped fields keep typechecking. -- ad0ede6: Keep tag-aware `ProjectableGuard` on both `project()` and `projectComputed` (no loose key-only paper-over). - - Hardening: - - - single-literal tags allow dual same-tag domain variants (KeysOfUnion of matched members) - - multi-tag / string tags still require keys on every matched member - - optional projection/domain keys checked by key presence only (not optional-assignability) - - Call sites must project domain-owned fields per tagged state. - -## 4.0.0-beta.306 - -### Patch Changes - -- 0fe925d: Tag-aware `ProjectableFromDomain` for `projectComputed`: projection Encoded fields must exist on the matching domain tagged state (or be computed). Prevents Overview.List SchemaErrors when cancel states omit workflow lock fields like `activeRequest`. - -## 4.0.0-beta.305 - -### Minor Changes - -- 8bd9a11: Support ID-scoped signal dependencies and additive repository dependencies derived from previous and current entities. - -## 4.0.0-beta.304 - -### Patch Changes - -- 412f08b: Include changed entity IDs in repository data dependencies, with explicit query scopes and configurable write aliases, while retaining coarse collection dependencies. - -## 4.0.0-beta.303 - -### Patch Changes - -- 33b0544: Replace repository codec child spans with parent-span timing attributes and bounded metrics, and record database operation timing for instrumented stores. - -## 4.0.0-beta.302 - -### Patch Changes - -- c0e5a1b: Add `Schema.dropConstructorDefault`, a `Struct.Lambda` that clears a field's `withConstructorDefault`. Compose it with `Struct.omit`/`Struct.map` when deriving a partial-update schema from a "create" schema, so omitted fields' constructor defaults don't resurrect themselves in `.make(...)` output. - - Also, `makeExactOptional` now drops constructor defaults automatically, matching this behavior. - -## 4.0.0-beta.301 - -## 4.0.0-beta.300 - -## 4.0.0-beta.299 - -## 4.0.0-beta.298 - -### Patch Changes - -- 08d092c: Update Atom query caches in memory and accumulate stream invalidations so live events do not cause repeated RPC refetches. - -## 4.0.0-beta.297 - -### Patch Changes - -- 30c512d: fix rpc middleware context type issue - -## 4.0.0-beta.296 - -### Patch Changes - -- e0c4835: revert bs - -## 4.0.0-beta.295 - -### Patch Changes - -- eb06b32: improve root union select - -## 4.0.0-beta.294 - -### Patch Changes - -- a211c12: Fix projection schema typing for union members and computed projections. - -## 4.0.0-beta.293 - -### Patch Changes - -- d1c15d3: Tighten projectComputed projection schema typing against encoded repo fields and computed output types. - -## 4.0.0-beta.292 - -## 4.0.0-beta.291 - -## 4.0.0-beta.290 - -### Patch Changes - -- dc07df5: Fix nested DTO subset projection inference for tagged unions. - -## 4.0.0-beta.289 - -### Patch Changes - -- a1b59bc: Allow query projections to use a repository's full union schema. - -## 4.0.0-beta.288 - -### Minor Changes - -- aeb17bc: Add `disableQueryInvalidation` flag to Command config for background saves. - - Set `disableQueryInvalidation: true` in a `Req.Command` config (3rd argument) - to suppress all client-side query invalidation for that command — client - `invalidatesQueries` callbacks, server-returned `metadata.invalidateQueries`, - and repository-derived write-dependency matching are all skipped. Use for - background saves (e.g. debounced auto-save) whose writes should not trigger - query refetches. - - - `InvalidationConfig` gains `disableQueryInvalidation?: boolean`. - - `RequestHandlerWithInput` gains `disableQueryInvalidation?: boolean`, - propagated from `Request.config` by `ApiClientFactory.makeFor`. - - `invalidateCache` early-returns `Effect.void` when the flag is set, - applying to both regular and stream mutations. - -## 4.0.0-beta.287 - -### Patch Changes - -- 261470f: Remove the local `Schema.Void` / `SchemaAST.Void` override. effect `4.0.0-beta.90` ships the TypeScript `void` parser semantics upstream (`SchemaAST.Void.getParser()` → `fromAnyToConst(undefined)`, the effect-smol PR #2475 behavior), so `S.Void` already accepts any present value and discards it to `undefined`. `Schema.Void` now re-uses effect's `S.Void` and `SchemaAST` is a plain re-export of `effect/SchemaAST`. The unused `Void_` alias is dropped. No behavior change. - -## 4.0.0-beta.286 - -### Patch Changes - -- b6dda09: Update effect packages to `4.0.0-beta.90` (from `beta.88`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 4.0.0-beta.285 - -### Patch Changes - -- 7bd8234: Use set-backed data dependencies and provide request-scoped dependency services through a shared root-checked helper. - -## 4.0.0-beta.284 - -## 4.0.0-beta.283 - -### Minor Changes - -- 0054611: Derive query invalidation from repository read/write dependencies and propagate dependency metadata through RPC clients. - -### Patch Changes - -- 0054611: Stream RPC: drain read/write data-dependencies per emitted value chunk instead of sending the - cumulative set on every metadata chunk. Each metadata chunk now carries only the delta recorded - since the last chunk, the bucket is cleared for the next segment, and the terminal "done"/"error" - chunks drain the remainder. The emit condition also broadens to include non-empty reads, so stream - queries forward their read-dependencies mid-stream too. The client already accumulates deltas into - its per-call recorder, so no FE change is required. - -## 4.0.0-beta.282 - -### Minor Changes - -- b2df3fa: Align `Schema.Void` with TypeScript `void` return-value semantics (effect-smol PR #2475 / `b7d46ab`). - - `S.Void` now accepts **any present value** at runtime and discards it to `undefined`, while keeping the decoded/encoded type as `void` — matching a `void` return whose result callers never observe. This is implemented as an override of the AST node in a new `effect-app/SchemaAST` module (`export * from "effect/SchemaAST"` plus a `Void` subclass whose parser mirrors the PR's `fromAnyToConst(undefined)`), so there is a single canonical `Void` used everywhere, including RPC success schemas. - - - New `effect-app/SchemaAST` module; internal `SchemaAST` imports across the libs now route through it. - - Removed `ForceVoid` from `effect-app/client/makeClient` — use `S.Void` directly, which now carries this behaviour. - - `S.Void_` remains available as effect's original (`undefined`-only) Void. - -### Patch Changes - -- f150cf9: Remove obsolete queue machinery left unused after the move to cluster entities: the SB/SQLite/mem `QueueMaker` implementations (`QueueMaker/{SQLQueue,memQueue,sbqueue}.ts`), their `ServiceBus.ts`/`memQueue.ts` transports, and `RequestFiberSet.ts` (its `setRootParentSpan` helper is inlined into `MainFiberSet`). `effect-app/QueueMaker` keeps only `QueueMeta`; the `QueueBase` interface and empty `QueueMaker` ops object are dropped. -- e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged. - -## 4.0.0-beta.281 - -### Patch Changes - -- ffd140f: Update effect packages to `4.0.0-beta.86` (from `beta.84`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. No source changes required — typecheck and tests pass unchanged. - -## 4.0.0-beta.280 - -### Patch Changes - -- 3eda52e: Fix `StructFacade` to extend effect-app's own `Struct`, not `effect/Schema`'s. - - `StructFacade` was built on `Omit, ...>` where `S` is `effect/Schema` (core). Scanner / consumer models are effect-app `S.Struct`, which is a distinct type from effect core's `Struct`, so a facade built on the core `Struct` is not assignable where an effect-app struct is expected — e.g. workflow payloads (`Workflow<…, Struct>`) and other effect-app `Struct`-shaped positions failed to type-check. Switch the `Omit` base to effect-app's `Struct` (the `Fields extends S.Struct.Fields` constraint stays effect core, matching effect-app's own `Struct`). - -## 4.0.0-beta.279 - -### Minor Changes - -- 5f9cd6a: Schema/Class facades: add `StructFacade` and carry `identifier` on the class facades. - - - Add `StructFacade` = `Omit, "Type" | "Encoded" | "~type.make.in" | "DecodingServices" | "EncodingServices"> & { pinned type-level members }`. A top-level `const X = S.Struct(...)` / `S.TaggedStruct(...)` model faceted by the `.d.ts`-emit compiler is retyped to it: still a real `S.Struct` (so `Workflow.AnyStructSchema`, the `Struct` reconstruction, `Union`, `.fields.x` keep working) while `Type`/`Encoded`/make/services resolve to the named namespace interfaces. - - Add `readonly identifier: string` to `OpaqueClassFacade` and `OpaqueErrorFacadeClass` (an `S.Class` static lost from the bare emitted interface, which the compiler otherwise had to hand-emit per model). `OpaqueFacade` is intentionally left untouched — `S.Opaque` and requests build on `S.Bottom`, not `S.Class`, so they have no `identifier`. - -## 4.0.0-beta.278 - -### Patch Changes - -- a354345: Move release tsconfig flattening from publish to pack lifecycle so package configs are restored before registry upload/auth can fail. -- a69da09: Publish from generated staging directories so release-only files are created outside the source package tree. - -## 4.0.0-beta.277 - -## 4.0.0-beta.276 - -### Patch Changes - -- 32f05c8: Remove unused dependency declarations from package manifests. - -## 4.0.0-beta.275 - -## 4.0.0-beta.274 - -## 4.0.0-beta.273 - -### Patch Changes - -- 664e83d: Add Atom-native Vue query APIs, stream query pull atoms, and a TanStack-backed legacy query engine toggle. -- e4ff9a6: Fix the global `Array`/`ReadonlyArray.map` override breaking union-of-array receivers. The previous `this: NonEmptyArray`/`NonEmptyReadonlyArray` overload was selected-then-rejected on a union receiver (e.g. `(readonly A[] | readonly B[]).map(...)`), raising TS2684. Replaced with a single conditional-return signature (`this extends NonEmpty… ? NonEmpty : U[]`) that preserves NonEmpty refinement without a `this` parameter, so union-array `.map` calls type-check again. - -## 4.0.0-beta.272 - -### Patch Changes - -- 21ac90a: Unify generated model facades on `OpaqueFacade` and support class facades through encoded-key schema transforms. - -## 4.0.0-beta.271 - -### Patch Changes - -- 939bebc: Remove the local `Config.nested` / `ConfigProvider.nested` overrides. They worked around an effect v3 bug where the nested namespace segment bypassed the provider's `mapInput`/`constantCase` transform. effect `4.0.0-beta.84` fixes this upstream (its transformation-compose refactor threads the nested prefix through the provider's lookup transform), and the old overrides relied on internals (`Config.make`, `provider.get`/`mapInput`/`prefix`) that no longer exist. Both modules now re-export effect directly; behaviour for consumers (e.g. `Config.nested("cups")` against a `constantCase` env provider) is unchanged and correct. - -## 4.0.0-beta.270 - -### Patch Changes - -- 78d076a: Update effect packages to `4.0.0-beta.84` (`effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`). - -## 4.0.0-beta.269 - -### Patch Changes - -- 50b022e: Harden model facades and add `OpaqueErrorFacadeClass`. - - - `OpaqueErrorFacadeClass`: facade `TaggedErrorClass`/`ErrorClass` while keeping - `Cause.YieldableError` on the constructed instance (so `yield* new Err()`, - `Effect.fail`, and `instanceof` keep working through the facade). - - `OpaqueFacadeInput` relaxed to require only the codec service channels, so - transformed schemas (`.pipe(encodeKeys/annotate/filter/...)`) can be facaded; - `fields`/`copy`/`mapFields` flow through `OpaqueFacadeStatics` when present, and - `to` is carried so models that compose via `X.to.fields` keep working. - - Dropped the wide `fields`/`mapFields` overrides on the facade interfaces so the - precise statics win (keeps `Q.project(X.mapFields(...))` typed). - - Codegen (`eslint-codegen-model`): resolver prefers the private `_X` over the - self-referential facade; converges static service types in one run; per-model - classification (facade Opaque models, leave `Class` standard in mixed files); - `Make` emitted as `type X = {...} | void` when the make-input is voidable; - `readonly`-prefixed array/tuple elements parenthesized; value self-references in - the moved `_X` body rewritten to `_X`; instance getters surface on `Self`. - -- 50b022e: Add generated opaque model facades that expose static Type, Encoded, Make, and Schema declarations without leaking the private struct schema type to downstream project references. - -## 4.0.0-beta.268 - -### Patch Changes - -- acfdc9e: Point development package exports at TypeScript sources while keeping published exports on compiled dist files. - -## 4.0.0-beta.267 - -### Minor Changes - -- 23a7167: Add a typed, serializable `DatabaseError { message, transient, cause }` for store adapter failures. - - Store adapters wrapped DB calls in bare `Effect.promise` / `.orDie`, so a transient infra failure (request timeout, throttle, 5xx, dropped socket) became a raw-`Error` defect — which `Effect.retry` can't retry and which breaks JSON encoding of a workflow exit cause ("Expected JSON value, got Error"). `DatabaseError` is now exposed on all `Store` and `Repository` method error channels (writes: `OptimisticConcurrencyException | DatabaseError`) and added to `SupportedErrors` so the api/client/FE treat it as a 500-class error. Each adapter wraps its db-call failures into `DatabaseError` with a `transient` flag (timeout/throttle/5xx ⇒ retryable); the `cause` serializes via `Schema.Defect`. Construction/seed/DDL paths stay `orDie`. - -## 4.0.0-beta.266 - -### Patch Changes - -- bbaa67e: Fix `TS2589: Type instantiation is excessively deep and possibly infinite` when querying models that embed `Schema.Defect()` (or any field encoded as `Json`). - - The query path type `Path` (used by `Q.where`/`Q.and`/`Q.order` to type dotted field paths) recursed through object types with no termination for self-referential ones. Under effect `4.0.0-beta.83`, `Schema.Defect()` encodes as `Json`, whose `JsonObject = { readonly [x: string]: Json }` index signature made `Path` descend forever, blowing past TypeScript's instantiation limit on any model with such a field (e.g. carrier-error states carrying `raw: Schema.Defect()`). - - `Path` now threads a `Seen` set of already-entered object types and stops before re-entering one, so self-referential types terminate (the field itself stays a valid leaf path; only the unbounded descent is cut). Finite models are unaffected. - -## 4.0.0-beta.265 - -### Patch Changes - -- 3e855bc: Update Effect packages to `4.0.0-beta.83` (from `beta.74`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/sql-sqlite-node`, `@effect/atom-vue`, `@effect/vitest`. - - Adapt the infra workflow engines to beta.83 API changes: - - - `Schema.Defect` is now a constructor function — use `S.Defect()` when building the deferred-exit codec (the bare constant no longer produces a usable schema and crashed `toType`). - - `Workflow` exposes its name as `_tag` instead of `name`. `WorkflowEngineSqlite`/`WorkflowEngineCosmos` now key the registry, codec caches, and persisted `workflow_name` off `workflow._tag`, fixing crash-recovery (stale-lease re-drive previously registered under an `undefined` key and never matched). - -## 4.0.0-beta.264 - -## 4.0.0-beta.263 - -## 4.0.0-beta.262 - -### Patch Changes - -- 55c6572: update packages - -## 4.0.0-beta.261 - -## 4.0.0-beta.260 - -### Patch Changes - -- bogus - -## 4.0.0-beta.259 - -## 4.0.0-beta.258 - -## 4.0.0-beta.257 - -### Minor Changes - -- e71eb78: Tighten typing of query DSL builders so field/path parameters are constrained to actual field paths instead of bare `string`: - - - `relation(path)` now infers the relation's element type and constrains `distinctCount`/`sum`/`collect`/`collectDistinct`/`collectFields`/`collectDistinctFields` and the `unit` of `sumExprBy`/`sumExprNormalized` to `FieldPath`. - - `relation(path).expr` exposes a scope-bound math-expression builder so `expr.field(...)` inside `sumExpr`/`sumExprBy`/`sumExprNormalized` is typed against the relation element. - - Top-level `expr.field` accepts an optional generic for opt-in tightening (`expr.field("x")`). - - `aggregate(schema, build)` accepts a builder callback whose `agg` argument is bound to the source row inferred from the pipe — paths are checked without any explicit generic: `make().pipe(aggregate(schema, ($) => ({ city: $.field("address.city") })))`. Plain `AggregateMap` form still accepted. - - `projectComputed(schema, build, mode?)` accepts a builder callback whose `{ relation }` argument is bound to the source row inferred from the pipe: `make().pipe(projectComputed(out, ({ relation }) => ({ total: relation("items").sum("qty") })))`. Plain `ComputedProjectionMap` form still accepted. - - `agg()` factory remains as an escape hatch when the builder is built outside a pipe. - -## 4.0.0-beta.256 - -### Patch Changes - -- 347af48: Fix schema interface widening regression introduced in 4.0.0-beta.255 (changeset `olive-onions-lose`). - - The original refactor annotated branded schema constants with interfaces extending `S.Codec & WithDefaults<...>`. That widened the underlying `BrandedSchema<...>` chain and dropped phantom slots (`Iso`, `~type.make.in`, `~type.parameters`, `Rebuild`, etc.) that downstream combinators read. In consumer projects this surfaced as `DecodingServices` / `EncodingServices` leaking as `unknown` — for example `Q.project(schema, "project")` failing with `Type 'unknown' is not assignable to type 'never'`, and RPC handler / generator-return shape mismatches against `Effect`. - - Each `*Schema` interface now extends the concrete underlying chain (e.g. `BrandedSchema`) and adds a call signature for `withDefaultMake` (and an explicit `withConstructorDefault` field for the numeric and `StringId` schemas). The runtime values are unchanged and the type-display improvement is preserved. - - Affected: `NonEmptyString*`, `Min3String255`, `StringId`, `Url`, `PositiveInt`, `NonNegativeInt`, `Int`, `PositiveNumber`, `NonNegativeNumber`, and `brandedStringId`. - -## 4.0.0-beta.255 - -### Patch Changes - -- 52a31dd: Reduce schema type complexity by exposing explicit interfaces for branded string and number schemas. - - Each branded constant in `Schema/strings.ts`, `Schema/moreStrings.ts`, and `Schema/numbers.ts` now has a named interface (e.g. `NonEmptyStringSchema`, `StringIdSchema`, `PositiveIntSchema`, `UrlSchema`) annotating its `export const`. Mirrors the pattern used in `effect/Schema` itself — TypeScript reports the interface name instead of expanding the full pipe/brand/extension chain, which shrinks inferred types in consumer code and speeds up type display. - - No runtime or API surface changes; `brandedStringId` now returns `BrandedStringIdSchema` (same shape as before, just named). - -## 4.0.0-beta.254 - -### Patch Changes - -- 29a1e57: Repository `changeFeed` is now namespace-aware. Events carry the `storeId` namespace as a third tuple element (`ChangeFeedEvent = [items, op, namespace]`). `subscribe` accepts `options.namespace` to register a per-namespace handler; omitting it registers a wildcard handler that receives events from every namespace. Per-namespace handler buckets eliminate cross-namespace fan-out and avoid waking handlers for irrelevant tenants. - -## 4.0.0-beta.253 - -### Patch Changes - -- b90fa30: Repository `changeFeed` now broadcasts synchronously: `publish` awaits every subscribed handler before resolving. Replaces `PubSub.PubSub` with a `ChangeFeed` interface (`publish` + scoped `subscribe`). Handlers are auto-removed when the subscriber's scope closes, and the full handler set is cleared when the repository's scope closes. Repository construction now requires `Scope` — wire `makeRepo` through `Layer.scoped` / `Effect.scoped`. - -## 4.0.0-beta.252 - -### Patch Changes - -- a788432: fix circular - -## 4.0.0-beta.251 - -### Patch Changes - -- 1c858d3: fix request scope problems - -## 4.0.0-beta.250 - -### Patch Changes - -- 3053760: fix: restore context map container behavior - -## 4.0.0-beta.249 - -### Minor Changes - -- ba789a2: Move core service contracts and runtime-agnostic modules into `effect-app`, keep `infra` and `vue` focused on adapters, and drop the temporary `infra` compatibility re-export paths in favor of the new canonical imports. - - `@effect-app/infra` no longer re-exports moved core modules such as `./Model`, `./Emailer/service`, `./QueueMaker/service`, `./Store/service`, `./adapters/*`, or `./api/*` entrypoints. - -## 4.0.0-beta.248 - -### Patch Changes - -- 2a86a17: improve tsgo compat: avoid deferred `Schema.Type`/`Codec.Encoded`/`Codec.DecodingServices`/`Codec.EncodingServices` conditional helpers in generic positions where the type parameter is already constrained to `Schema.Top`. Index the property directly (`X["Type"]`, `X["Encoded"]`, `X["DecodingServices"]`, …) so tsgo doesn't leak `unknown` into `Effect` channels (notably `R`). - - Sites: `client/clientFor.ts` (`RequestHandlerFor`, `FinalTypeOf`, `ExtractResponse`, `ExtractEResponse`), `client/makeClient.ts` (`InputFromPayload`, `OutputFromSuccess`, `InvalidationConfigForCommand`, `TaggedRequestWithMeta` overloads), `rpc/MiddlewareMaker.ts` (`Errors`), `rpc/RpcMiddleware.ts` (`Failure`, `FailureContext`), `Schema/ext.ts` (`ReadonlySetFromArray`, `ReadonlyMapFromArray`), `infra/routing.ts` (`GetSuccessShape`, handlers, route matcher), `vue/makeClient.ts` (`MutationExt.project`, `MutationWithExtensions`, `QueryProjection`), `vue/routeParams.ts` (`parseRouteParams*`). - -## 4.0.0-beta.247 - -### Patch Changes - -- 01bab22: Work around tsgo failing to reduce `S.Codec.DecodingServices` (`X extends Top ? X["DecodingServices"] : never`) in generic positions, which left `unknown` and polluted the `R` channel of client handlers and RPC middleware failure context. Since the schemas involved are already constrained to `S.Top`, read `["DecodingServices"]` directly in `RequestHandlerFor`, `TagClass.FailureContext`, and the Vue `MutationExt` / `QueryProjection` types. - -## 4.0.0-beta.246 - -## 4.0.0-beta.245 - -## 4.0.0-beta.244 - -### Patch Changes - -- b8b9080: update packages - -## 4.0.0-beta.243 - -## 4.0.0-beta.242 - -### Patch Changes - -- da83c1f: Align `InvalidationEntry` (vue) with `InvalidateQueryInstruction` (effect-app). - - `InvalidateQueryInstruction` is now parametrized over `Filters` / `Options` - (defaulting to `Record` so the core stays framework-agnostic). - `@effect-app/vue` exposes `InvalidationEntry` as a narrowed alias substituting - `@tanstack/vue-query`'s `InvalidateQueryFilters` and `InvalidateOptions`. Single - source of truth for the union shape across both packages. - -- 21017d5: `InvalidationSet.add` accepts arrays and is exposed as a static shortcut. - - - Single item: `yield* InvalidationSet.add(UserRsc.GetMe)` - - Batch: `yield* InvalidationSet.add([UserRsc.GetMe, ["custom", "key"]])` - - Skips the `.use(_ => _.add(...))` boilerplate. - - Existing `InvalidationSet.use(_ => _.add(...))` form still works; the service - identity is preserved via `Object.assign` so `Effect.provideService` and - `.use` / `.useSync` continue to operate on the same `Context.Reference`. - -- 2495ace: `InvalidationSet.add` accepts RPC handler shorthand. - - Server-side handlers may now pass an RPC handler object directly to - `InvalidationSet.add`; its query key is derived via `makeQueryKey`. Raw - `InvalidationKey` arrays continue to work. - - ```ts - // before - yield * - Invalidation.InvalidationSet.use((_) => _.add(makeQueryKey(UserRsc.GetMe))); - - // after - yield * Invalidation.InvalidationSet.use((_) => _.add(UserRsc.GetMe)); - ``` - -## 4.0.0-beta.241 - -### Patch Changes - -- b035b1c: Isolate per-call `Effect.provide(layers)` / `Stream.provide(layers)` in the - RPC api client factory via `{ local: true }`. - - `layers` here is built per RPC invocation from a caller-supplied - `requestLevelLayers` plus a fresh `RequestName` layer. Today both pieces are - stateless, so no observable bug — but `Effect.provide(layer)` without - `local: true` resolves its `MemoMap` from the ambient fiber context, and on a - long-lived runtime (browser app, server fiber) any stateful layer slipped into - `requestLevelLayers` by a caller would be memoized and shared across every - subsequent RPC call. `{ local: true }` builds the layer fresh per call and - skips the ambient MemoMap entirely. - - Also documents the rule in `AGENTS.md` ("Per-request `Effect.provide(layer)` - must isolate its MemoMap") so the pattern is enforceable in code review and - caught early. Companion to the `provideOnRequestScope` MemoMap fix in - `@effect-app/infra`. - -## 4.0.0-beta.240 - -## 4.0.0-beta.239 - -## 4.0.0-beta.238 - -## 4.0.0-beta.237 - -## 4.0.0-beta.236 - -## 4.0.0-beta.235 - -### Patch Changes - -- ba4bdc3: improve: support .ts extension -- 6cfd83d: update effect to latest beta - -## 4.0.0-beta.234 - -## 4.0.0-beta.233 - -### Patch Changes - -- 57a1862: Update to effect 4.0.0-beta.67. Switch deps from `pkg.pr.new` snapshot back to npm beta tag. - -## 4.0.0-beta.232 - -### Patch Changes - -- 0263827: Fix `Date` / `DateValid` default helpers to pipe from their own schema. - - `withConstructorDefault` and `withDecodingDefaultType` on `DateValid` previously piped from `DateFromString`, dropping the `isDateValid()` check (and, after the recent identifier split, the `DateValid` annotations) from the resulting defaulted schema. Both `Date` and `DateValid` now use `extendM` so the helpers attach to the underlying schema (`DateFromString` / `DateValidFromString`). - -- 0263827: Distinguish `Date` and `DateValid` in JSON Schema output. - - - `Date` now emits identifier `DateOrInvalid` with description noting the value may be invalid. - - `DateValid` now emits its own annotated string (identifier `Date`) with description stating a valid ISO 8601 date is required. - -- 0263827: Update to effect `pkg.pr.new` snapshot at `a42ef66` (4.0.0-beta.66). Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). - -## 4.0.0-beta.231 - -### Patch Changes - -- 7ca66ce: Update to effect 4.0.0-beta.66. Remove `Yieldable` and `asEffect()` (service tags are now `Effect` directly). - -## 4.0.0-beta.230 - -## 4.0.0-beta.229 - -## 4.0.0-beta.228 - -## 4.0.0-beta.227 - -## 4.0.0-beta.226 - -## 4.0.0-beta.225 - -## 4.0.0-beta.224 - -## 4.0.0-beta.223 - -## 4.0.0-beta.222 - -## 4.0.0-beta.221 - -### Patch Changes - -- 439cbeb: Adopt module system from effect-smol: replace barrel imports with specific submodule imports (`import * as X from "effect-app/X"` / `import * as X from "effect/X"`). - -## 4.0.0-beta.220 - -## 4.0.0-beta.219 - -### Patch Changes - -- d738811: remove double prefix - -## 4.0.0-beta.218 - -## 4.0.0-beta.217 - -### Patch Changes - -- 0e824ef: add missing Brand - -## 4.0.0-beta.216 - -## 4.0.0-beta.215 - -## 4.0.0-beta.214 - -## 4.0.0-beta.213 - -## 4.0.0-beta.212 - -### Patch Changes - -- 8792221: Rename `withDefault` schema extensions to `withConstructorDefault` for clarity. - - Document that `.withConstructorDefault` is **construction-only** (applied during `.make(...)` when a field is omitted) and is **not** applied during decode — it cannot be used to JIT-migrate database fields. Per-property JSDoc on every `.withConstructorDefault` / `.withDecodingDefaultType` exposed by `Schema/ext.ts`, `Schema/numbers.ts`, `Schema/moreStrings.ts`, and `ids.ts` so the caveat is visible on hover. - - Re-export `withConstructorDefault`, `withDecodingDefault`, `withDecodingDefaultKey`, `withDecodingDefaultType`, and `withDecodingDefaultTypeKey` from `effect-app/Schema` with explicit JSDoc. `withDecodingDefault*` is discouraged for persisted data: a missing field may be data corruption rather than an old-shape document, and silently substituting a default hides the problem. Prefer an explicit, preferably versioned migration of database data over decode-time fallbacks. - -## 4.0.0-beta.211 - -## 4.0.0-beta.210 - -## 4.0.0-beta.209 - -### Patch Changes - -- c991be1: update packages -- 3bae238: Remove lodash type imports from utils.ts, replacing with native TypeScript equivalents. - -## 4.0.0-beta.208 - -## 4.0.0-beta.207 - -### Patch Changes - -- 8fffc3c: cleanup - -## 4.0.0-beta.206 - -### Patch Changes - -- 54bfc59: Require middleware to flow through `makeRpcClient` and the live layer through `makeRouter`. - - ### `makeRpcClient(middleware, generalErrors?)` - - Signature drops the `rcs` (request-context map wrapper) parameter. `rcs` was only load-bearing on the type side for `RequestConfig` inference; that information is now derived from `middleware.requestContextMap`. `middleware` is required — the previous "rcs + optional middleware" overload is gone. - - **Migration**: - - ```diff - -makeRpcClient(RequestContextMap, undefined, AppMiddleware) - +makeRpcClient(AppMiddleware) - ``` - - For tests/clients without a real middleware, build a minimal stub (`{ requestContextMap, requestContext }`) or pass any value satisfying `ClientMiddleware`. - - ### `makeRouter(middlewareLive)` - - `makeRouter()` no longer infers the live middleware layer from `meta.middleware.Default`. The Live layer is now passed explicitly to `makeRouter`, and the request classes only carry the middleware tag (schema-only). This decouples the router from any assumption that the middleware tag exposes a `Default` static. - - **Migration**: - - ```diff - -export const { Router, matchAll } = makeRouter() - +export const { Router, matchAll } = makeRouter(AppMiddleware.Default) - ``` - -## 4.0.0-beta.205 - -### Patch Changes - -- f313973: fix type make interface - -## 4.0.0-beta.204 - -### Patch Changes - -- 0a0030f: fix missing overloads - -## 4.0.0-beta.203 - -### Patch Changes - -- 992d9fa: fix more branded types - -## 4.0.0-beta.202 - -### Patch Changes - -- 1186b09: improve branded types, keep types through Rebuild - -## 4.0.0-beta.201 - -### Patch Changes - -- d67d17a: Source middleware errors exclusively from the rpc middleware tag, and move command/stream invalidation wrap/unwrap entirely into the routing layer (server) and `apiClientFactory` (client). `InvalidationMiddleware` and `InvalidationMiddlewareLive` are removed. - - ### Resource error schemas - - Three sites that used to fold `RequestContextMap[*].error` into a request's own error schema now stop doing so: - - - `makeRpcClient` / `makeRequestClass` — `failureSchema` is just `config.error` (still merged with the optional `generalErrors` parameter, which is the only remaining error mix on both type and runtime levels). - - `MiddlewareMaker.rpc()` — `error: options.error` only; the previous union with `rcm.config[*].error` is gone. - - Routing and `apiClientFactory.makeRpcGroupFromRequestsAndModuleName` — `Invalidation.makeCommandRpc` is called with `error: resource.error` (no widening with the composite middleware error union). - - Middleware errors reach the client through the rpc's `middlewares[*].error` failure-union channel of `Rpc.exitSchema`, exposed by attaching the middleware tag to the rpc on both sides: - - - **Server**: `makeRouter(middleware)` attaches the live composite tag (existing behavior). - - **Client**: new `middleware` option on `ClientForOptions` / `ApiClientFactory.makeFor(layer, { middleware })` attaches the same tag schema-only (no Live invoked). Threaded through `makeRpcGroupFromRequestsAndModuleName` to `RpcGroup.middleware(tag)`. Without it, stream rpcs (whose top-level `errorSchema` is forced to `Never` by effect-rpc) hit `SchemaError: Expected never | { _tag: "error", ... }` decoding middleware-thrown errors that bypass the in-stream `Stream.catch` wrap. - - **Migration**: handlers that yield errors previously sourced from rcm (e.g. `yield* new UnauthorizedError()`) now require those errors to be declared explicitly on the resource — `Req.Query()("...", fields, { success, error: UnauthorizedError })`. The handler error type no longer auto-includes the rcm union. - - ### Invalidation wrap/unwrap - - - `routing.ts` (server) provides a per-request `InvalidationSet` for commands, wraps the success value as `CommandResponseWithMetaData`, and converts handler-thrown failures into `CommandFailureWithMetaData` so accumulated invalidation keys reach the client on either path. Stream wrap (per-chunk envelope + final `done` chunk) was already in routing and is unchanged. - - `apiClientFactory.ts` (client) `unwrapCommand` strips both envelopes and forwards keys to `InvalidationKeysFromServer`. - - `InvalidationMiddleware` (the tag) and `InvalidationMiddlewareLive` (the layer) are **removed**. The middleware was the previous home of the wrap; with the wrap moved to routing/apiClientFactory, the middleware became a thin pass-through and is no longer needed. `DefaultGenericMiddlewares` and `DefaultGenericMiddlewaresLive` shrink accordingly — no migration needed for callers that used the defaults; callers that referenced `InvalidationMiddleware` / `InvalidationMiddlewareLive` directly should drop those imports. - - Middleware-thrown errors are never wrapped: by definition the handler never ran, so there is nothing to invalidate. They flow raw on the Cause and the client decodes them via the middleware-tag failure-union channel described above. - -## 4.0.0-beta.200 - -### Patch Changes - -- 8f1cf6a: able to configure schema concurrency -- 0cff7c1: workaround middleware error issue - -## 4.0.0-beta.199 - -## 4.0.0-beta.198 - -### Patch Changes - -- 32dbc54: fix stream type when no success specified - -## 4.0.0-beta.197 - -### Minor Changes - -- 3dc0d2a: Add streaming as a `stream: true` config option on `Query` / `Command` instead of a separate request type. - - `TaggedRequestFor` now exposes only `Query` and `Command` factories — the standalone `Stream` factory is removed. To produce a Stream of `success` values, pass `stream: true` in the request config. The request `type` field stays `"command" | "query"`; a new `stream: boolean` field carries the streaming flag (stripped from the stored handler config). - - ```ts - // Query that streams results - Req.Query()("Tag", {}, { stream: true, success: ... }) - - // Command that streams results - Req.Command()("Tag", {}, { stream: true, success: ... }) - ``` - - Vue client mapping (per-handler properties mirror the non-stream API — `.query`, `.fn`, `.mutate`): - - - `query` + `stream: true` → exposes `.query` (read-only streaming, tracked Vue Query). Helper map key: `${name}Query`. - - `command` + `stream: true` → exposes `.fn` and `.mutate` (mutating streaming). - - Plain `query` / `command` unchanged. - - Server routing dispatches via the new `stream` flag (`makeStreamRpc` for streaming commands/queries, `makeCommandRpc` / `Rpc.make` otherwise). - - Also lifts the `Struct` / `TaggedStruct` and `Opaque` definitions in `effect-app/Schema` to use `S.Bottom` / `S.Opaque` directly, exposing `fields`, `mapFields`, and a `MakeIn` that allows `void` when all fields are optional. `TaggedRequestFor` request classes now use `Opaque(TaggedStruct(...))` instead of `TaggedClass`, and decoding/encoding services are derived from `success` / `error` rather than stored on the request. - - **Migration**: replace `Req.Stream` with `Req.Query` or `Req.Command` and add `stream: true` to the config — `Query` for read-only streams, `Command` for mutating streams. - -## 4.0.0-beta.196 - -## 4.0.0-beta.195 - -### Patch Changes - -- 774a9b3: `MiddlewareMaker.makeMiddlewareBasic` now derives each middleware's effective error from both the static `error` field on the tag AND the `rcm` config entry referenced by `dynamic.key`, rather than relying on the static field alone. - - Middlewares declared with `dynamic: RequestContextMap.get("foo")` (instead of an explicit static `error: ...`) end up with `tag.error = Schema.Never` at runtime — `RpcMiddleware.Tag` defaults the static error to `Never` when not provided. The composite `MiddlewareMaker.Tag(...).middleware(...)` walked `make[*].error` to build its own error union, collapsing to `Union ≡ Never`. - - `Rpc.exitSchema` walks `rpc.middlewares[*].error` when building the wire-level failure union for every rpc kind. Empty-union meant middleware-thrown errors (`NotLoggedInError`, `UnauthorizedError`, etc.) never reached the wire schema. Query/command happened to work because their wire `errorSchema = resource.error` already covered the merge from `makeRpcClient`. Stream rpcs have `errorSchema` force-set to `Schema.Never` by effect-rpc, so the resource-level merge never reached the wire — middleware errors decoded as "Expected never, got X". - - Per middleware, the new logic pushes both the static `_.error` (if non-`Never`) and `rcm[_.dynamic.key].error` (if non-`Never`) into the composite's failure union. - -## 4.0.0-beta.194 - -## 4.0.0-beta.193 - -## 4.0.0-beta.192 - -## 4.0.0-beta.191 - -### Patch Changes - -- 50ce7e6: Replace typescript-eslint with oxlint-tsgolint for type-aware lint. Drop ESLint entirely from non-vue packages (cli, effect-app, infra) — they now use only `oxlint --type-aware`. Vue packages keep ESLint to run `@effect-app/no-await-effect` (no tsgolint equivalent) via `@typescript-eslint/parser` + `vue-eslint-parser`. - -## 4.0.0-beta.190 - -### Patch Changes - -- 985176b: Align request handler input typing with the request's `make` signature. Handlers are now classified as no-input only when the request schema declares no payload fields; any payload (even fully-optional) yields a function handler whose input matches `make`'s first parameter. Adds `HandlerInput` and threads it through `CommandFromRequest`. - -## 4.0.0-beta.189 - -### Patch Changes - -- ea32222: Update to effect 4.0.0-beta.60 and use native `Rpc.custom` constructors (`makeCommandRpc`, `makeStreamRpc`) for metadata-wrapped RPC schemas instead of manually wrapping/unwrapping schemas inline. - -## 4.0.0-beta.188 - -### Patch Changes - -- b2e438f: Remove Operations service and repo - -## 4.0.0-beta.187 - -### Patch Changes - -- 0d4e0b8: Fix `isGeneratorFunction` using `isObject` instead of `isFunction`: generator functions have `typeof === "function"`, not `"object"`, so the check always returned `false`. This caused `Command.streamFn` generator-form handlers to silently pass a raw `Generator` object rather than an `Effect`, meaning the mutation was never executed. - -## 4.0.0-beta.186 - -## 4.0.0-beta.185 - -## 4.0.0-beta.184 - -## 4.0.0-beta.183 - -## 4.0.0-beta.182 - -## 4.0.0-beta.181 - -### Patch Changes - -- 583393f: Default the stream `mutateStream` execute resolved value to the request's success type when no `final` schema is declared. - - Previously the type defaulted to `void`, but the runtime already resolves with the last emitted value. Types now match runtime behaviour: `execute` returns `Final` if a `final` schema is set, otherwise the success type. - -## 4.0.0-beta.180 - -### Minor Changes - -- 7fa3045: V1/V2/V3: stream and command requests carry invalidation metadata - - **V1** – stream final response includes metadata - - - `Invalidation.StreamResponseChunk` wraps each stream item as `{ _tag: "value", value }` and appends `{ _tag: "done", metadata }` at the end carrying all accumulated invalidation keys. - - **V2** – invalidation keys included in failures - - - `Invalidation.CommandFailureWithMetaData` and `Invalidation.StreamFailureChunk` carry keys accumulated up to the point of failure, so clients can invalidate queries even when a command or stream errors. - - `InvalidationMiddlewareLive` wraps command failures; `routing.ts` wraps stream failures. - - `apiClientFactory.ts` unwraps both on the client side, forwarding keys before re-failing with the original error. - - **V3** – mid-stream metadata chunks - - - `Invalidation.StreamResponseChunk` now also includes `{ _tag: "metadata", metadata }` for mid-stream invalidation. - - After each emitted value, the server drains accumulated keys and emits a "metadata" chunk if any keys were collected since the last drain (bucket reset via `InvalidationSet.drain`). - - `apiClientFactory.ts` processes "metadata" chunks the same as "done" chunks, forwarding keys to `InvalidationKeysFromServer` immediately. - - `makeInvalidationKeysService` accepts an optional `onAdded` callback that fires after each key addition, enabling `mutate.ts` to trigger query invalidation mid-stream without waiting for the stream to complete. - -## 4.0.0-beta.179 - -### Minor Changes - -- 828d264: Stream requests now support an optional `final` schema that models the final success type of the stream. When declared, `mutateStream`'s execute effect resolves with the last emitted value typed as `Final` instead of `void`. - - ```ts - class MyStream extends SomethingStream()( - "MyStream", - { id: S.String }, - { - success: S.Union([OperationProgress, ExportComplete]), - final: ExportComplete, // execute now resolves with ExportComplete - } - ) {} - ``` - -## 4.0.0-beta.178 - -## 4.0.0-beta.177 - -### Minor Changes - -- 89d8b3a: Add Effect RPC `Stream` support to the wrapper. - - - New `Stream` request constructor on `TaggedRequestFor` parallel to `Query`/`Command`. Emits resources with `type: "stream"`. - - Server router (`@effect-app/infra` `routing.ts`) accepts stream resources whose handlers return a `Stream.Stream` (or a function from input to one). Forwards `stream: true` to `Rpc.make` so `RpcSchema.Stream` wrapping is applied. Streams bypass `applyRequestTypeInterruptibility` and the `Effect.withSpan` wrapping (the RPC server adds its own span). - - Client (`apiClientFactory.ts`) detects stream resources, forwards `stream: true` when constructing `RpcGroup`, and exposes the per-request `handler` as a `Stream.Stream` (via `Stream.unwrap` over the `ManagedRuntime` context) instead of an `Effect`. `Invalidation.CommandResponseWithMetaData` continues to apply only to commands. - - New `RequestStreamHandler` / `RequestStreamHandlerWithInput` shapes in `clientFor.ts`; `RequestHandlers` dispatches on `type: "stream"`. - -## 4.0.0-beta.176 - -### Patch Changes - -- pass options - -## 4.0.0-beta.175 - -## 4.0.0-beta.174 - -### Minor Changes - -- 821468d: Add server-driven cache invalidation via RPC response headers. - - - `effect-app/rpc`: new `Invalidation` module with `InvalidationKey` / `InvalidationKeys` schemas, `Invalidates` annotation (for declaring static invalidation on Rpc definitions), `InvalidationSet` reference (request-scoped accumulator), and `makeInvalidationSet` helper. - - `effect-app/middleware`: new `InvalidationMiddleware` RPC middleware tag; included in `DefaultGenericMiddlewares`. - - `effect-app/client`: new `InvalidationKeys` module with `InvalidationKeysFromServer` reference and `makeInvalidationKeysService` helper; `apiClientFactory` now taps HTTP responses to read the `x-invalidate` header and forward keys to `InvalidationKeysFromServer`. - - `@effect-app/infra`: new `InvalidationMiddlewareLive` RPC middleware implementation that owns the full lifecycle — creates a request-scoped `InvalidationSet` (backed by a `Ref`), pre-populates it from the `Invalidates` annotation, provides it to the handler, and after the handler completes registers an HTTP pre-response handler (via `appendPreResponseHandlerUnsafe`) to write the accumulated keys as an `x-invalidate` response header. No separate HTTP middleware is needed. - - `@effect-app/vue`: `invalidateQueries` / `useMutation` now reads server-provided invalidation keys from `InvalidationKeysFromServer` after each mutation and applies them alongside the client-side invalidation. - -## 4.0.0-beta.173 - -## 4.0.0-beta.172 - -## 4.0.0-beta.171 - -### Patch Changes - -- d71d976: fix - -## 4.0.0-beta.170 - -### Patch Changes - -- 8f09f77: fix - -## 4.0.0-beta.169 - -### Patch Changes - -- 8ae8b53: input mess - -## 4.0.0-beta.168 - -### Patch Changes - -- 178480a: Fix request handler input classification to use request schema fields instead of `make` parameters, preventing defaulted/nullable input fields from being treated as no-input handlers. - -## 4.0.0-beta.167 - -### Patch Changes - -- 140e192: Relax invalidation resource value constraints to allow arbitrary values while preserving query-only filtering in invalidation handling. - -## 4.0.0-beta.166 - -### Patch Changes - -- dbcc53b: Refactor command invalidation typing: declare resources via `Command()`, pass `invalidatesQueries` as the optional 4th argument, and enforce exact `clientFor` invalidation resources when required. - -## 4.0.0-beta.165 - -### Minor Changes - -- f88ea34: Move `makeQueryKey` into `effect-app/client` and update Vue source and tests to import it from the shared client module. Vue still re-exports `makeQueryKey` from `src/lib` for compatibility. - -## 4.0.0-beta.164 - -### Minor Changes - -- 8cb3de4: Add command invalidation helpers that preserve query-only resource types and pass mutation input and `Exit` results into invalidation callbacks. Update Vue `clientFor` to merge request-level invalidation config with call-site invalidation and require matching invalidation resources. - -## 4.0.0-beta.163 - -### Patch Changes - -- b952f19: bye cruft - -## 4.0.0-beta.162 - -### Patch Changes - -- b52b424: restore annotations for now - -## 4.0.0-beta.161 - -## 4.0.0-beta.160 - -### Patch Changes - -- 505bfa9: Add concurrent decode helper APIs and migrate decode callsites to use them. - - - Add `withDefaultParseOptions` and keep `DefaultParseOptions` centralized. - - Export `decodeEffectConcurrently` and `decodeUnknownEffectConcurrently` from Schema and SchemaParser modules. - - Update repository, queue, client, form, and CLI decode paths to use concurrent decode helpers. - - Keep schema constructors free of hardcoded parse concurrency overrides. - -## 4.0.0-beta.159 - -### Patch Changes - -- c1e73de: - -## 4.0.0-beta.158 - -### Patch Changes - -- 3c1f52d: improve: class strictness enabled by default again, allow `strict: false` as opt out for now. -- 6ae3050: Preserve class annotation parseOptions in relaxed declaration struct decoding so custom parse options (including concurrency defaults) are applied consistently. - -## 4.0.0-beta.157 - -### Patch Changes - -- 6fff09c: unify encoded function for when you use encodedKeys - -## 4.0.0-beta.156 - -## 4.0.0-beta.155 - -### Patch Changes - -- c215db8: align TaggedUnion with array arg - -## 4.0.0-beta.154 - -## 4.0.0-beta.153 - -## 4.0.0-beta.152 - -## 4.0.0-beta.151 - -## 4.0.0-beta.150 - -### Patch Changes - -- 85a8275: Expose `make`, `makeOption`, and `makeEffect` static helpers on request classes created via `Query`/`Command`. - -## 4.0.0-beta.149 - -### Patch Changes - -- f317c5e: Preserve omitted-input make helpers through Schema.Opaque when the wrapped schema allows optional or default-only constructor input. - -## 4.0.0-beta.148 - -### Patch Changes - -- 199e9a5: Allow Struct and TaggedStruct make helpers to omit input when every constructor field is optional or defaulted, and preserve widening copy typings through a lighter named public type to improve TypeScript editor responsiveness. - -## 4.0.0-beta.147 - -### Patch Changes - -- 47e3742: Preserve Struct.copy through `annotate`, `annotateKey`, and `mapFields` chains, and add tests covering chained copy behavior on Struct schemas. - -## 4.0.0-beta.146 - -### Patch Changes - -- a4dff57: Adjust Struct and TaggedStruct copy typing to follow class-style widening constraints while keeping structural copy runtime behavior. - -## 4.0.0-beta.145 - -### Patch Changes - -- 12abb55: Refine schema copy behavior by keeping class copy constructor-based while using structural copy for Struct and TaggedStruct helpers. - -## 4.0.0-beta.144 - -### Patch Changes - -- 11422f8: Update request helper typing and runtime invocation to rely on schema `.make` instead of class constructors, avoiding `new`-based assumptions for request schemas. -- d31253f: Refactor eligible schema classes and tagged classes to Opaque schemas, and migrate constructor call sites to use `.make` for those models. - -## 4.0.0-beta.143 - -### Patch Changes - -- 79eb019: Remove redundant schema `title` annotations when they duplicate the schema `identifier`. - -## 4.0.0-beta.142 - -### Minor Changes - -- 025de47: Fold the encoded-override support from `ExtendedClass` and `ExtendedTaggedClass` into `Class`, `TaggedClass`, `ErrorClass`, and `TaggedErrorClass`, and update model codegen to detect the new second-generic form. - -### Patch Changes - -- 3436d44: Extend `Schema.Opaque` in `effect-app/Schema` to support an optional encoded-type generic while preserving the original single-generic behavior. - -## 4.0.0-beta.141 - -### Patch Changes - -- 7c25dbb: Add relaxed wrapper support for `ErrorClass` and `TaggedErrorClass` in `effect-app/Schema`, matching the existing class wrapper behavior (`copy`, cached `ast`, and unbounded parse concurrency). - -## 4.0.0-beta.140 - -## 4.0.0-beta.139 - -## 4.0.0-beta.138 - -## 4.0.0-beta.137 - -## 4.0.0-beta.136 - -## 4.0.0-beta.135 - -### Patch Changes - -- c7bbc41: fix - class should check type side - -## 4.0.0-beta.134 - -### Patch Changes - -- 9d3495e: Preserve field-level schema decode errors for relaxed Class and TaggedClass declarations so decode failures report nested constraints (for example min-length violations) instead of only a generic class-type mismatch. -- f353d48: Rename the Class/TaggedClass relaxed declaration option to `strict` (default `false`) and apply it to `Class`, `TaggedClass`, `ExtendedClass`, and `ExtendedTaggedClass`. - - When `strict: true`, class decoding keeps strict class-level declaration behavior; by default, decoding remains relaxed and preserves field-level schema errors. - -## 4.0.0-beta.133 - -### Patch Changes - -- c3299f7: update packages - -## 4.0.0-beta.132 - -## 4.0.0-beta.131 - -## 4.0.0-beta.130 - -### Patch Changes - -- ea1bd46: fix: prevent expansion of nominal brands - -## 4.0.0-beta.129 - -## 4.0.0-beta.128 - -### Patch Changes - -- 57db551: Split `TaggedRequestFor` into `Query` and `Command` factories, and mark generated request classes with `type: "query" | "command"`. - - Vue client helpers now expose query-only helpers (`query`, `suspense`, `fetch`) for query requests and mutation-only helpers (`mutate`, `fetch`) for command requests. - -## 4.0.0-beta.127 - -## 4.0.0-beta.126 - -### Patch Changes - -- 458bb1b: fix type - -## 4.0.0-beta.125 - -## 4.0.0-beta.124 - -### Patch Changes - -- 256ae85: cleanup - -## 4.0.0-beta.123 - -### Patch Changes - -- 14aba14: fix: clientFor - -## 4.0.0-beta.122 - -### Patch Changes - -- f052d38: Replace `(...) => Effect.gen` with `Effect.fnUntraced` in `apiClientFactory`. - -## 4.0.0-beta.121 - -### Patch Changes - -- 5ac46cb: Make `Schema.provide` dual (pipeable) - -## 4.0.0-beta.120 - -### Minor Changes - -- f21190c: Add `copy` property to `Class` and `TaggedClass` for creating modified instances with updated fields. The `copy` method is cached per-class and supports both object and function-based updates, with pipeline support. - -## 4.0.0-beta.119 - -## 4.0.0-beta.118 - -### Minor Changes - -- 08d2e70: Add `concurrency: "unbounded"` parseOptions annotation to Schema constructors (Struct, Array, NonEmptyArray, Record, TaggedStruct, ReadonlySet, ReadonlyMap, Class, TaggedClass) so all encode/decode operations automatically run with unbounded concurrency. Also override `mapFields` on Struct and Class/TaggedClass to preserve the annotation. - -### Patch Changes - -- bd26832: Add nested anyOf flattening to JSON Schema post-processing -- Update effect packages to 4.0.0-beta.52 - -## 4.0.0-beta.117 - -## 4.0.0-beta.116 - -## 4.0.0-beta.115 - -## 4.0.0-beta.114 - -## 4.0.0-beta.113 - -## 4.0.0-beta.112 - -## 4.0.0-beta.111 - -### Patch Changes - -- ca94edf: fix typo - -## 4.0.0-beta.110 - -## 4.0.0-beta.109 - -## 4.0.0-beta.108 - -### Patch Changes - -- 3e46e7b: allow excess props in openapi schemas - -## 4.0.0-beta.107 - -## 4.0.0-beta.106 - -## 4.0.0-beta.105 - -## 4.0.0-beta.104 - -### Minor Changes - -- e944bca: Add `generateGuards` and `generateGuardsFor` to `TaggedUnion` / `ExtendTaggedUnion` for property-scoped type guards. - - - `generateGuards("key")` — generic per-guard, no need to specify the container type - - `generateGuardsFor()("key")` — curried, fixes `A` for concrete guard signatures - - Both return `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. - -## 4.0.0-beta.103 - -### Minor Changes - -- 7119320: Add `generateGuards` to `TaggedUnion` / `ExtendTaggedUnion` for generating property-scoped type guards. - - Usage: `MyUnion.generateGuards()("propertyKey")` returns `{ is{Tag}, isAnyOf }` guards that narrow the container type by its tagged union property. - -## 4.0.0-beta.102 - -## 4.0.0-beta.101 - -## 4.0.0-beta.100 - -## 4.0.0-beta.99 - -## 4.0.0-beta.98 - -## 4.0.0-beta.97 - -## 4.0.0-beta.96 - -### Patch Changes - -- 5615e47: bs - -## 4.0.0-beta.95 - -### Patch Changes - -- 88838fb: Remove pick/omit customizations from Class/TaggedClass/Struct/TaggedStruct. Use `Struct.pick(X.fields, [...])` from `effect-app` instead. - -## 4.0.0-beta.94 - -## 4.0.0-beta.93 - -## 4.0.0-beta.92 - -## 4.0.0-beta.91 - -### Minor Changes - -- 738b482: Add pick/omit to Struct and TaggedStruct - -## 4.0.0-beta.90 - -## 4.0.0-beta.89 - -## 4.0.0-beta.88 - -## 4.0.0-beta.87 - -## 4.0.0-beta.86 - -## 4.0.0-beta.85 - -## 4.0.0-beta.84 - -## 4.0.0-beta.83 - -## 4.0.0-beta.82 - -## 4.0.0-beta.81 - -## 4.0.0-beta.80 - -## 4.0.0-beta.79 - -### Minor Changes - -- d16845e: Remove `TaggedRequest` from `makeRpcClient`, now only `TaggedRequestFor` is returned. Remove all legacy `meta.moduleName` support — `id` and `moduleName` are now required on `Req` type. Remove `makeRpcGroup` (use `makeRpcGroupFromRequestsAndModuleName` instead). - -## 4.0.0-beta.78 - -## 4.0.0-beta.77 - -### Minor Changes - -- 3613e87: Add `TaggedRequestFor` helper to `makeRpcClient` that curries a `moduleName`, producing request classes with static `id` and `moduleName` properties. This enables passing request classes directly to `makeQueryKey` without going through `clientFor` first. The `clientFor` function no longer requires a `meta` property on the module when requests carry `moduleName`. The meta codegen preset now generates `Req = TaggedRequestFor(moduleName)`. Original `TaggedRequest` remains for backwards compatibility. - -## 4.0.0-beta.76 - -### Patch Changes - -- a5248a9: use new schema features -- fac725d: update effect to latest beta - -## 4.0.0-beta.75 - -### Patch Changes - -- 24f0a5a: rename Literal to Literals, no longer hiding Schema's singular Literal. - -## 4.0.0-beta.74 - -### Patch Changes - -- 54ec1ef: fix bs - -## 4.0.0-beta.73 - -## 4.0.0-beta.72 - -### Patch Changes - -- 0541f0d: fix withDefault types - -## 4.0.0-beta.71 - -### Minor Changes - -- beae3a0: Remove `withDefaultConstructor` wrapper, use `S.withConstructorDefault` directly with `Effect.succeed`/`Effect.sync`. - -## 4.0.0-beta.70 - -## 4.0.0-beta.69 - -### Patch Changes - -- dc465e3: update to latest effect beta - -## 4.0.0-beta.68 - -### Patch Changes - -- e6f2341: Add SecretURL tests and switch secretURL config to use Config.nonEmptyString - -## 4.0.0-beta.67 - -## 4.0.0-beta.66 - -### Patch Changes - -- edc52e4: update - -## 4.0.0-beta.65 - -### Minor Changes - -- 1f103b2: Replace `proxify` with explicit service accessor helpers: `accessFn`, `accessEffectFn`, `accessCn`, `accessEffectCn`. - -### Patch Changes - -- 31739d7: Fix JSON schema output for Email, Date, PhoneNumber, and Url schemas. The `jsonSchema` annotation key is not recognized by Effect v4's JSON schema generator — use proper v4 annotations (`format`, `description`) and built-in checks (`isMinLength`, `isMaxLength`) instead. - -## 4.0.0-beta.64 - -### Patch Changes - -- c1a6fdc: fix proxify - -## 4.0.0-beta.63 - -## 4.0.0-beta.62 - -### Patch Changes - -- 0b21a02: fix: flatten simple allOf constraints in JSON Schema output for better codegen compatibility - -## 4.0.0-beta.61 - -## 4.0.0-beta.60 - -## 4.0.0-beta.59 - -### Patch Changes - -- cec026d: update packages - -## 4.0.0-beta.58 - -## 4.0.0-beta.57 - -## 4.0.0-beta.56 - -## 4.0.0-beta.55 - -## 4.0.0-beta.54 - -### Patch Changes - -- d867272: the return of `Context` - -## 4.0.0-beta.53 - -### Patch Changes - -- ee9694e: fix type issue? - -## 4.0.0-beta.52 - -### Minor Changes - -- 6252808: Add `ReadonlySetFromArray` and `ReadonlyMapFromArray` schema transformations that decode from annotated arrays to Set/Map. Update `ReadonlySet` and `ReadonlyMap` to use these internally. - -## 4.0.0-beta.51 - -## 4.0.0-beta.50 - -## 4.0.0-beta.49 - -### Patch Changes - -- e585c9c: fix config nested - -## 4.0.0-beta.48 - -### Patch Changes - -- 0c88f78: Remove `TaggedError` compatibility re-export, use `TaggedErrorClass` directly - -## 4.0.0-beta.47 - -### Patch Changes - -- 3365758: workaround effect error messages - -## 4.0.0-beta.46 - -## 4.0.0-beta.45 - -### Patch Changes - -- 10b55ff: update packages - -## 4.0.0-beta.44 - -### Patch Changes - -- a37aa38: Update to effect beta 43 - -## 4.0.0-beta.43 - -## 4.0.0-beta.42 - -## 4.0.0-beta.41 - -## 4.0.0-beta.40 - -## 4.0.0-beta.39 - -### Patch Changes - -- 10e90d5: fix: TaggedRequest should be an Opaque Schema Class - -## 4.0.0-beta.38 - -### Patch Changes - -- 0b3e00e: fix bug - -## 4.0.0-beta.37 - -### Patch Changes - -- 947fe20: fix input date - -## 4.0.0-beta.36 - -## 4.0.0-beta.35 - -## 4.0.0-beta.34 - -### Patch Changes - -- 8c645d5: update to latest effect - -## 4.0.0-beta.33 - -### Patch Changes - -- 4b95009: use Finite instead of Number - -## 4.0.0-beta.32 - -### Patch Changes - -- 01d862a: again - -## 4.0.0-beta.31 - -### Patch Changes - -- 6b6d601: fix ForceVOid - -## 4.0.0-beta.30 - -### Patch Changes - -- 5ec1f45: fix ForceVoid for real - -## 4.0.0-beta.29 - -### Patch Changes - -- a899d46: Replace `ForceVoid` in `makeClient` with a typed codec transformation instead of an unsafe cast. - -## 4.0.0-beta.28 - -### Patch Changes - -- 0099208: Refactor `Pure` helpers to use typed `useSync` calls instead of unsafe casts. - -## 4.0.0-beta.27 - -### Patch Changes - -- 601a1ff: update effect to 4.0.0-beta.37 and drop the Schema Class disableValidation workaround now that the patched effect schema covers it - -## 4.0.0-beta.26 - -### Patch Changes - -- 4da28e2: Change `Schema.Class` and `Schema.TaggedClass` wrappers to default constructor options to `{ disableValidation: true }`. - - This avoids strict class identifier validation by default when constructing wrapper classes (for example passing a compatible view class), while keeping existing behavior when explicit options are provided. - -## 4.0.0-beta.25 - -### Patch Changes - -- 6282d4b: Update `Effect.allLower` to call `svc.asEffect()` when available, ensuring service entries are normalized before `Effect.all` evaluation. - -## 4.0.0-beta.24 - -### Patch Changes - -- 32f71bf: Fix `Schema.TaggedUnion(...).tags` extraction for class-based members (for example `TaggedClass`) by using a local AST sentinel walker instead of relying on internal effect APIs. - - Add tests covering: - - - `TaggedUnion` with `encodeKeys`-wrapped members - - `TaggedUnion` with `TaggedClass` members - -## 4.0.0-beta.23 - -### Patch Changes - -- ea08a79: remove `fields` constraint from TaggedUnion/tags helpers, extract `_tag` via AST walk instead - -## 4.0.0-beta.22 - -## 4.0.0-beta.21 - -### Patch Changes - -- 0d349e7: Align effect-app tagged union helpers with Effect v4 by delegating to the native tagged union utilities, exposing v4-style `cases` and `guards`, and preserving the local `tags` helper. -- 75c72ee: update effect to 4.0.0-beta.36, adapt to Option revert from A | undefined - -## 4.0.0-beta.20 - -### Patch Changes - -- aa921db: Fix `TaggedRequest` no-config error type inference so requests without a third argument infer the same default error schema as requests with explicit success config. - -## 4.0.0-beta.19 - -### Patch Changes - -- ffc10a4: Migrate `Schema/ext` context helpers to Effect v4 by implementing `provide` and `contextFromServices` with proper service subtraction across decoding and encoding requirements. - -## 4.0.0-beta.18 - -### Patch Changes - -- e9dbbdd: Tighten schema helper generics in `Schema/ext.ts` so default-related helpers accept properly named schema type parameters instead of bare `S.Top` inputs. - -## 4.0.0-beta.17 - -### Patch Changes - -- c8a6a3f: fix TaggedUnion - -## 4.0.0-beta.16 - -### Patch Changes - -- 56db2c4: fix: isA - -## 4.0.0-beta.15 - -### Patch Changes - -- 20186b6: Migrate schema arbitrary annotations to Effect v4 `toArbitrary` and cover `StringId` generation with a fast-check test. - -## 4.0.0-beta.14 - -## 4.0.0-beta.13 - -### Patch Changes - -- 16de88c: remove hopefully obsolete Struct overrides -- 1c3c541: Update Effect dependencies to 4.0.0-beta.31 across workspace packages. -- de2359d: Update Effect dependencies to 4.0.0-beta.28 across workspace packages. -- 0d72f34: Update Effect dependencies to 4.0.0-beta.29 across workspace packages. - -## 4.0.0-beta.12 - -### Patch Changes - -- 622d54a: Configure Changesets fixed versioning for public packages. - -## 4.0.0-beta.10 - -### Patch Changes - -- 01c70d0: update all teh tings - -## 4.0.0-beta.9 - -### Patch Changes - -- 5727372: switch to NdJson - -## 4.0.0-beta.8 - -### Patch Changes - -- 1f336bc: fix RequestName - -## 4.0.0-beta.7 - -### Patch Changes - -- 62b4989: fix request attr - -## 4.0.0-beta.6 - -### Patch Changes - -- df75041: fix Req - -## 4.0.0-beta.5 - -### Patch Changes - -- 016c5a3: adapt isObject change - -## 4.0.0-beta.4 - -### Patch Changes - -- 88b90c3: fix withDefault - -## 4.0.0-beta.3 - -### Patch Changes - -- 3a7abae: fix bs - -## 4.0.0-beta.2 - -### Major Changes - -- 3887256: Fix Schema->Codec - -## 4.0.0-beta.1 - -### Patch Changes - -- 64786af: Beta25 - -## 4.0.0-beta.0 - -### Major Changes - -- 880df28: Effect v4 beta - -## 3.16.0 - -### Minor Changes - -- 1a102e2: update packages - -## 3.15.1 - -### Patch Changes - -- 049b54a: improve: add NEA support to Array.groupByT. - -## 3.15.0 - -### Minor Changes - -- ecbd35f: drop default proxy - -## 3.14.2 - -### Patch Changes - -- faf7e9d: add utils, fix bugs - -## 3.14.1 - -### Patch Changes - -- c5a2bd5: add dropUndefinedRecursive - -## 3.14.0 - -### Minor Changes - -- 9316017: feat: add `dropUndefinedT2` - -### Patch Changes - -- 358782e: update packages - -## 3.13.0 - -### Minor Changes - -- 07f1de5: update packages and tsconfigs - -## 3.12.0 - -### Minor Changes - -- 23ba90f: improve: compare deep structurally - -## 3.11.0 - -### Minor Changes - -- 9697a93: feat: Map withDefault - -## 3.10.2 - -### Patch Changes - -- fdf1781: enhance runtimeFiberAsPromise with abort. - -## 3.10.1 - -### Patch Changes - -- fe49192: improve literal default, overridable - -## 3.10.0 - -### Minor Changes - -- 51ded8d: add "withDefault" to Schema.Literal. - -## 3.9.0 - -### Minor Changes - -- b8f5e30: update packages - -## 3.8.1 - -### Patch Changes - -- 493fbb8: fix tagged class pick/omit omitting \_tag. - -## 3.8.1-next.0 - -### Patch Changes - -- fix tagged class pick/omit omitting \_tag. - -## 3.8.0 - -### Minor Changes - -- 3cb4c4a: simplify requests/client, drop raw for now, fix missing schema context in request handlers - -## 3.7.1 - -### Patch Changes - -- 4da7f79: cleanup helpers namespace - -## 3.7.0 - -### Minor Changes - -- 6d8c512: update packages - -## 3.6.1 - -### Patch Changes - -- d38a50a: align S.Class to original, adding missing make. - -## 3.6.0 - -### Minor Changes - -- 3d49d81: update packages - -## 3.5.0 - -### Minor Changes - -- 68e91c9: rename `name` to `id` - -## 3.4.0 - -### Minor Changes - -- 923f0d8: improve defaultFailureMessageHandler - -## 3.3.7 - -### Patch Changes - -- 4000c95: track const action name - -## 3.3.6 - -### Patch Changes - -- 8096576: new eslint shared config package - -## 3.3.5 - -### Patch Changes - -- 47e83ca: update cli - -## 3.3.4 - -### Patch Changes - -- f9aee77: some fixes - -## 3.3.3 - -### Patch Changes - -- f6a3328: export base eslint files so that we import them in projects - -## 3.3.2 - -### Patch Changes - -- f5da9d0: update packages - -## 3.3.1 - -### Patch Changes - -- d670d5a: runtimefiber errors should be wrapped - -## 3.3.0 - -### Minor Changes - -- d50dfe6: add support for RuntimeFiber, fix should return the Promise for eventOnSubmit. - -## 3.2.4 - -### Patch Changes - -- 8cb893c: dprint rules the world - -## 3.2.3 - -### Patch Changes - -- f101ef0: upgrade ea - -## 3.2.2 - -### Patch Changes - -- 33f100d: finalize new cli - -## 3.2.1 - -### Patch Changes - -- c12a49b: effect app cli to effect cli - -## 3.2.0 - -### Minor Changes - -- bd12e89: Disable Schema batching by default, not to run into unexpected endless waiting for resolving to finish. - -## 3.1.0 - -### Minor Changes - -- 1e4de39: remove namespace/type merging in Prelude. - -### Patch Changes - -- 41352d8: refactor: remove internal usage of namespace/type collission. - -## 3.0.10 - -### Patch Changes - -- ce91007: fix non inverted rpc context map use - -## 3.0.9 - -### Patch Changes - -- ea6380f: cleanup RpcContextMap - -## 3.0.8 - -### Patch Changes - -- 3543bfb: cleanup dev-mode - -## 3.0.7 - -### Patch Changes - -- d42afe6: cleanup - -## 3.0.6 - -### Patch Changes - -- 868eab6: cleanup - -## 3.0.5 - -### Patch Changes - -- 182ca78: improve: MiddlewareMaker is a Tag with Self so we can extend it from class. - -## 3.0.4 - -### Patch Changes - -- bc68d9f: feat: introduce Layer.make as alternative to Context.ProvideService - -## 3.0.3 - -### Patch Changes - -- c31423a: cleanup rpc - -## 3.0.2 - -### Patch Changes - -- 88bb989: align dynamic/global middleware with V4 - -## 3.0.1 - -### Patch Changes - -- 74c28d1: move common rpc out of infra - -## 3.0.0 - -### Major Changes - -- 3e4c45d: feat: Add new @effect/rpc Dynamic and Global Middleware, based on effect-app Router - -## 2.56.0 - -### Minor Changes - -- db2b575: switch to new HttpLayerRouter - -## 2.55.0 - -### Minor Changes - -- 37eb29a: improve ServiceBus api - -## 2.54.3 - -### Patch Changes - -- dd30471: improve - -## 2.54.2 - -### Patch Changes - -- 79ffd88: updates - -## 2.54.1 - -### Patch Changes - -- 9f90266: fix prefix namespace - -## 2.54.0 - -### Minor Changes - -- 0f2b2cf: update packages - -## 2.53.1 - -### Patch Changes - -- fa2c8d0: fix prefix - -## 2.53.0 - -### Minor Changes - -- 5c51597: refactor Middleware to run as RPC middleware - -## 2.52.1 - -### Patch Changes - -- 244f84d: various improvements, documentation - -## 2.52.0 - -### Minor Changes - -- 99cf15f: unify middleware api - -## 2.51.0 - -### Minor Changes - -- 54dd59a: cleanup NES - -## 2.50.2 - -### Patch Changes - -- 0d33085: fix dts freak - -## 2.50.1 - -### Patch Changes - -- build fix - -## 2.50.0 - -### Minor Changes - -- 0a82e7f: update packages - -## 2.49.0 - -### Minor Changes - -- a36ceed: feat: composable dynamic middleware - -## 2.48.0 - -### Minor Changes - -- 8bb246b: cleanup and document router/middleware - -## 2.47.2 - -### Patch Changes - -- fup - -## 2.47.1 - -### Patch Changes - -- fup - -## 2.47.0 - -### Minor Changes - -- d29cb1b: refactor: drop optional identifier for Schema classes. - -## 2.46.1 - -### Patch Changes - -- b095bb5: tscp - -## 2.46.0 - -### Minor Changes - -- ba4ad65: update packages; drop ServiceStrict in favour of language-service - -## 2.45.0 - -### Minor Changes - -- 2c217ce: update packages - -## 2.44.0 - -### Minor Changes - -- 55da9e7: improve: Router supports contextProvider through middleware, replacing flawed requestLayers - -## 2.43.3 - -### Patch Changes - -- b771967: update packages - -## 2.43.2 - -### Patch Changes - -- aec6960: fix complex ast tagged union - -## 2.43.1 - -### Patch Changes - -- a2e1e1e: improve jsonschema of Email - -## 2.43.0 - -### Minor Changes - -- eac9c67: update packages - -## 2.42.1 - -### Patch Changes - -- 49d93a5: update packages - -## 2.42.0 - -### Minor Changes - -- ba96171: improve Cosmos errors - -## 2.41.0 - -### Minor Changes - -- b1d4af4: housekeeping: update packages - -## 2.40.1 - -### Patch Changes - -- 0230385: improve: rpc spans - - fewer duplicate spans - - improve name - -## 2.40.0 - -### Minor Changes - -- 8cb33d5: update packages - -## 2.39.1 - -### Patch Changes - -- d3b05cb: update packages -- 4627c7b: improve: optimise getFirstById. -- 1e1f32b: housekeeping: update packages - -## 2.39.0 - -### Minor Changes - -- bdc9d5c: feat: add support for skippable namespaces in query keys - -## 2.38.5 - -### Patch Changes - -- 8b7fdbe: update packages - -## 2.38.4 - -### Patch Changes - -- revert copyOrigin changes - -## 2.38.3 - -### Patch Changes - -- update packages - -## 2.38.2 - -### Patch Changes - -- c0ed28a: preserve readonlyness in the returned type for copyOrigin -- 0fcab38: update packages - -## 2.38.1 - -### Patch Changes - -- cd15a8e: some fixes for copyOrigin - -## 2.38.0 - -### Minor Changes - -- 81479bf: add specialized copy function wrt a constructor - -## 2.37.1 - -### Patch Changes - -- 7bedb13: updates - -## 2.37.0 - -### Minor Changes - -- 14ea804: housekeeping: update packages - -## 2.36.1 - -### Patch Changes - -- 4c8dfb4: update packages - -## 2.36.0 - -### Minor Changes - -- e42d5fb: update to effect 3.14 with native rpc2 - -## 2.35.0 - -### Minor Changes - -- f38e31f: make LogLevels readonly - -### Patch Changes - -- f30b57e: fix faker and rebuild dts - -## 2.34.0 - -### Minor Changes - -- 64d3a54: refactor: replace custom FiberRefs with Context.Reference in preparation of Effect v4 - -### Patch Changes - -- 771ac45: update packages - -## 2.33.1 - -### Patch Changes - -- 36996ed: housekeeping: update packages - -## 2.33.0 - -### Minor Changes - -- 1e88a86: improve error handled marking - -## 2.32.3 - -### Patch Changes - -- 63e966a: update packages - -## 2.32.2 - -### Patch Changes - -- 96158a1: remove manual error headers/body gathering. - -## 2.32.1 - -### Patch Changes - -- a42d5a6: mark also number for brand - -## 2.32.0 - -### Minor Changes - -- bca03c7: feat: enhance Types module - -## 2.31.1 - -### Patch Changes - -- 24417c7: lol - -## 2.31.0 - -### Minor Changes - -- 9ebae95: improve: rpc error reporting - -## 2.30.0 - -### Minor Changes - -- 10a8adf: improve: lock rpc responses to 418/422/200, report otherwise as response error! helping with bad proxies in between. -- 8be2984: housekeeping: update packages - -## 2.29.0 - -### Minor Changes - -- 7a4d393: report errors anyway to Sentry but with lower severity. add log namespace annotations. - -## 2.28.0 - -### Minor Changes - -- 32730a4: improve error reporting - -## 2.27.0 - -### Minor Changes - -- 9b2e385: update packages - -## 2.26.3 - -### Patch Changes - -- 02c4141: fix type - -## 2.26.2 - -### Patch Changes - -- 411c3d5: fix type - -## 2.26.1 - -### Patch Changes - -- 709c9cc: fix missing \_tag on TaggedClass - -## 2.26.0 - -### Minor Changes - -- 2d0ad81: update packages - -## 2.25.0 - -### Minor Changes - -- f597755: feat: remove void check and force void conversion at runtime. - -## 2.24.3 - -### Patch Changes - -- 9d11dfb: housekeeping: update packages - -## 2.24.2 - -### Patch Changes - -- 42e6776: fix Map error - -## 2.24.1 - -### Patch Changes - -- 469f28d: fix: groupByT should support any key type -- ab9d1ea: housekeeping: update packages - -## 2.24.0 - -### Minor Changes - -- 3fa0eda: fix: stream error, change to nostream - -## 2.23.2 - -### Patch Changes - -- 1c3af52: fix HttpClient type - -## 2.23.1 - -### Patch Changes - -- 93e88c2: fix: export - -## 2.23.0 - -### Minor Changes - -- c8ebfb4: update packages - -## 2.22.0 - -### Minor Changes - -- 67023f8: kill \_cjs - -## 2.21.0 - -### Minor Changes - -- b1396c9: refactor: rename ApiClient to ApiClientFactory - -## 2.20.0 - -### Minor Changes - -- 3388736: feat: add `cause` support to errors - -### Patch Changes - -- 9f504c6: update packages - -## 2.19.5 - -### Patch Changes - -- 47eb94e: cleanup legacy annotations - -## 2.19.4 - -### Patch Changes - -- fix: return type - -## 2.19.3 - -### Patch Changes - -- 750b46a: update packages - -## 2.19.2 - -### Patch Changes - -- 1bfc3d8: update packages - -## 2.19.1 - -### Patch Changes - -- 5ef6481: update packages - -## 2.19.0 - -### Minor Changes - -- 29a1a65: update packages - -## 2.18.0 - -### Minor Changes - -- fe7783b: improve: TaggedUnion .is is now S.is, isA, is the old is - -## 2.17.0 - -### Minor Changes - -- 47a543b: remove transform-paths as default plugin -- d84aef7: update packages - -## 2.16.0 - -### Minor Changes - -- cc2c43e: update packages - -## 2.15.0 - -### Minor Changes - -- improve: clientFor as part of service so that cache is also fine. - -## 2.14.0 - -### Minor Changes - -- a30da33: improve: cleanup client legacy -- 9301abb: cleanup legacy - -## 2.13.3 - -### Patch Changes - -- 125e2ea: update packages - -## 2.13.2 - -### Patch Changes - -- 54f700f: fix: missing empty config - -## 2.13.1 - -### Patch Changes - -- 0feb32c: update packages - -## 2.13.0 - -### Minor Changes - -- bump - -## 2.12.0 - -### Minor Changes - -- 82363f6: add: Schema.contextFromServices - -## 2.11.2 - -### Patch Changes - -- c7c76b4: update packages - -## 2.11.1 - -### Patch Changes - -- c971d73: update packages - -## 2.11.0 - -### Minor Changes - -- update - -## 2.10.0 - -### Minor Changes - -- f19762b: update packages - -## 2.9.0 - -### Minor Changes - -- 565d678: improve: strict dependencies for routing and services. - -## 2.8.2 - -### Patch Changes - -- c2fa0ed: fix error schema - -## 2.8.1 - -### Patch Changes - -- 1e83e5c: fix type - -## 2.8.0 - -### Minor Changes - -- ba1c7fc: feat: add support for specifying success fields directly. - closes #102 - -### Patch Changes - -- 412cb3d: update packages - -## 2.7.0 - -### Minor Changes - -- 590e5bf: update packages - -## 2.6.0 - -### Minor Changes - -- d4ab73f: update packages - -## 2.5.2 - -### Patch Changes - -- 19ea035: update packages - -## 2.5.1 - -### Patch Changes - -- 724a1a8: error bs - -## 2.5.0 - -### Minor Changes - -- e95cab7: improve: also treat cosmos 409 error as OptimisticConcurrencyException - -## 2.4.0 - -### Minor Changes - -- d5623a6: update packages - -## 2.3.0 - -### Minor Changes - -- 9ef18f6: adapt Prelude workaround - -### Patch Changes - -- 9ef18f6: adapt Prelude workaround - -## 2.2.1 - -### Patch Changes - -- f68dbd2: update packages - -## 2.2.0 - -### Minor Changes - -- 02c3f55: refactor: restructure infra - -## 2.1.1 - -### Patch Changes - -- bs - -## 2.1.0 - -### Minor Changes - -- a34ba2a: add Schema.provide - -## 2.0.2 - -### Patch Changes - -- 25b154d: fix circular - -## 2.0.1 - -### Patch Changes - -- 39e4f3d: fix: missing export - -## 2.0.0 - -### Major Changes - -- 13abe12: refactor: merge core into prelude - -## 1.32.0 - -### Minor Changes - -- d0471b1: refactor: merge @effect-app/schema into core - -### Patch Changes - -- Updated dependencies [d0471b1] - - @effect-app/core@1.19.0 - -## 1.31.0 - -### Minor Changes - -- 3872061: migrate to effect/Schema - -### Patch Changes - -- Updated dependencies [3872061] - - @effect-app/schema@1.20.0 - - @effect-app/core@1.18.0 - -## 1.30.5 - -### Patch Changes - -- e836dcc: cleanup - -## 1.30.4 - -### Patch Changes - -- 0eac9da: improve: Operation error handling - -## 1.30.3 - -### Patch Changes - -- 4a814f5: improve error report - -## 1.30.2 - -### Patch Changes - -- cda1477: update packages -- Updated dependencies [cda1477] - - @effect-app/schema@1.19.1 - - @effect-app/core@1.17.1 - -## 1.30.1 - -### Patch Changes - -- cleanup - -## 1.30.0 - -### Minor Changes - -- c069b8e: add WithOptionalSpan - -## 1.29.0 - -### Minor Changes - -- 3d4e45a: cleanup FiberSets - -## 1.28.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.19.0 - - @effect-app/core@1.17.0 - -## 1.27.0 - -### Minor Changes - -- 9ddb31b: update packages - -### Patch Changes - -- Updated dependencies [9ddb31b] - - @effect-app/schema@1.18.0 - - @effect-app/core@1.16.0 - -## 1.26.8 - -### Patch Changes - -- 0ea3023: drop pipe ext, should've known -- Updated dependencies [0ea3023] - - @effect-app/core@1.15.8 - - @effect-app/schema@1.17.8 - -## 1.26.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.7 - - @effect-app/schema@1.17.7 - -## 1.26.6 - -### Patch Changes - -- Updated dependencies [c57fd1b] - - @effect-app/core@1.15.6 - - @effect-app/schema@1.17.6 - -## 1.26.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.5 - - @effect-app/schema@1.17.5 - -## 1.26.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.4 - - @effect-app/schema@1.17.4 - -## 1.26.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@1.15.3 - - @effect-app/schema@1.17.3 - -## 1.26.2 - -### Patch Changes - -- Updated dependencies [56b764d] - - @effect-app/core@1.15.2 - - @effect-app/schema@1.17.2 - -## 1.26.1 - -### Patch Changes - -- Updated dependencies [56df0f3] - - @effect-app/core@1.15.1 - - @effect-app/schema@1.17.1 - -## 1.26.0 - -### Minor Changes - -- 0effd97: update packages - -### Patch Changes - -- Updated dependencies [0effd97] - - @effect-app/schema@1.17.0 - - @effect-app/core@1.15.0 - -## 1.25.0 - -### Minor Changes - -- a2ac359: add pipe extensions experiment - -### Patch Changes - -- Updated dependencies [a2ac359] - - @effect-app/core@1.14.0 - - @effect-app/schema@1.16.2 - -## 1.24.1 - -### Patch Changes - -- Updated dependencies [61eb517] - - @effect-app/core@1.13.1 - - @effect-app/schema@1.16.1 - -## 1.24.0 - -### Minor Changes - -- b9b64b8: housekeeping: update packages - -### Patch Changes - -- Updated dependencies [b9b64b8] - - @effect-app/schema@1.16.0 - - @effect-app/core@1.13.0 - -## 1.23.1 - -### Patch Changes - -- 16bc3b2: fix typo - -## 1.23.0 - -### Minor Changes - -- 67a202a: fix location - -## 1.22.1 - -### Patch Changes - -- fup - -## 1.22.0 - -### Minor Changes - -- c2487d4: remove legacy FetchResponse - -## 1.21.1 - -### Patch Changes - -- 66086f3: f - -## 1.21.0 - -### Minor Changes - -- cleanup all legacy router cruft - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.15.0 - -## 1.20.0 - -### Minor Changes - -- 65dcc73: update packages - -### Patch Changes - -- Updated dependencies [65dcc73] - - @effect-app/schema@1.14.0 - - @effect-app/core@1.12.0 - -## 1.19.1 - -### Patch Changes - -- supply layer - -## 1.19.0 - -### Minor Changes - -- 4d2b804: housekeeping: update packages - -### Patch Changes - -- Updated dependencies [4d2b804] - - @effect-app/schema@1.13.0 - - @effect-app/core@1.11.0 - -## 1.18.0 - -### Minor Changes - -- b670052: feat: followup rpc client side - -## 1.17.5 - -### Patch Changes - -- 447cba4: fix paths - -## 1.17.4 - -### Patch Changes - -- 550ba43: control log levels -- Updated dependencies [550ba43] - - @effect-app/core@1.10.2 - - @effect-app/schema@1.12.2 - -## 1.17.3 - -### Patch Changes - -- 6a3e66a: feat: expose tags on TaggedUnion - -## 1.17.2 - -### Patch Changes - -- 13b7aa1: fix: TaggedUnion should inherit the rest of Union - -## 1.17.1 - -### Patch Changes - -- 38c0e65: cleanup - -## 1.17.0 - -### Minor Changes - -- 190b139: update packages - -## 1.16.1 - -### Patch Changes - -- c710953: update packagesy -- Updated dependencies [3de8bfb] - - @effect-app/core@1.10.1 - - @effect-app/schema@1.12.1 - -## 1.16.0 - -### Minor Changes - -- 4bc6e99: update packages -- 1018e58: update packages - -### Patch Changes - -- Updated dependencies [4bc6e99] -- Updated dependencies [1018e58] - - @effect-app/schema@1.12.0 - - @effect-app/core@1.10.0 - -## 1.15.5 - -### Patch Changes - -- d9fd446: fix type - -## 1.15.4 - -### Patch Changes - -- 70c6222: improve doc - -## 1.15.3 - -### Patch Changes - -- 97a0bf8: fix client type - -## 1.15.2 - -### Patch Changes - -- fb62f02: bwc - -## 1.15.1 - -### Patch Changes - -- 2b56d7e: cleanup routing -- 73d5e0a: cleanup routing - -## 1.15.0 - -### Minor Changes - -- feat: add new router support - -## 1.14.0 - -### Minor Changes - -- b3d9a3e: update packages - -### Patch Changes - -- Updated dependencies [b3d9a3e] - - @effect-app/schema@1.11.0 - - @effect-app/core@1.9.0 - -## 1.13.4 - -### Patch Changes - -- d08286c: update packages -- Updated dependencies [d08286c] - - @effect-app/schema@1.10.1 - - @effect-app/core@1.8.1 - -## 1.13.3 - -### Patch Changes - -- 6c81e3c: improve: remove framework stacktrace location - -## 1.13.2 - -### Patch Changes - -- a64695e: fix runtime error - -## 1.13.1 - -### Patch Changes - -- e5fc429: fix types - -## 1.13.0 - -### Minor Changes - -- c5f5223: update packages - -### Patch Changes - -- Updated dependencies [c5f5223] - - @effect-app/schema@1.10.0 - - @effect-app/core@1.8.0 - -## 1.12.5 - -### Patch Changes - -- Updated dependencies [5cb4b35] - - @effect-app/schema@1.9.5 - -## 1.12.4 - -### Patch Changes - -- Updated dependencies [0151436] - - @effect-app/schema@1.9.4 - -## 1.12.3 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/schema@1.9.3 - - @effect-app/core@1.7.5 - -## 1.12.2 - -### Patch Changes - -- e6eecd2: update packages -- Updated dependencies [e6eecd2] - - @effect-app/schema@1.9.2 - - @effect-app/core@1.7.4 - -## 1.12.1 - -### Patch Changes - -- ff -- Updated dependencies - - @effect-app/core@1.7.3 - - @effect-app/schema@1.9.1 - -## 1.12.0 - -### Minor Changes - -- 056f293: fix type - -### Patch Changes - -- Updated dependencies [056f293] - - @effect-app/schema@1.9.0 - -## 1.11.3 - -### Patch Changes - -- fix -- Updated dependencies - - @effect-app/core@1.7.2 - - @effect-app/schema@1.8.1 - -## 1.11.2 - -### Patch Changes - -- 65135b2: fix issue - -## 1.11.1 - -### Patch Changes - -- c229a73: add tagMap to TaggedUnion - -## 1.11.0 - -### Minor Changes - -- 24f0c0f: feat: allow custom errors per endpoint. - -### Patch Changes - -- Updated dependencies [24f0c0f] - - @effect-app/schema@1.8.0 - -## 1.10.2 - -### Patch Changes - -- 1ac74ec: cleanup tsconfig base -- 0b399ce: update packages -- Updated dependencies [0b399ce] - - @effect-app/schema@1.7.1 - - @effect-app/core@1.7.1 - -## 1.10.1 - -### Patch Changes - -- 9fbf129: fix: messagefallback - -## 1.10.0 - -### Minor Changes - -- 2143dd9: update packages - -### Patch Changes - -- Updated dependencies [2143dd9] - - @effect-app/schema@1.7.0 - - @effect-app/core@1.7.0 - -## 1.9.2 - -### Patch Changes - -- e120966: update packages -- f6066ac: update packages -- Updated dependencies [e120966] -- Updated dependencies [f6066ac] - - @effect-app/schema@1.6.1 - - @effect-app/core@1.6.1 - -## 1.9.1 - -### Patch Changes - -- 26551ce: improve rendering - -## 1.9.0 - -### Minor Changes - -- 79f58af: update packages - -### Patch Changes - -- Updated dependencies [79f58af] - - @effect-app/schema@1.6.0 - - @effect-app/core@1.6.0 - -## 1.8.2 - -### Patch Changes - -- b3ebc41: update packages -- Updated dependencies [b3ebc41] - - @effect-app/schema@1.5.2 - - @effect-app/core@1.5.2 - -## 1.8.1 - -### Patch Changes - -- f569681: update packages -- Updated dependencies [f569681] - - @effect-app/schema@1.5.1 - - @effect-app/core@1.5.1 - -## 1.8.0 - -### Minor Changes - -- d12fc1a: improve: expose interrupt, not waitUntilEmpty on FiberSets - -## 1.7.0 - -### Minor Changes - -- 613c148: update packages - -### Patch Changes - -- Updated dependencies [613c148] - - @effect-app/schema@1.5.0 - - @effect-app/core@1.5.0 - -## 1.6.0 - -### Minor Changes - -- 8d0c511: update packages - -### Patch Changes - -- Updated dependencies [8d0c511] - - @effect-app/schema@1.4.0 - - @effect-app/core@1.4.0 - -## 1.5.2 - -### Patch Changes - -- Updated dependencies [b48c630] - - @effect-app/schema@1.3.1 - -## 1.5.1 - -### Patch Changes - -- 8438eed: fix ConfigSecretURL - -## 1.5.0 - -### Minor Changes - -- 9cc6f86: update packages - -### Patch Changes - -- Updated dependencies [9cc6f86] - - @effect-app/schema@1.3.0 - - @effect-app/core@1.3.0 - -## 1.4.8 - -### Patch Changes - -- fe2754a: improve: decode span -- 349c421: Update packages -- Updated dependencies [349c421] - - @effect-app/schema@1.2.8 - - @effect-app/core@1.2.3 - -## 1.4.7 - -### Patch Changes - -- 744cf0f: add client.response span - -## 1.4.6 - -### Patch Changes - -- 9d944a1: update packages -- Updated dependencies [9d944a1] - - @effect-app/schema@1.2.7 - - @effect-app/core@1.2.2 - -## 1.4.5 - -### Patch Changes - -- a67d115: improve client trace - -## 1.4.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.2.6 - -## 1.4.3 - -### Patch Changes - -- 24c3fe9: align brands -- Updated dependencies [24c3fe9] - - @effect-app/schema@1.2.5 - -## 1.4.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.2.4 - -## 1.4.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.2.3 - -## 1.4.0 - -### Minor Changes - -- c10818c: improve forking - -### Patch Changes - -- 2c661c1: update packages -- Updated dependencies [2c661c1] - - @effect-app/schema@1.2.2 - - @effect-app/core@1.2.1 - -## 1.3.1 - -### Patch Changes - -- Updated dependencies [92f303f] - - @effect-app/schema@1.2.1 - -## 1.3.0 - -### Minor Changes - -- 9ac9416: update packages - -### Patch Changes - -- Updated dependencies [9ac9416] - - @effect-app/schema@1.2.0 - - @effect-app/core@1.2.0 - -## 1.2.5 - -### Patch Changes - -- revert - -## 1.2.4 - -### Patch Changes - -- 6335139: allow error - -## 1.2.3 - -### Patch Changes - -- 3411e78: update packages -- Updated dependencies [3411e78] - - @effect-app/schema@1.1.3 - - @effect-app/core@1.1.3 - -## 1.2.2 - -### Patch Changes - -- 128f636: update packages -- Updated dependencies [128f636] - - @effect-app/schema@1.1.2 - - @effect-app/core@1.1.2 - -## 1.2.1 - -### Patch Changes - -- Updated dependencies [132bbc4] - - @effect-app/core@1.1.1 - - @effect-app/schema@1.1.1 - -## 1.2.0 - -### Minor Changes - -- 2e29520: update packages - -### Patch Changes - -- Updated dependencies [2e29520] - - @effect-app/schema@1.1.0 - - @effect-app/core@1.1.0 - -## 1.1.1 - -### Patch Changes - -- 4dba4a5: add annotateSpanWithError - -## 1.1.0 - -### Minor Changes - -- b8b729e: adopt @effect-rx - -## 1.0.12 - -### Patch Changes - -- c99cdbb: update packages -- Updated dependencies [c99cdbb] - - @effect-app/schema@1.0.10 - - @effect-app/core@1.0.6 - -## 1.0.11 - -### Patch Changes - -- feat: improve error reported state. - -## 1.0.10 - -### Patch Changes - -- Updated dependencies [f202812] - - @effect-app/schema@1.0.9 - -## 1.0.9 - -### Patch Changes - -- 83f19e1: schema next minor -- Updated dependencies [83f19e1] - - @effect-app/schema@1.0.8 - -## 1.0.8 - -### Patch Changes - -- 86d33e8: add error reporter for vue - -## 1.0.7 - -### Patch Changes - -- c4dbb6c: update packages -- Updated dependencies [c4dbb6c] - - @effect-app/schema@1.0.7 - - @effect-app/core@1.0.5 - -## 1.0.6 - -### Patch Changes - -- 93f53d4: update packages -- Updated dependencies [93f53d4] - - @effect-app/schema@1.0.6 - - @effect-app/core@1.0.4 - -## 1.0.5 - -### Patch Changes - -- Updated dependencies [9c69b5b] - - @effect-app/core@1.0.3 - - @effect-app/schema@1.0.5 - -## 1.0.4 - -### Patch Changes - -- Updated dependencies [6363ff5] - - @effect-app/core@1.0.2 - - @effect-app/schema@1.0.4 - -## 1.0.3 - -### Patch Changes - -- bf702ba: update packages -- Updated dependencies [bf702ba] - - @effect-app/schema@1.0.3 - - @effect-app/core@1.0.1 - -## 1.0.2 - -### Patch Changes - -- Updated dependencies [156664e] - - @effect-app/schema@1.0.2 - -## 1.0.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@1.0.1 - -## 1.0.0 - -### Major Changes - -- 4e9992f: effect 3.0! - -### Patch Changes - -- Updated dependencies [4e9992f] - - @effect-app/schema@1.0.0 - - @effect-app/core@1.0.0 - -## 0.204.0 - -### Minor Changes - -- version prob - -## 0.203.1 - -### Patch Changes - -- ff5d466: cleanup Struct type -- Updated dependencies [ff5d466] - - @effect-app/schema@0.221.1 - -## 0.203.0 - -### Minor Changes - -- 0d1b378: update to latest schema - -### Patch Changes - -- Updated dependencies [0d1b378] - - @effect-app/schema@0.221.0 - - @effect-app/core@0.170.0 - -## 0.202.1 - -### Patch Changes - -- 45770ea: re-add MainFiberSet join logging - -## 0.202.0 - -### Minor Changes - -- c0a2bb1: rename FiberBag to MainFiberSet - -## 0.201.6 - -### Patch Changes - -- 4a242e9: use built in FiberSet.join - -## 0.201.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.169.1 - - @effect-app/schema@0.220.4 - -## 0.201.4 - -### Patch Changes - -- 36fb254: fix emptyto check - -## 0.201.3 - -### Patch Changes - -- Updated dependencies [0ad9ea9] - - @effect-app/schema@0.220.3 - -## 0.201.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.220.2 - -## 0.201.1 - -### Patch Changes - -- Updated dependencies [89bdcf0] - - @effect-app/schema@0.220.1 - -## 0.201.0 - -### Minor Changes - -- 290a709: update packages - -### Patch Changes - -- Updated dependencies [290a709] - - @effect-app/schema@0.220.0 - - @effect-app/core@0.169.0 - -## 0.200.0 - -### Minor Changes - -- 564a95c: fix branded schemas - -### Patch Changes - -- Updated dependencies [564a95c] - - @effect-app/schema@0.219.0 - -## 0.199.0 - -### Minor Changes - -- ecb05f6: update packages -- fbfa538: add title to Operations and expose all - -### Patch Changes - -- Updated dependencies [ecb05f6] - - @effect-app/schema@0.218.0 - - @effect-app/core@0.168.0 - -## 0.198.2 - -### Patch Changes - -- Updated dependencies [7b1ff1c] - - @effect-app/core@0.167.0 - - @effect-app/schema@0.217.2 - -## 0.198.1 - -### Patch Changes - -- Updated dependencies [e165c07] - - @effect-app/schema@0.217.1 - -## 0.198.0 - -### Minor Changes - -- 01f8283: cleanup: drop legacy `FromClass` - -### Patch Changes - -- Updated dependencies [01f8283] - - @effect-app/schema@0.217.0 - -## 0.197.0 - -### Minor Changes - -- revert form changes - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.166.0 - - @effect-app/schema@0.216.0 - -## 0.196.2 - -### Patch Changes - -- Updated dependencies [2044733] - - @effect-app/core@0.165.0 - - @effect-app/schema@0.215.2 - -## 0.196.1 - -### Patch Changes - -- Updated dependencies [a9a81a1] - - @effect-app/core@0.164.0 - - @effect-app/schema@0.215.1 - -## 0.196.0 - -### Minor Changes - -- 7a3f05c: update packages - -### Patch Changes - -- Updated dependencies [7a3f05c] - - @effect-app/core@0.163.0 - - @effect-app/schema@0.215.0 - -## 0.195.0 - -### Minor Changes - -- bbaa558: bs - -### Patch Changes - -- Updated dependencies [bbaa558] - - @effect-app/core@0.162.0 - - @effect-app/schema@0.214.0 - -## 0.194.1 - -### Patch Changes - -- Updated dependencies [ac37ff7] - - @effect-app/fluent-extensions@0.155.1 - - @effect-app/core@0.161.1 - - @effect-app/schema@0.213.1 - -## 0.194.0 - -### Minor Changes - -- e5b0d8e: update packages -- 2e2fa98: update packages -- 44ef913: update packages - -### Patch Changes - -- Updated dependencies [e5b0d8e] -- Updated dependencies [2e2fa98] -- Updated dependencies [44ef913] - - @effect-app/fluent-extensions@0.155.0 - - @effect-app/schema@0.213.0 - - @effect-app/core@0.161.0 - -## 0.193.0 - -### Minor Changes - -- 494842c: update packages - -### Patch Changes - -- Updated dependencies [494842c] - - @effect-app/fluent-extensions@0.154.0 - - @effect-app/schema@0.212.0 - - @effect-app/core@0.160.0 - -## 0.192.1 - -### Patch Changes - -- Updated dependencies [413bdee] - - @effect-app/core@0.159.1 - - @effect-app/schema@0.211.2 - -## 0.192.0 - -### Minor Changes - -- 215071f: cleanup TagClass -- af56c32: Integrate Service in Context - -### Patch Changes - -- Updated dependencies [af56c32] - - @effect-app/core@0.159.0 - - @effect-app/schema@0.211.1 - -## 0.191.0 - -### Minor Changes - -- cb84068: replace lodash with builtin and cleanup - -### Patch Changes - -- Updated dependencies [cb84068] - - @effect-app/fluent-extensions@0.153.0 - - @effect-app/schema@0.211.0 - - @effect-app/core@0.158.0 - -## 0.190.0 - -### Minor Changes - -- 790bfc2: cleanup - -### Patch Changes - -- Updated dependencies [790bfc2] - - @effect-app/fluent-extensions@0.152.0 - - @effect-app/core@0.157.0 - - @effect-app/schema@0.210.2 - -## 0.189.1 - -### Patch Changes - -- 0334285: update packages -- Updated dependencies [0334285] -- Updated dependencies [b9f35eb] - - @effect-app/fluent-extensions@0.151.0 - - @effect-app/schema@0.210.1 - - @effect-app/core@0.156.1 - -## 0.189.0 - -### Minor Changes - -- 498deb0: update packages - -### Patch Changes - -- Updated dependencies [498deb0] - - @effect-app/fluent-extensions@0.150.0 - - @effect-app/schema@0.210.0 - - @effect-app/core@0.156.0 - -## 0.188.0 - -### Minor Changes - -- fd8f019: update packages - -### Patch Changes - -- Updated dependencies [fd8f019] - - @effect-app/fluent-extensions@0.149.0 - - @effect-app/schema@0.209.0 - - @effect-app/core@0.155.0 - -## 0.187.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.208.1 - -## 0.187.0 - -### Minor Changes - -- 51253a2: update packages - -### Patch Changes - -- Updated dependencies [51253a2] - - @effect-app/fluent-extensions@0.148.0 - - @effect-app/schema@0.208.0 - - @effect-app/core@0.154.0 - -## 0.186.2 - -### Patch Changes - -- 0a0b8e9: add ServiceUnavailableError - -## 0.186.1 - -### Patch Changes - -- Updated dependencies [df2a290] - - @effect-app/schema@0.207.1 - -## 0.186.0 - -### Minor Changes - -- f2c3553: update packages - -### Patch Changes - -- Updated dependencies [f2c3553] - - @effect-app/fluent-extensions@0.147.0 - - @effect-app/schema@0.207.0 - - @effect-app/core@0.153.0 - -## 0.185.8 - -### Patch Changes - -- Updated dependencies [2b53680] - - @effect-app/schema@0.206.7 - -## 0.185.7 - -### Patch Changes - -- Updated dependencies [2794b0a] - - @effect-app/schema@0.206.6 - -## 0.185.6 - -### Patch Changes - -- Updated dependencies [41bec85] - - @effect-app/schema@0.206.5 - -## 0.185.5 - -### Patch Changes - -- Updated dependencies [511d81b] - - @effect-app/schema@0.206.4 - -## 0.185.4 - -### Patch Changes - -- df3a550: fup - -## 0.185.3 - -### Patch Changes - -- f385129: fix legacy struct use -- Updated dependencies [f385129] - - @effect-app/schema@0.206.3 - -## 0.185.2 - -### Patch Changes - -- Updated dependencies [fe910fe] - - @effect-app/schema@0.206.2 - -## 0.185.1 - -### Patch Changes - -- Updated dependencies [dbc7c83] - - @effect-app/schema@0.206.1 - -## 0.185.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.146.0 - - @effect-app/schema@0.206.0 - - @effect-app/core@0.152.1 - -## 0.184.0 - -### Minor Changes - -- 5178025: update packages - -### Patch Changes - -- Updated dependencies [5178025] - - @effect-app/fluent-extensions@0.145.0 - - @effect-app/schema@0.205.0 - - @effect-app/core@0.152.0 - -## 0.183.2 - -### Patch Changes - -- c7b8007: bag it - -## 0.183.1 - -### Patch Changes - -- Updated dependencies [58233dd] - - @effect-app/core@0.151.1 - - @effect-app/schema@0.204.1 - -## 0.183.0 - -### Minor Changes - -- 77366cc: don't go through service constructor - -## 0.182.1 - -### Patch Changes - -- f188543: update accessor shape - -## 0.182.0 - -### Minor Changes - -- d0a1774: update ts to 5.4.1-rc - -### Patch Changes - -- Updated dependencies [d0a1774] - - @effect-app/fluent-extensions@0.144.0 - - @effect-app/schema@0.204.0 - - @effect-app/core@0.151.0 - -## 0.181.8 - -### Patch Changes - -- Updated dependencies [ce64bc5] - - @effect-app/schema@0.203.4 - -## 0.181.7 - -### Patch Changes - -- Updated dependencies [bbf24a6] - - @effect-app/schema@0.203.3 - -## 0.181.6 - -### Patch Changes - -- Updated dependencies [e12b4f8] - - @effect-app/schema@0.203.2 - -## 0.181.5 - -### Patch Changes - -- Updated dependencies [09bc0d5] - - @effect-app/schema@0.203.1 - -## 0.181.4 - -### Patch Changes - -- Updated dependencies [e355a45] - - @effect-app/schema@0.203.0 - -## 0.181.3 - -### Patch Changes - -- Updated dependencies [f4bea89] - - @effect-app/fluent-extensions@0.143.4 - - @effect-app/core@0.150.5 - - @effect-app/schema@0.202.5 - -## 0.181.2 - -### Patch Changes - -- Updated dependencies [ea3dd75] - - @effect-app/fluent-extensions@0.143.3 - - @effect-app/core@0.150.4 - - @effect-app/schema@0.202.4 - -## 0.181.1 - -### Patch Changes - -- 579f42d: fix typo - -## 0.181.0 - -### Minor Changes - -- 752958a: awesome tag - -## 0.180.10 - -### Patch Changes - -- abb5332: add ServiceShape - -## 0.180.9 - -### Patch Changes - -- Updated dependencies [cef7a30] - - @effect-app/fluent-extensions@0.143.2 - - @effect-app/core@0.150.3 - - @effect-app/schema@0.202.3 - -## 0.180.8 - -### Patch Changes - -- 190415c: fix - -## 0.180.7 - -### Patch Changes - -- 6ece717: base FiberBag on FiberSet - -## 0.180.6 - -### Patch Changes - -- dba2711: fix generic - -## 0.180.5 - -### Patch Changes - -- e470bd5: add forkDaemon and forkScoped in FiberBag. - -## 0.180.4 - -### Patch Changes - -- Updated dependencies [81f152f] - - @effect-app/core@0.150.2 - - @effect-app/schema@0.202.2 - -## 0.180.3 - -### Patch Changes - -- add fiberbag - -## 0.180.2 - -### Patch Changes - -- Updated dependencies [ef4b0ba] - - @effect-app/fluent-extensions@0.143.1 - - @effect-app/core@0.150.1 - - @effect-app/schema@0.202.1 - -## 0.180.1 - -### Patch Changes - -- f2eea61: fix TagClass - -## 0.180.0 - -### Minor Changes - -- 7346691: update packages - -### Patch Changes - -- Updated dependencies [7346691] - - @effect-app/fluent-extensions@0.143.0 - - @effect-app/schema@0.202.0 - - @effect-app/core@0.150.0 - -## 0.179.0 - -### Minor Changes - -- add AcessTag support - -## 0.178.1 - -### Patch Changes - -- fix - -## 0.178.0 - -### Minor Changes - -- 0354650: prototype - -## 0.177.0 - -### Minor Changes - -- 32f852e: add requestname to span - -## 0.176.2 - -### Patch Changes - -- Updated dependencies [d1e4f1b] - - @effect-app/schema@0.201.2 - -## 0.176.1 - -### Patch Changes - -- Updated dependencies [0b514ad] - - @effect-app/schema@0.201.1 - -## 0.176.0 - -### Minor Changes - -- e8e8ee2: update packages - -### Patch Changes - -- Updated dependencies [e8e8ee2] - - @effect-app/fluent-extensions@0.142.0 - - @effect-app/schema@0.201.0 - - @effect-app/core@0.149.0 - -## 0.175.1 - -### Patch Changes - -- Updated dependencies [ee00776] - - @effect-app/schema@0.200.0 - -## 0.175.0 - -### Minor Changes - -- 117be64: bump - -### Patch Changes - -- Updated dependencies [117be64] - - @effect-app/core@0.148.0 - - @effect-app/fluent-extensions@0.141.0 - - @effect-app/schema@0.199.0 - -## 0.174.1 - -### Patch Changes - -- 4008eb1: cleanup and add byIdAndSaveWithPure - -## 0.174.0 - -### Minor Changes - -- A new day -- 78b3c9e: reduce tspuse - -### Patch Changes - -- Updated dependencies -- Updated dependencies [78b3c9e] - - @effect-app/core@0.147.0 - - @effect-app/fluent-extensions@0.140.0 - - @effect-app/schema@0.198.0 - -## 0.173.4 - -### Patch Changes - -- 9577608: cleanup - -## 0.173.3 - -### Patch Changes - -- 4d9b13c: add extendTaggedUnion - -## 0.173.2 - -### Patch Changes - -- Updated dependencies [3170396] - - @effect-app/core@0.146.1 - - @effect-app/schema@0.197.1 - -## 0.173.1 - -### Patch Changes - -- ac8e9aa: add OperationResult.is - -## 0.173.0 - -### Minor Changes - -- c070e0d: update effect - -### Patch Changes - -- dd0cf42: update -- Updated dependencies [c070e0d] -- Updated dependencies [dd0cf42] - - @effect-app/fluent-extensions@0.139.0 - - @effect-app/schema@0.197.0 - - @effect-app/core@0.146.0 - -## 0.172.0 - -### Minor Changes - -- 57fcfef: update packages - -### Patch Changes - -- 739e742: add missing plugins in tsconfig -- Updated dependencies [57fcfef] - - @effect-app/fluent-extensions@0.138.0 - - @effect-app/schema@0.196.0 - - @effect-app/core@0.145.0 - -## 0.171.4 - -### Patch Changes - -- Updated dependencies [b5543d4] - - @effect-app/core@0.144.3 - - @effect-app/schema@0.195.3 - -## 0.171.3 - -### Patch Changes - -- Updated dependencies [39beb09] - - @effect-app/core@0.144.2 - - @effect-app/schema@0.195.2 - -## 0.171.2 - -### Patch Changes - -- 0fd4175: f - -## 0.171.1 - -### Patch Changes - -- 73c3e1b: update packages -- Updated dependencies [73c3e1b] - - @effect-app/fluent-extensions@0.137.1 - - @effect-app/core@0.144.1 - - @effect-app/schema@0.195.1 - -## 0.171.0 - -### Minor Changes - -- 49fb62c: align Tag arg order. - -## 0.170.0 - -### Minor Changes - -- 76ea9ae: improve - -## 0.169.0 - -### Minor Changes - -- 7079fc8: connection reset by peer - -### Patch Changes - -- Updated dependencies [7079fc8] - - @effect-app/fluent-extensions@0.137.0 - - @effect-app/schema@0.195.0 - - @effect-app/core@0.144.0 - -## 0.168.0 - -### Minor Changes - -- update TagClassId - -## 0.167.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.136.1 - - @effect-app/core@0.143.1 - - @effect-app/schema@0.194.2 - -## 0.167.1 - -### Patch Changes - -- Updated dependencies [c4ac357] - - @effect-app/core@0.143.0 - - @effect-app/schema@0.194.1 - -## 0.167.0 - -### Minor Changes - -- 8374f61: update packages - -### Patch Changes - -- Updated dependencies [8374f61] - - @effect-app/fluent-extensions@0.136.0 - - @effect-app/schema@0.194.0 - - @effect-app/core@0.142.0 - -## 0.166.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.141.1 - - @effect-app/schema@0.193.1 - -## 0.166.0 - -### Minor Changes - -- de336f6: src - -### Patch Changes - -- Updated dependencies [de336f6] - - @effect-app/fluent-extensions@0.135.0 - - @effect-app/schema@0.193.0 - - @effect-app/core@0.141.0 - -## 0.165.2 - -### Patch Changes - -- dbbd0da: fix path - -## 0.165.1 - -### Patch Changes - -- 7715b74: add tsconfig.base.json -- f10e630: update packages -- Updated dependencies [f10e630] - - @effect-app/fluent-extensions@0.134.1 - - @effect-app/schema@0.192.1 - - @effect-app/core@0.140.1 - -## 0.165.0 - -### Minor Changes - -- 47f46f0: use actual action name casing so that editor go to works. - -## 0.164.0 - -### Minor Changes - -- 5f52e06: update packages - -### Patch Changes - -- Updated dependencies [5f52e06] - - @effect-app/fluent-extensions@0.134.0 - - @effect-app/schema@0.192.0 - - @effect-app/core@0.140.0 - -## 0.163.0 - -### Minor Changes - -- m - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.139.0 - - @effect-app/fluent-extensions@0.133.0 - - @effect-app/schema@0.191.0 - -## 0.162.3 - -### Patch Changes - -- 5b30ea1: cleanup - -## 0.162.2 - -### Patch Changes - -- fix name - -## 0.162.1 - -### Patch Changes - -- fix name - -## 0.162.0 - -### Minor Changes - -- bs - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.138.0 - - @effect-app/fluent-extensions@0.132.0 - - @effect-app/schema@0.190.0 - -## 0.161.0 - -### Minor Changes - -- 8794c23: bs - -## 0.160.0 - -### Minor Changes - -- 797056c: update packages - -### Patch Changes - -- Updated dependencies [797056c] - - @effect-app/fluent-extensions@0.131.0 - - @effect-app/core@0.137.0 - - @effect-app/schema@0.189.1 - -## 0.159.0 - -### Minor Changes - -- 6145788: switch to tanstack-query - -## 0.158.5 - -### Patch Changes - -- b4ee7e7: f - -## 0.158.4 - -### Patch Changes - -- pf - -## 0.158.3 - -### Patch Changes - -- Updated dependencies [c31e783] - - @effect-app/fluent-extensions@0.130.3 - -## 0.158.2 - -### Patch Changes - -- 4848bc6: bump -- Updated dependencies [4848bc6] - - @effect-app/fluent-extensions@0.130.2 - -## 0.158.1 - -### Patch Changes - -- Updated dependencies [67571c9] - - @effect-app/fluent-extensions@0.130.1 - -## 0.158.0 - -### Minor Changes - -- 12eb7f7: update packages - -### Patch Changes - -- Updated dependencies [12eb7f7] - - @effect-app/fluent-extensions@0.130.0 - - @effect-app/schema@0.189.0 - - @effect-app/core@0.136.0 - -## 0.157.4 - -### Patch Changes - -- Updated dependencies [b525b4f] - - @effect-app/fluent-extensions@0.129.3 - -## 0.157.3 - -### Patch Changes - -- Updated dependencies [aed6cd9] - - @effect-app/fluent-extensions@0.129.2 - -## 0.157.2 - -### Patch Changes - -- Updated dependencies [37c167f] - - @effect-app/fluent-extensions@0.129.1 - -## 0.157.1 - -### Patch Changes - -- 28feba6: fix var - -## 0.157.0 - -### Minor Changes - -- 17cfe0f: update packages - -### Patch Changes - -- Updated dependencies [17cfe0f] - - @effect-app/fluent-extensions@0.129.0 - - @effect-app/schema@0.188.0 - - @effect-app/core@0.135.0 - -## 0.156.5 - -### Patch Changes - -- Updated dependencies [3c1cb25] - - @effect-app/core@0.134.1 - - @effect-app/fluent-extensions@0.128.5 - - @effect-app/schema@0.187.5 - -## 0.156.4 - -### Patch Changes - -- Updated dependencies [eddbf3c] - - @effect-app/core@0.134.0 - - @effect-app/fluent-extensions@0.128.4 - - @effect-app/schema@0.187.4 - -## 0.156.3 - -### Patch Changes - -- 7c28686: update packages -- Updated dependencies [7c28686] - - @effect-app/fluent-extensions@0.128.3 - - @effect-app/schema@0.187.3 - - @effect-app/core@0.133.3 - -## 0.156.2 - -### Patch Changes - -- Updated dependencies [963b8d0] - - @effect-app/core@0.133.2 - - @effect-app/fluent-extensions@0.128.2 - - @effect-app/schema@0.187.2 - -## 0.156.1 - -### Patch Changes - -- b9e1e73: ext -- Updated dependencies [b9e1e73] - - @effect-app/core@0.133.1 - - @effect-app/fluent-extensions@0.128.1 - - @effect-app/schema@0.187.1 - -## 0.156.0 - -### Minor Changes - -- 9b67f67: update packages - -### Patch Changes - -- Updated dependencies [9b67f67] - - @effect-app/fluent-extensions@0.128.0 - - @effect-app/schema@0.187.0 - - @effect-app/core@0.133.0 - -## 0.155.2 - -### Patch Changes - -- b3e095c: fix - -## 0.155.1 - -### Patch Changes - -- fix - -## 0.155.0 - -### Minor Changes - -- 71dbfc3: update packages -- 1cc7ee7: cleanup service tags - -### Patch Changes - -- Updated dependencies [71dbfc3] - - @effect-app/fluent-extensions@0.127.0 - - @effect-app/schema@0.186.0 - - @effect-app/core@0.132.0 - -## 0.154.3 - -### Patch Changes - -- Updated dependencies [8577c90] - - @effect-app/core@0.131.9 - - @effect-app/fluent-extensions@0.126.10 - - @effect-app/schema@0.185.2 - -## 0.154.2 - -### Patch Changes - -- 7db0be1: dual copy/clone -- Updated dependencies [7db0be1] - - @effect-app/core@0.131.8 - - @effect-app/fluent-extensions@0.126.9 - - @effect-app/schema@0.185.1 - -## 0.154.1 - -### Patch Changes - -- Updated dependencies [ac55751] - - @effect-app/schema@0.185.0 - - @effect-app/fluent-extensions@0.126.8 - -## 0.154.0 - -### Minor Changes - -- 83091eb: cleanup - -## 0.153.7 - -### Patch Changes - -- Updated dependencies [dc45062] - - @effect-app/core@0.131.7 - - @effect-app/fluent-extensions@0.126.7 - - @effect-app/schema@0.184.7 - -## 0.153.6 - -### Patch Changes - -- Updated dependencies [fc86a75] - - @effect-app/core@0.131.6 - - @effect-app/fluent-extensions@0.126.6 - - @effect-app/schema@0.184.6 - -## 0.153.5 - -### Patch Changes - -- Updated dependencies [e47df05] - - @effect-app/core@0.131.5 - - @effect-app/fluent-extensions@0.126.5 - - @effect-app/schema@0.184.5 - -## 0.153.4 - -### Patch Changes - -- Updated dependencies [3db43d9] - - @effect-app/core@0.131.4 - - @effect-app/fluent-extensions@0.126.4 - - @effect-app/schema@0.184.4 - -## 0.153.3 - -### Patch Changes - -- Updated dependencies [8dd650f] - - @effect-app/core@0.131.3 - - @effect-app/fluent-extensions@0.126.3 - - @effect-app/schema@0.184.3 - -## 0.153.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.131.2 - - @effect-app/fluent-extensions@0.126.2 - - @effect-app/schema@0.184.2 - -## 0.153.1 - -### Patch Changes - -- Updated dependencies [27e6867] - - @effect-app/core@0.131.1 - - @effect-app/fluent-extensions@0.126.1 - - @effect-app/schema@0.184.1 - -## 0.153.0 - -### Minor Changes - -- c8a07bc: fix path - -### Patch Changes - -- Updated dependencies [c8a07bc] - - @effect-app/core@0.131.0 - - @effect-app/fluent-extensions@0.126.0 - - @effect-app/schema@0.184.0 - -## 0.152.0 - -### Minor Changes - -- 525237e: rename @effect-app/prelude to effect-app - -### Patch Changes - -- Updated dependencies [525237e] - - @effect-app/core@0.130.0 - - @effect-app/fluent-extensions@0.125.9 - - @effect-app/schema@0.183.14 - -## 0.151.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.129.3 - - @effect-app/fluent-extensions@0.125.8 - - @effect-app/schema@0.183.13 - -## 0.151.2 - -### Patch Changes - -- Updated dependencies [0d02cbd] - - @effect-app/core@0.129.2 - - @effect-app/fluent-extensions@0.125.7 - - @effect-app/schema@0.183.12 - -## 0.151.1 - -### Patch Changes - -- Updated dependencies [b01fd1e] - - @effect-app/core@0.129.1 - - @effect-app/fluent-extensions@0.125.6 - - @effect-app/schema@0.183.11 - -## 0.151.0 - -### Minor Changes - -- b2d3e53: consolidate - -### Patch Changes - -- Updated dependencies [b2d3e53] - - @effect-app/core@0.129.0 - - @effect-app/fluent-extensions@0.125.5 - - @effect-app/schema@0.183.10 - -## 0.150.3 - -### Patch Changes - -- 200a840: address -- Updated dependencies [200a840] - - @effect-app/core@0.128.2 - - @effect-app/fluent-extensions@0.125.4 - - @effect-app/schema@0.183.9 - -## 0.150.2 - -### Patch Changes - -- Updated dependencies [0c57301] - - @effect-app/core@0.128.1 - - @effect-app/fluent-extensions@0.125.3 - - @effect-app/schema@0.183.8 - -## 0.150.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.128.0 - - @effect-app/fluent-extensions@0.125.2 - - @effect-app/schema@0.183.7 - -## 0.150.0 - -### Minor Changes - -- 83b5bb6: fix option -- 87753e4: improve prelude export - -### Patch Changes - -- Updated dependencies [83b5bb6] -- Updated dependencies [87753e4] - - @effect-app/core@0.127.0 - - @effect-app/fluent-extensions@0.125.1 - - @effect-app/schema@0.183.6 - -## 0.149.6 - -### Patch Changes - -- 988ed1d: update packages -- Updated dependencies [38bff6b] -- Updated dependencies [988ed1d] - - @effect-app/fluent-extensions@0.125.0 - - @effect-app/core@0.126.1 - - @effect-app/schema@0.183.5 - -## 0.149.5 - -### Patch Changes - -- Updated dependencies [c497ff3] - - @effect-app/schema@0.183.4 - - @effect-app/fluent-extensions@0.124.4 - -## 0.149.4 - -### Patch Changes - -- 3c4251e: fix - -## 0.149.3 - -### Patch Changes - -- Updated dependencies [65f8b7b] - - @effect-app/schema@0.183.3 - - @effect-app/fluent-extensions@0.124.3 - -## 0.149.2 - -### Patch Changes - -- Updated dependencies [8290a87] - - @effect-app/schema@0.183.2 - - @effect-app/fluent-extensions@0.124.2 - -## 0.149.1 - -### Patch Changes - -- Updated dependencies [d17dc32] - - @effect-app/schema@0.183.1 - - @effect-app/fluent-extensions@0.124.1 - -## 0.149.0 - -### Minor Changes - -- e5cdd42: update packages - -### Patch Changes - -- Updated dependencies [e5cdd42] - - @effect-app/fluent-extensions@0.124.0 - - @effect-app/schema@0.183.0 - - @effect-app/core@0.126.0 - -## 0.148.0 - -### Minor Changes - -- update to latest Effect "minor" - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.123.0 - - @effect-app/schema@0.182.0 - - @effect-app/core@0.125.0 - -## 0.147.0 - -### Minor Changes - -- c29b6ff: bump - -### Patch Changes - -- Updated dependencies [c29b6ff] - - @effect-app/core@0.124.0 - - @effect-app/fluent-extensions@0.122.0 - - @effect-app/schema@0.181.0 - -## 0.145.3 - -### Patch Changes - -- 6e9f253: fix requestid withDefault - -## 0.146.0 - -### Minor Changes - -- 0c47b83: update packages - -### Patch Changes - -- Updated dependencies [0c47b83] - - @effect-app/fluent-extensions@0.121.0 - - @effect-app/schema@0.180.0 - - @effect-app/core@0.123.0 - -## 0.145.2 - -### Patch Changes - -- Updated dependencies [9d31709] - - @effect-app/fluent-extensions@0.120.0 - -## 0.145.1 - -### Patch Changes - -- 871cccf: remove cpy - -## 0.145.0 - -### Minor Changes - -- bump - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.119.0 - - @effect-app/schema@0.179.0 - - @effect-app/core@0.122.0 - -## 0.144.0 - -### Minor Changes - -- d740bf6: use schema as patch instead - -### Patch Changes - -- Updated dependencies [d740bf6] - - @effect-app/schema@0.178.0 - - @effect-app/fluent-extensions@0.118.1 - -## 0.143.11 - -### Patch Changes - -- Updated dependencies [028af99] - - @effect-app/fluent-extensions@0.118.0 - -## 0.143.10 - -### Patch Changes - -- Updated dependencies [8217bcd] - - @effect-app/fluent-extensions@0.117.4 - -## 0.143.9 - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.117.3 - -## 0.143.8 - -### Patch Changes - -- Updated dependencies - - @effect-app/fluent-extensions@0.116.6 - -## 0.143.7 - -### Patch Changes - -- Updated dependencies [e1c9d4c] - - @effect-app/fluent-extensions@0.117.0 - -## 0.143.6 - -### Patch Changes - -- Updated dependencies [cf3080b] - - @effect-app/fluent-extensions@0.116.6 - -## 0.143.5 - -### Patch Changes - -- Updated dependencies [e279f74] - - @effect-app/fluent-extensions@0.116.5 - -## 0.143.4 - -### Patch Changes - -- Updated dependencies [fb25eb5] - - @effect-app/fluent-extensions@0.116.4 - -## 0.143.3 - -### Patch Changes - -- Updated dependencies [34b8449] - - @effect-app/fluent-extensions@0.116.3 - -## 0.143.2 - -### Patch Changes - -- Updated dependencies [67d4ec9] - - @effect-app/fluent-extensions@0.116.2 - -## 0.143.1 - -### Patch Changes - -- Updated dependencies [bc98aeb] - - @effect-app/schema@0.177.5 - - @effect-app/fluent-extensions@0.116.1 - -## 0.143.0 - -### Minor Changes - -- e6c8452: remove global Either - -### Patch Changes - -- Updated dependencies [e6c8452] - - @effect-app/fluent-extensions@0.116.0 - - @effect-app/core@0.121.0 - - @effect-app/schema@0.177.4 - -## 0.142.3 - -### Patch Changes - -- Updated dependencies [acbbe35] - - @effect-app/schema@0.177.3 - - @effect-app/fluent-extensions@0.115.2 - -## 0.142.2 - -### Patch Changes - -- Updated dependencies [39cc9b0] - - @effect-app/fluent-extensions@0.115.1 - -## 0.142.1 - -### Patch Changes - -- 78de0f5: fix - -## 0.142.0 - -### Minor Changes - -- b51e7b3: ext - -### Patch Changes - -- Updated dependencies [b51e7b3] - - @effect-app/fluent-extensions@0.115.0 - -## 0.141.3 - -### Patch Changes - -- Updated dependencies [6806433] - - @effect-app/fluent-extensions@0.114.3 - -## 0.141.2 - -### Patch Changes - -- Updated dependencies [f28cec3] - - @effect-app/fluent-extensions@0.114.2 - -## 0.141.1 - -### Patch Changes - -- Updated dependencies [6de437e] - - @effect-app/fluent-extensions@0.114.1 - -## 0.141.0 - -### Minor Changes - -- 72e0db6: package updates - -### Patch Changes - -- Updated dependencies [72e0db6] - - @effect-app/fluent-extensions@0.114.0 - - @effect-app/core@0.120.0 - - @effect-app/schema@0.177.2 - -## 0.140.8 - -### Patch Changes - -- Updated dependencies [e66dbfc] - - @effect-app/fluent-extensions@0.113.13 - -## 0.140.7 - -### Patch Changes - -- Updated dependencies [6677889] - - @effect-app/fluent-extensions@0.113.12 - - @effect-app/schema@0.177.1 - -## 0.140.6 - -### Patch Changes - -- Updated dependencies [78982c7] - - @effect-app/fluent-extensions@0.113.11 - -## 0.140.5 - -### Patch Changes - -- Updated dependencies [928dfc8] - - @effect-app/fluent-extensions@0.113.10 - -## 0.140.4 - -### Patch Changes - -- Updated dependencies [2ab1f78] - - @effect-app/fluent-extensions@0.113.9 - -## 0.140.3 - -### Patch Changes - -- Updated dependencies [287c4e6] - - @effect-app/schema@0.177.0 - - @effect-app/fluent-extensions@0.113.8 - -## 0.140.2 - -### Patch Changes - -- Updated dependencies [bff7d58] - - @effect-app/schema@0.176.2 - - @effect-app/fluent-extensions@0.113.7 - -## 0.140.1 - -### Patch Changes - -- Updated dependencies [f7da591] - - @effect-app/schema@0.176.1 - - @effect-app/fluent-extensions@0.113.6 - -## 0.140.0 - -### Minor Changes - -- 76c89b8: cleanup exts - -### Patch Changes - -- Updated dependencies [76c89b8] -- Updated dependencies [76c89b8] - - @effect-app/schema@0.176.0 - - @effect-app/core@0.119.0 - - @effect-app/fluent-extensions@0.113.5 - -## 0.139.0 - -### Minor Changes - -- 2ccca0f: update exts - -### Patch Changes - -- Updated dependencies [ad46a20] -- Updated dependencies [40d51e4] -- Updated dependencies [2ccca0f] - - @effect-app/schema@0.175.0 - - @effect-app/fluent-extensions@0.113.4 - -## 0.138.0 - -### Minor Changes - -- b487764: improve extensions - -### Patch Changes - -- Updated dependencies [b487764] - - @effect-app/fluent-extensions@0.113.0 - -## 0.137.0 - -### Minor Changes - -- 465afb5: fix: Schema functions should use ParseError variants - -### Patch Changes - -- Updated dependencies [465afb5] - - @effect-app/core@0.118.0 - - @effect-app/schema@0.174.0 - -## 0.136.0 - -### Minor Changes - -- f01680a: update packages -- 349d358: improve support for schemas with R. - -### Patch Changes - -- ebcd0dc: update packages -- Updated dependencies [ebcd0dc] -- Updated dependencies [f01680a] -- Updated dependencies [349d358] - - @effect-app/schema@0.173.0 - -## 0.135.0 - -### Minor Changes - -- 744e5fc: disable auto constructor call - -### Patch Changes - -- 0a898f8: update packages -- Updated dependencies [0a898f8] - - @effect-app/schema@0.172.1 - -## 0.134.0 - -### Minor Changes - -- 449b343: update packages - -### Patch Changes - -- Updated dependencies [449b343] -- Updated dependencies [d103a32] - - @effect-app/schema@0.172.0 - -## 0.133.0 - -### Minor Changes - -- 2fd03d3: update packages - -### Patch Changes - -- Updated dependencies [2fd03d3] - - @effect-app/schema@0.171.0 - - @effect-app/core@0.117.0 - -## 0.132.1 - -### Patch Changes - -- c80a6bb: fix type - -## 0.132.0 - -### Minor Changes - -- 99f79ab: update packages -- cd25b5f: improve: typed error handling - -### Patch Changes - -- Updated dependencies [99f79ab] - - @effect-app/schema@0.170.0 - - @effect-app/core@0.116.0 - -## 0.131.0 - -### Minor Changes - -- 352d57c: cleanup query/mutations. - -## 0.130.5 - -### Patch Changes - -- Updated dependencies [17dbbff] - - @effect-app/schema@0.169.3 - -## 0.130.4 - -### Patch Changes - -- 578e25c: pick POST for query/delete when not all members assignable to string -- Updated dependencies [578e25c] - - @effect-app/schema@0.169.2 - -## 0.130.3 - -### Patch Changes - -- fix - -## 0.130.2 - -### Patch Changes - -- f5d1644: fix - -## 0.130.1 - -### Patch Changes - -- Updated dependencies [cb62aaa] - - @effect-app/schema@0.169.1 - -## 0.130.0 - -### Minor Changes - -- 1832f49: add auto method support - -### Patch Changes - -- Updated dependencies [1832f49] - - @effect-app/schema@0.169.0 - -## 0.129.0 - -### Minor Changes - -- default paths to / - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.168.0 - -## 0.128.2 - -### Patch Changes - -- 7db2745: meta - -## 0.128.1 - -### Patch Changes - -- f725d15: fix js - -## 0.128.0 - -### Minor Changes - -- 5786fb6: prefix path with request name - -## 0.127.2 - -### Patch Changes - -- Updated dependencies [690624e] - - @effect-app/schema@0.167.0 - -## 0.127.1 - -### Patch Changes - -- 9780ef0: fup - -## 0.127.0 - -### Minor Changes - -- a30db88: update packages -- b454cd7: embrace Matcher - -### Patch Changes - -- Updated dependencies [a30db88] -- Updated dependencies [b454cd7] - - @effect-app/schema@0.166.0 - - @effect-app/core@0.115.0 - -## 0.126.0 - -### Minor Changes - -- ed813b4: update packages - -### Patch Changes - -- Updated dependencies [ed813b4] - - @effect-app/schema@0.165.0 - -## 0.125.0 - -### Minor Changes - -- 3eb4057: update packages - -### Patch Changes - -- Updated dependencies [3eb4057] - - @effect-app/schema@0.164.0 - - @effect-app/core@0.114.0 - -## 0.124.0 - -### Minor Changes - -- 051bad7: override reasonable Email/PhoneNumber - -### Patch Changes - -- Updated dependencies [051bad7] - - @effect-app/core@0.113.0 - - @effect-app/schema@0.163.1 - -## 0.123.0 - -### Minor Changes - -- 4c915ea: update packages - -### Patch Changes - -- Updated dependencies [4c915ea] - - @effect-app/schema@0.163.0 - - @effect-app/core@0.112.0 - -## 0.122.1 - -### Patch Changes - -- Updated dependencies [e356144] - - @effect-app/schema@0.162.1 - -## 0.122.0 - -### Minor Changes - -- fix exts - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.111.0 - - @effect-app/schema@0.162.0 - -## 0.121.0 - -### Minor Changes - -- e2b5df8: revert workaround - -### Patch Changes - -- Updated dependencies [e2b5df8] - - @effect-app/schema@0.161.0 - - @effect-app/core@0.110.0 - -## 0.120.0 - -### Minor Changes - -- efa846c: improve: class workaround - -### Patch Changes - -- Updated dependencies [efa846c] - - @effect-app/schema@0.160.0 - - @effect-app/core@0.109.0 - -## 0.119.2 - -### Patch Changes - -- d3bc95b: fix lazy arb - -## 0.119.1 - -### Patch Changes - -- Updated dependencies [3936865] - - @effect-app/schema@0.159.1 - -## 0.119.0 - -### Minor Changes - -- 4aaa360: update packages -- a64728d: update to latest Schema - -### Patch Changes - -- Updated dependencies [4aaa360] -- Updated dependencies [a64728d] - - @effect-app/schema@0.159.0 - - @effect-app/core@0.108.0 - -## 0.118.0 - -### Minor Changes - -- 3ee1505: refactor: remove \_\_call extensions - -### Patch Changes - -- Updated dependencies [3ee1505] - - @effect-app/core@0.107.0 - - @effect-app/schema@0.158.1 - -## 0.117.0 - -### Minor Changes - -- 33dcf2e: update packages - -### Patch Changes - -- Updated dependencies [33dcf2e] - - @effect-app/schema@0.158.0 - - @effect-app/core@0.106.0 - -## 0.116.1 - -### Patch Changes - -- fd233aa: package updates -- Updated dependencies [5fe664e] -- Updated dependencies [afa98cf] - - @effect-app/core@0.105.0 - - @effect-app/schema@0.157.1 - -## 0.116.0 - -### Minor Changes - -- e535d05: update packages - -### Patch Changes - -- Updated dependencies [e535d05] - - @effect-app/schema@0.157.0 - - @effect-app/core@0.104.0 - -## 0.115.0 - -### Minor Changes - -- 9049ccd: filter path params from qs - -### Patch Changes - -- Updated dependencies [9049ccd] - - @effect-app/core@0.103.0 - - @effect-app/schema@0.156.1 - -## 0.114.0 - -### Minor Changes - -- a894a63: update packages - -### Patch Changes - -- Updated dependencies [a894a63] - - @effect-app/schema@0.156.0 - - @effect-app/core@0.102.0 - -## 0.113.0 - -### Minor Changes - -- 4e2e01b: roll into one - -## 0.112.1 - -### Patch Changes - -- 59077e8: feat: add `TagClassMakeScoped` - -## 0.112.0 - -### Minor Changes - -- 7945f38: update packages - -### Patch Changes - -- bc5f735: add `TagClassMake` -- Updated dependencies [7945f38] - - @effect-app/schema@0.155.0 - - @effect-app/core@0.101.0 - -## 0.111.0 - -### Minor Changes - -- 067274a: Update packages - -### Patch Changes - -- Updated dependencies [067274a] - - @effect-app/schema@0.154.0 - -## 0.110.0 - -### Minor Changes - -- 63425a7: update packages - -### Patch Changes - -- Updated dependencies [63425a7] - - @effect-app/schema@0.153.0 - - @effect-app/core@0.100.0 - -## 0.109.3 - -### Patch Changes - -- fix Positive->NonNegative - -## 0.109.2 - -### Patch Changes - -- 4464df4: missing ext - -## 0.109.1 - -### Patch Changes - -- Updated dependencies [fd85bed] - - @effect-app/schema@0.152.1 - -## 0.109.0 - -### Minor Changes - -- 82f0f0d: update packages - -### Patch Changes - -- Updated dependencies [82f0f0d] - - @effect-app/schema@0.152.0 - - @effect-app/core@0.99.0 - -## 0.108.3 - -### Patch Changes - -- Updated dependencies [153a19f] - - @effect-app/core@0.98.1 - - @effect-app/schema@0.151.2 - -## 0.108.2 - -### Patch Changes - -- c8e7d1d: improve: stack error helper on assign.tag - -## 0.108.1 - -### Patch Changes - -- 26e8e9f: improve: use meta info for request name -- 0f8297a: feat: add class annotate decorator -- Updated dependencies [0f8297a] - - @effect-app/schema@0.151.1 - -## 0.108.0 - -### Minor Changes - -- 2ea1e3c: update packages - -### Patch Changes - -- Updated dependencies [2ea1e3c] - - @effect-app/schema@0.151.0 - - @effect-app/core@0.98.0 - -## 0.107.11 - -### Patch Changes - -- Updated dependencies [cf69a10] - - @effect-app/schema@0.150.10 - -## 0.107.10 - -### Patch Changes - -- 5564b85: update packages -- Updated dependencies [5564b85] - - @effect-app/schema@0.150.9 - - @effect-app/core@0.97.3 - -## 0.107.9 - -### Patch Changes - -- f8b45b9: export numbers -- Updated dependencies [f8b45b9] - - @effect-app/schema@0.150.8 - -## 0.107.8 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.150.7 - -## 0.107.7 - -### Patch Changes - -- Updated dependencies [a72f796] - - @effect-app/schema@0.150.6 - -## 0.107.6 - -### Patch Changes - -- c615f35: update packages -- Updated dependencies [c615f35] - - @effect-app/core@0.97.2 - - @effect-app/schema@0.150.5 - -## 0.107.5 - -### Patch Changes - -- Updated dependencies [d9034fe] - - @effect-app/schema@0.150.4 - -## 0.107.4 - -### Patch Changes - -- 38f3367: fix unsafe - -## 0.107.3 - -### Patch Changes - -- Updated dependencies [6a5591a] - - @effect-app/schema@0.150.3 - -## 0.107.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.97.1 - - @effect-app/schema@0.150.2 - -## 0.107.1 - -### Patch Changes - -- fd520f7: chore: restore UnionBrand -- Updated dependencies [fd520f7] - - @effect-app/schema@0.150.1 - -## 0.107.0 - -### Minor Changes - -- 640d24a: reference schema2 -- 640d24a: update packages - -### Patch Changes - -- 640d24a: mm -- 640d24a: clean -- 640d24a: cleanup -- 640d24a: f -- 640d24a: update -- 640d24a: faker -- 640d24a: cleanup schema ext -- 640d24a: fix stringid and clientfor -- 640d24a: updates -- 640d24a: S -- 640d24a: fix things -- 640d24a: improve -- 640d24a: update schema -- 640d24a: upd -- 640d24a: cl -- 640d24a: form and client fixes -- 640d24a: update packages -- 640d24a: ext -- 640d24a: improve rc -- 640d24a: fix RequestId -- 640d24a: dynamic faker arb -- 640d24a: fix empty body -- 640d24a: fix mapPath should use From -- 640d24a: exports -- 640d24a: use schema fork -- 640d24a: cleanup errors and use decodeSyc over parseSync -- 640d24a: update packages -- 640d24a: expose ParseResult -- 640d24a: update packages -- 640d24a: fup -- 640d24a: upd -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] -- Updated dependencies [640d24a] - - @effect-app/schema@0.150.0 - - @effect-app/core@0.97.0 - -## 0.107.0-next.45 - -### Patch Changes - -- d2bc3ac: update schema -- Updated dependencies [d2bc3ac] - - @effect-app/schema@0.150.0-next.30 - -## 0.107.0-next.44 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/schema@0.150.0-next.29 - - @effect-app/core@0.97.0-next.8 - -## 0.107.0-next.43 - -### Patch Changes - -- 19dc3b1: ext -- Updated dependencies [19dc3b1] - - @effect-app/schema@0.150.0-next.28 - -## 0.107.0-next.42 - -### Patch Changes - -- 09ed839: dynamic faker arb - -## 0.107.0-next.41 - -### Patch Changes - -- b6d4272: update packages -- Updated dependencies [0bc4207] -- Updated dependencies [b6d4272] - - @effect-app/schema@0.150.0-next.27 - - @effect-app/core@0.97.0-next.7 - -## 0.107.0-next.40 - -### Patch Changes - -- 14b33a9: cleanup errors and use decodeSyc over parseSync -- Updated dependencies [14b33a9] - - @effect-app/schema@0.150.0-next.26 - -## 0.107.0-next.39 - -### Patch Changes - -- Updated dependencies [aa1b654] - - @effect-app/schema@0.150.0-next.25 - -## 0.107.0-next.38 - -### Patch Changes - -- 64793a8: fix mapPath should use From - -## 0.107.0-next.37 - -### Patch Changes - -- 9e151ed: update packages -- Updated dependencies [9e151ed] - - @effect-app/schema@0.150.0-next.24 - - @effect-app/core@0.97.0-next.6 - -## 0.107.0-next.36 - -### Patch Changes - -- Updated dependencies [6b89c27] - - @effect-app/core@0.97.0-next.5 - - @effect-app/schema@0.150.0-next.23 - -## 0.107.0-next.35 - -### Patch Changes - -- 169c159: expose ParseResult -- Updated dependencies [169c159] - - @effect-app/schema@0.150.0-next.22 - -## 0.107.0-next.34 - -### Patch Changes - -- b46e30d: fup - -## 0.107.0-next.33 - -### Patch Changes - -- Updated dependencies [c57bb66] - - @effect-app/schema@0.150.0-next.21 - -## 0.107.0-next.32 - -### Patch Changes - -- 735dea9: improve rc - -## 0.107.0-next.31 - -### Patch Changes - -- 21482c6: cl - -## 0.107.0-next.30 - -### Patch Changes - -- eab24be: upd - -## 0.107.0-next.29 - -### Patch Changes - -- def06a7: fix things - -## 0.107.0-next.28 - -### Patch Changes - -- eb31797: fix empty body - -## 0.107.0-next.27 - -### Patch Changes - -- 65696a6: fix RequestId - -## 0.107.0-next.26 - -### Patch Changes - -- 777162f: improve -- Updated dependencies [777162f] - - @effect-app/schema@0.150.0-next.20 - -## 0.107.0-next.25 - -### Patch Changes - -- 0776693: fix stringid and clientfor - -## 0.107.0-next.24 - -### Patch Changes - -- 68eaf52: form and client fixes - -## 0.107.0-next.23 - -### Patch Changes - -- Updated dependencies [ca927a5] - - @effect-app/schema@0.150.0-next.19 - -## 0.107.0-next.22 - -### Patch Changes - -- 5f62f5a: upd -- Updated dependencies [5f62f5a] - - @effect-app/schema@0.150.0-next.18 - -## 0.107.0-next.21 - -### Patch Changes - -- Updated dependencies [53d4b3e] - - @effect-app/schema@0.150.0-next.17 - -## 0.107.0-next.20 - -### Patch Changes - -- Updated dependencies [63d4549] - - @effect-app/schema@0.150.0-next.16 - -## 0.107.0-next.19 - -### Patch Changes - -- b19ce70: S - -## 0.107.0-next.18 - -### Patch Changes - -- 92ff6b9: exports -- Updated dependencies [92ff6b9] - - @effect-app/schema@0.150.0-next.15 - -## 0.107.0-next.17 - -### Patch Changes - -- Updated dependencies [c234016] - - @effect-app/schema@0.150.0-next.14 - -## 0.107.0-next.16 - -### Patch Changes - -- Updated dependencies [5d53b26] - - @effect-app/schema@0.150.0-next.13 - -## 0.107.0-next.15 - -### Patch Changes - -- Updated dependencies [147bc14] - - @effect-app/schema@0.150.0-next.12 - -## 0.107.0-next.14 - -### Patch Changes - -- Updated dependencies [660d57a] - - @effect-app/schema@0.150.0-next.11 - -## 0.107.0-next.13 - -### Patch Changes - -- 20076e0: update -- Updated dependencies [20076e0] - - @effect-app/schema@0.150.0-next.10 - -## 0.107.0-next.12 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.97.0-next.4 - - @effect-app/schema@0.150.0-next.9 - -## 0.107.0-next.11 - -### Patch Changes - -- ac35466: use schema fork -- Updated dependencies [ac35466] - - @effect-app/schema@0.150.0-next.8 - -## 0.107.0-next.10 - -### Patch Changes - -- Updated dependencies [4f8ade3] - - @effect-app/core@0.97.0-next.3 - - @effect-app/schema@0.150.0-next.7 - -## 0.107.0-next.9 - -### Patch Changes - -- Updated dependencies [8897586] - - @effect-app/schema@0.150.0-next.6 - -## 0.107.0-next.8 - -### Patch Changes - -- c550ee5: updates -- Updated dependencies [c550ee5] - - @effect-app/schema@0.150.0-next.5 - - @effect-app/core@0.97.0-next.2 - -## 0.107.0-next.7 - -### Patch Changes - -- 82b3ca0: mm - -## 0.107.0-next.6 - -### Patch Changes - -- Updated dependencies [ace7afb] - - @effect-app/schema@0.150.0-next.4 - -## 0.107.0-next.5 - -### Patch Changes - -- 3320864: cleanup schema ext -- Updated dependencies [3320864] - - @effect-app/schema@0.150.0-next.3 - -## 0.107.0-next.4 - -### Patch Changes - -- Updated dependencies [fde3e90] - - @effect-app/schema@0.150.0-next.2 - - @effect-app/core@0.97.0-next.1 - -## 0.107.0-next.3 - -### Patch Changes - -- 3d9e1ab: clean -- 47d0151: faker - -## 0.107.0-next.2 - -### Patch Changes - -- c3de47e: f - -## 0.107.0-next.1 - -### Patch Changes - -- bd2130b: cleanup -- Updated dependencies [bd2130b] - - @effect-app/schema@0.150.0-next.1 - -## 0.107.0-next.0 - -### Minor Changes - -- 87a6ac2: update packages - -### Patch Changes - -- Updated dependencies [87a6ac2] - - @effect-app/schema2@0.1.0-next.0 - - @effect-app/core@0.97.0-next.0 - -## 0.106.1 - -### Patch Changes - -- efb854a: update packages -- Updated dependencies [efb854a] - - @effect-app/schema@0.90.1 - - @effect-app/core@0.96.1 - -## 0.106.0 - -### Minor Changes - -- 4330242: improve: TagClass - -## 0.105.0 - -### Minor Changes - -- c6efdeb: update packages - -### Patch Changes - -- Updated dependencies [c6efdeb] - - @effect-app/schema@0.90.0 - - @effect-app/core@0.96.0 - -## 0.104.1 - -### Patch Changes - -- cf6ae3c: update packages -- Updated dependencies [cf6ae3c] - - @effect-app/schema@0.89.1 - - @effect-app/core@0.95.1 - -## 0.104.0 - -### Minor Changes - -- 46bd7be: update packages - -### Patch Changes - -- Updated dependencies [46bd7be] - - @effect-app/schema@0.89.0 - - @effect-app/core@0.95.0 - -## 0.103.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.94.2 - - @effect-app/schema@0.88.3 - -## 0.103.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.94.1 - - @effect-app/schema@0.88.2 - -## 0.103.0 - -### Minor Changes - -- 92d7e37: update packages - -### Patch Changes - -- Updated dependencies [92d7e37] -- Updated dependencies - - @effect-app/core@0.94.0 - - @effect-app/schema@0.88.1 - -## 0.102.0 - -### Minor Changes - -- bump - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.93.0 - - @effect-app/schema@0.88.0 - -## 0.101.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.87.10 - -## 0.101.0 - -### Minor Changes - -- 67eb741: update packages - -### Patch Changes - -- Updated dependencies [67eb741] - - @effect-app/core@0.92.0 - - @effect-app/schema@0.87.9 - -## 0.100.1 - -### Patch Changes - -- 7f447d3: update packages -- Updated dependencies [7f447d3] - - @effect-app/core@0.91.1 - - @effect-app/schema@0.87.8 - -## 0.100.0 - -### Minor Changes - -- ad8154a: update packages - -### Patch Changes - -- Updated dependencies [ad8154a] - - @effect-app/core@0.91.0 - - @effect-app/schema@0.87.7 - -## 0.99.1 - -### Patch Changes - -- f33d0a1: feat: change request path to extends from `/${string}` for platform alignment -- Updated dependencies [f33d0a1] - - @effect-app/schema@0.87.6 - -## 0.99.0 - -### Minor Changes - -- f20991e: update packages - -### Patch Changes - -- Updated dependencies [f20991e] - - @effect-app/core@0.90.0 - - @effect-app/schema@0.87.5 - -## 0.98.3 - -### Patch Changes - -- Updated dependencies [687c313] - - @effect-app/schema@0.87.4 - -## 0.98.2 - -### Patch Changes - -- Updated dependencies [fcfea25] - - @effect-app/schema@0.87.3 - -## 0.98.1 - -### Patch Changes - -- Updated dependencies [e468bd0] - - @effect-app/schema@0.87.2 - -## 0.98.0 - -### Minor Changes - -- 98fbdb3: update packages - -### Patch Changes - -- Updated dependencies [98fbdb3] - - @effect-app/core@0.89.0 - - @effect-app/schema@0.87.1 - -## 0.97.0 - -### Minor Changes - -- 8b90f25: feat: add NumberAsInt, PositiveNumberAsInt - -### Patch Changes - -- Updated dependencies [8b90f25] - - @effect-app/schema@0.87.0 - -## 0.96.2 - -### Patch Changes - -- c3c5877: extend refinements with copy and clone - -## 0.96.1 - -### Patch Changes - -- a5b77fc: fix types - -## 0.96.0 - -### Minor Changes - -- 60ecffb: feat: copy/clone trait trial - -### Patch Changes - -- Updated dependencies [60ecffb] - - @effect-app/schema@0.86.0 - -## 0.95.0 - -### Minor Changes - -- fb332b4: update packages - -### Patch Changes - -- Updated dependencies [fb332b4] - - @effect-app/schema@0.85.0 - - @effect-app/core@0.88.0 - -## 0.94.0 - -### Minor Changes - -- 0069d09: Cleanup layers - -## 0.93.0 - -### Minor Changes - -- a681ab6: update packages - -### Patch Changes - -- Updated dependencies [a681ab6] - - @effect-app/schema@0.84.0 - - @effect-app/core@0.87.0 - -## 0.92.0 - -### Minor Changes - -- a9e8b8f: update packages - -### Patch Changes - -- Updated dependencies [169ccb0] -- Updated dependencies [a9e8b8f] - - @effect-app/core@0.86.0 - - @effect-app/schema@0.83.0 - -## 0.91.1 - -### Patch Changes - -- Updated dependencies [3cf98d6] - - @effect-app/schema@0.82.2 - -## 0.91.0 - -### Minor Changes - -- e0b2e24: fix: update for Secret - -### Patch Changes - -- Updated dependencies [e0b2e24] - - @effect-app/core@0.85.0 - - @effect-app/schema@0.82.1 - -## 0.90.0 - -### Minor Changes - -- ac36723: update dependencies - -### Patch Changes - -- Updated dependencies [ac36723] - - @effect-app/schema@0.82.0 - - @effect-app/core@0.84.0 - -## 0.89.0 - -### Minor Changes - -- da80c66: feat: add literalNumber - -### Patch Changes - -- Updated dependencies [da80c66] - - @effect-app/schema@0.81.0 - -## 0.88.9 - -### Patch Changes - -- Updated dependencies [8235a1a] - - @effect-app/core@0.83.3 - - @effect-app/schema@0.80.6 - -## 0.88.8 - -### Patch Changes - -- Updated dependencies [f6e1e15] - - @effect-app/schema@0.80.5 - -## 0.88.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.83.2 - - @effect-app/schema@0.80.4 - -## 0.88.6 - -### Patch Changes - -- Updated dependencies [8a81a8f] - - @effect-app/core@0.83.1 - - @effect-app/schema@0.80.3 - -## 0.88.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.83.0 - - @effect-app/schema@0.80.2 - -## 0.88.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.82.1 - - @effect-app/schema@0.80.1 - -## 0.88.3 - -### Patch Changes - -- Updated dependencies [4012f97] - - @effect-app/schema@0.80.0 - - @effect-app/core@0.82.0 - -## 0.88.2 - -### Patch Changes - -- 7f946cb: remove obsolete helpers -- Updated dependencies [7f946cb] - - @effect-app/core@0.81.1 - - @effect-app/schema@0.79.1 - -## 0.88.1 - -### Patch Changes - -- 65886f2: fix type sigs - -## 0.88.0 - -### Minor Changes - -- 16ffb5e: class service/repo support andThen, drop flatMap - -## 0.87.0 - -### Minor Changes - -- align v - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.81.0 - - @effect-app/schema@0.79.0 - -## 0.86.0 - -### Minor Changes - -- align v - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.80.0 - - @effect-app/schema@0.78.0 - -## 0.85.5 - -### Patch Changes - -- 4627526: improve string name - -## 0.85.4 - -### Patch Changes - -- Updated dependencies [081418b] - - @effect-app/schema@0.77.1 - -## 0.85.3 - -### Patch Changes - -- fix - -## 0.85.2 - -### Patch Changes - -- fix - -## 0.85.1 - -### Patch Changes - -- fix typo - -## 0.85.0 - -### Minor Changes - -- feat: support constructor input for clientFor requests - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.77.0 - -## 0.84.0 - -### Minor Changes - -- improve String schemas - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.76.0 - -## 0.83.0 - -### Minor Changes - -- fa908e8: move fluent-extensions - -## 0.82.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.75.3 - -## 0.82.2 - -### Patch Changes - -- Updated dependencies [086c2b1] - - @effect-app/schema@0.75.2 - -## 0.82.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.75.1 - -## 0.82.0 - -### Minor Changes - -- 68359de: more schema alignment - -### Patch Changes - -- Updated dependencies [68359de] - - @effect-app/schema@0.75.0 - -## 0.81.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.74.2 - -## 0.81.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.74.1 - -## 0.81.0 - -### Minor Changes - -- 771d7e6: Fix model name - -### Patch Changes - -- Updated dependencies [771d7e6] - - @effect-app/schema@0.74.0 - -## 0.80.1 - -### Patch Changes - -- Updated dependencies [af19954] - - @effect-app/core@0.79.1 - - @effect-app/schema@0.73.1 - -## 0.80.0 - -### Minor Changes - -- 1f6b9a1: update packages - -### Patch Changes - -- Updated dependencies [1f6b9a1] - - @effect-app/schema@0.73.0 - - @effect-app/core@0.79.0 - -## 0.79.0 - -### Minor Changes - -- c528c33: initial alignment of Schema with effect/schema - -### Patch Changes - -- Updated dependencies [c528c33] - - @effect-app/schema@0.72.0 - - @effect-app/core@0.78.0 - -## 0.78.2 - -### Patch Changes - -- d4a6be2: fix ext - -## 0.78.1 - -### Patch Changes - -- 4c9e60a: add basic fluent extensions for projects without tsplus - -## 0.78.0 - -### Minor Changes - -- da85f48: cleanup operators. add nonEmpty map variant - -### Patch Changes - -- ab7eddc: update packages -- Updated dependencies [ab7eddc] -- Updated dependencies [da85f48] - - @effect-app/core@0.77.0 - - @effect-app/schema@0.71.0 - -## 0.77.3 - -### Patch Changes - -- Updated dependencies [d462c64] - - @effect-app/schema@0.70.0 - - @effect-app/core@0.76.0 - -## 0.77.2 - -### Patch Changes - -- Updated dependencies [26a3bc8] - - @effect-app/schema@0.69.2 - -## 0.77.1 - -### Patch Changes - -- Updated dependencies [9efeed84] - - @effect-app/core@0.75.1 - - @effect-app/schema@0.69.1 - -## 0.77.0 - -### Minor Changes - -- 88f5695c: revert schema classes double as tag - -### Patch Changes - -- Updated dependencies [88f5695c] - - @effect-app/schema@0.69.0 - - @effect-app/core@0.75.0 - -## 0.76.0 - -### Minor Changes - -- 46719cec: feat: schema classes double as Tag - -### Patch Changes - -- Updated dependencies [46719cec] - - @effect-app/schema@0.68.0 - - @effect-app/core@0.74.0 - -## 0.75.2 - -### Patch Changes - -- Updated dependencies [5813c6b2] - - @effect-app/core@0.73.1 - - @effect-app/schema@0.67.2 - -## 0.75.1 - -### Patch Changes - -- a644e7b5: fix methods exports -- Updated dependencies [a644e7b5] - - @effect-app/schema@0.67.1 - -## 0.75.0 - -### Minor Changes - -- 6c180253: cleanup - -### Patch Changes - -- Updated dependencies [6c180253] - - @effect-app/schema@0.67.0 - - @effect-app/core@0.73.0 - -## 0.74.4 - -### Patch Changes - -- Updated dependencies [f436ce54] - - @effect-app/schema@0.66.0 - -## 0.74.3 - -### Patch Changes - -- 7ff1f6f9: update packages -- 04f6bafa: update packages -- Updated dependencies [7ff1f6f9] -- Updated dependencies [04f6bafa] - - @effect-app/core@0.72.1 - - @effect-app/schema@0.65.3 - -## 0.74.2 - -### Patch Changes - -- Updated dependencies [07c0dbfc] - - @effect-app/schema@0.65.2 - -## 0.74.1 - -### Patch Changes - -- Updated dependencies [a7a9f51f] - - @effect-app/schema@0.65.1 - -## 0.74.0 - -### Minor Changes - -- 0a4bfbc7: update packages - -### Patch Changes - -- Updated dependencies [0a4bfbc7] - - @effect-app/schema@0.65.0 - - @effect-app/core@0.72.0 - -## 0.73.5 - -### Patch Changes - -- 56583d9b: align span names -- Updated dependencies [baf5a2b7] - - @effect-app/core@0.71.5 - - @effect-app/schema@0.64.5 - -## 0.73.4 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/core@0.71.4 - - @effect-app/schema@0.64.4 - -## 0.73.3 - -### Patch Changes - -- Updated dependencies [99205b44] - - @effect-app/core@0.71.3 - - @effect-app/schema@0.64.3 - -## 0.73.2 - -### Patch Changes - -- 002cdb31: update packages -- Updated dependencies [002cdb31] - - @effect-app/core@0.71.2 - - @effect-app/schema@0.64.2 - -## 0.73.1 - -### Patch Changes - -- Updated dependencies [868919c8] - - @effect-app/schema@0.64.1 - - @effect-app/core@0.71.1 - -## 0.73.0 - -### Minor Changes - -- 90af6a41: update packages - -### Patch Changes - -- Updated dependencies [90af6a41] - - @effect-app/schema@0.64.0 - - @effect-app/core@0.71.0 - -## 0.72.9 - -### Patch Changes - -- de419614: enhance errors - -## 0.72.8 - -### Patch Changes - -- ed346253: Improve NotLoggedInError - -## 0.72.7 - -### Patch Changes - -- ff54e102: update packages -- Updated dependencies [ff54e102] - - @effect-app/core@0.70.2 - - @effect-app/schema@0.63.2 - -## 0.72.6 - -### Patch Changes - -- ef626697: improve client span - -## 0.72.5 - -### Patch Changes - -- 60615760: cleanup - -## 0.72.4 - -### Patch Changes - -- fix id - -## 0.72.3 - -### Patch Changes - -- f03b6b48: fix - -## 0.72.2 - -### Patch Changes - -- Updated dependencies [3f0dba21] - - @effect-app/core@0.70.1 - - @effect-app/schema@0.63.1 - -## 0.72.1 - -### Patch Changes - -- 8b0e01b4: workaround new Ref variance - -## 0.72.0 - -### Minor Changes - -- 283ebbe7: update packages - -### Patch Changes - -- Updated dependencies [283ebbe7] - - @effect-app/schema@0.63.0 - - @effect-app/core@0.70.0 - -## 0.71.1 - -### Patch Changes - -- d1a6193b: feat: support span across client and api -- Updated dependencies [d1a6193b] - - @effect-app/core@0.69.1 - - @effect-app/schema@0.62.5 - -## 0.71.0 - -### Minor Changes - -- 40a6c3b6: update packages - -### Patch Changes - -- Updated dependencies [40a6c3b6] - - @effect-app/core@0.69.0 - - @effect-app/schema@0.62.4 - -## 0.70.4 - -### Patch Changes - -- housekeeping: update packages -- Updated dependencies - - @effect-app/core@0.68.3 - - @effect-app/schema@0.62.3 - -## 0.70.3 - -### Patch Changes - -- e4185126: unification workaround - -## 0.70.2 - -### Patch Changes - -- 263a56b9: update packages -- Updated dependencies [263a56b9] - - @effect-app/core@0.68.2 - - @effect-app/schema@0.62.2 - -## 0.70.1 - -### Patch Changes - -- aa3c98d1: update packages -- Updated dependencies [aa3c98d1] - - @effect-app/schema@0.62.1 - - @effect-app/core@0.68.1 - -## 0.70.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.68.0 - - @effect-app/schema@0.62.0 - -## 0.69.10 - -### Patch Changes - -- Updated dependencies [9c347b64] - - @effect-app/schema@0.61.1 - -## 0.69.9 - -### Patch Changes - -- Updated dependencies [fdd19b0b] - - @effect-app/schema@0.61.0 - -## 0.69.8 - -### Patch Changes - -- d7dd36d4: update packages -- Updated dependencies [d7dd36d4] - - @effect-app/schema@0.60.5 - - @effect-app/core@0.67.5 - -## 0.69.7 - -### Patch Changes - -- 780a6718: update packages -- Updated dependencies [780a6718] - - @effect-app/core@0.67.4 - - @effect-app/schema@0.60.4 - -## 0.69.6 - -### Patch Changes - -- 57f506e7: feat: add demandJson for HttpClient - -## 0.69.5 - -### Patch Changes - -- 8c0b8270: improve json parse error - -## 0.69.4 - -### Patch Changes - -- 84e54d54: Update packages -- Updated dependencies [84e54d54] - - @effect-app/core@0.67.3 - - @effect-app/schema@0.60.3 - -## 0.69.3 - -### Patch Changes - -- 696a525e: update packages -- Updated dependencies [696a525e] - - @effect-app/schema@0.60.2 - - @effect-app/core@0.67.2 - -## 0.69.2 - -### Patch Changes - -- f8cb0efd: schemaJsonBody - -## 0.69.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.67.1 - - @effect-app/schema@0.60.1 - -## 0.69.0 - -### Minor Changes - -- housekeeping: update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.60.0 - - @effect-app/core@0.67.0 - -## 0.68.3 - -### Patch Changes - -- blurg - -## 0.68.2 - -### Patch Changes - -- 7118bf61: fix error type - -## 0.68.1 - -### Patch Changes - -- Updated dependencies [56a4418f] - - @effect-app/schema@0.59.0 - -## 0.68.0 - -### Minor Changes - -- 271e2c7a: yieldable errors - -### Patch Changes - -- Updated dependencies [271e2c7a] - - @effect-app/schema@0.58.0 - - @effect-app/core@0.66.0 - -## 0.67.10 - -### Patch Changes - -- Updated dependencies [89bae16e] - - @effect-app/core@0.65.5 - - @effect-app/schema@0.57.10 - -## 0.67.9 - -### Patch Changes - -- 838eeb10: update packages -- Updated dependencies [838eeb10] - - @effect-app/schema@0.57.9 - -## 0.67.8 - -### Patch Changes - -- Updated dependencies [0b279a80] - - @effect-app/core@0.65.4 - - @effect-app/schema@0.57.8 - -## 0.67.7 - -### Patch Changes - -- 50c7fe31: fix typo - -## 0.67.6 - -### Patch Changes - -- 7fc3cfbe: improve client api - -## 0.67.5 - -### Patch Changes - -- 26087368: update platform extensions -- Updated dependencies [26087368] - - @effect-app/core@0.65.3 - - @effect-app/schema@0.57.7 - -## 0.67.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.65.2 - - @effect-app/schema@0.57.6 - -## 0.67.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.65.1 - - @effect-app/schema@0.57.5 - -## 0.67.2 - -### Patch Changes - -- fix HttpClient global - -## 0.67.1 - -### Patch Changes - -- 924817d9: fix type - -## 0.67.0 - -### Minor Changes - -- 51b546c5: feat: Convert clientFor to use @effect/platform HttpClient - -### Patch Changes - -- Updated dependencies [51b546c5] - - @effect-app/core@0.65.0 - - @effect-app/schema@0.57.4 - -## 0.66.0 - -### Minor Changes - -- 5f3ae060: feat: add HttpClient from @effect/platform - -### Patch Changes - -- Updated dependencies [5f3ae060] - - @effect-app/core@0.64.0 - - @effect-app/schema@0.57.3 - -## 0.65.2 - -### Patch Changes - -- 87e29b7d: export httpclient -- Updated dependencies [87e29b7d] - - @effect-app/core@0.63.2 - - @effect-app/schema@0.57.2 - -## 0.65.1 - -### Patch Changes - -- fix tsplus.config for pnpm -- Updated dependencies - - @effect-app/core@0.63.1 - - @effect-app/schema@0.57.1 - -## 0.65.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.57.0 - - @effect-app/core@0.63.0 - -## 0.64.0 - -### Minor Changes - -- update packages, improve error reporting - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.62.0 - - @effect-app/schema@0.56.0 - -## 0.63.1 - -### Patch Changes - -- add date extensions - -## 0.63.0 - -### Minor Changes - -- add match and update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.55.0 - - @effect-app/core@0.61.0 - -## 0.62.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.54.0 - - @effect-app/core@0.60.0 - -## 0.61.0 - -### Minor Changes - -- housekeeping: update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.53.0 - - @effect-app/core@0.59.0 - -## 0.60.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.58.4 - - @effect-app/schema@0.52.4 - -## 0.60.3 - -### Patch Changes - -- Updated dependencies [6b002041] -- Updated dependencies - - @effect-app/core@0.58.3 - - @effect-app/schema@0.52.3 - -## 0.60.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.58.2 - - @effect-app/schema@0.52.2 - -## 0.60.1 - -### Patch Changes - -- improve tsc parsing performance 3x -- Updated dependencies - - @effect-app/core@0.58.1 - - @effect-app/schema@0.52.1 - -## 0.60.0 - -### Minor Changes - -- 4d2e68df: Switch to effect meta package - -### Patch Changes - -- Updated dependencies [4d2e68df] - - @effect-app/schema@0.52.0 - - @effect-app/core@0.58.0 - -## 0.59.7 - -### Patch Changes - -- fix tagclass type - -## 0.59.6 - -### Patch Changes - -- fix tagclass - -## 0.59.5 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.5 - - @effect-app/schema@0.51.5 - -## 0.59.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.4 - - @effect-app/schema@0.51.4 - -## 0.59.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.3 - - @effect-app/schema@0.51.3 - -## 0.59.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.2 - - @effect-app/schema@0.51.2 - -## 0.59.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.57.1 - - @effect-app/schema@0.51.1 - -## 0.59.0 - -### Minor Changes - -- c80b9e7a: Updated to latest Effect - -### Patch Changes - -- Updated dependencies [c80b9e7a] - - @effect-app/schema@0.51.0 - - @effect-app/core@0.57.0 - -## 0.58.2 - -### Patch Changes - -- fix type of E variants of clientFor - -## 0.58.1 - -### Patch Changes - -- update packages -- Updated dependencies - - @effect-app/core@0.56.1 - - @effect-app/schema@0.50.1 - -## 0.58.0 - -### Minor Changes - -- housekeeping: update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.50.0 - - @effect-app/core@0.56.0 - -## 0.57.0 - -### Minor Changes - -- aa9c3126: using the new faker-js package instead of the old cursed one - -## 0.56.1 - -### Patch Changes - -- e4609777: Use distributive omit - -## 0.56.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.55.0 - - @effect-app/schema@0.49.0 - -## 0.55.2 - -### Patch Changes - -- add pick and omit object extensions - -## 0.55.1 - -### Patch Changes - -- Update packages -- Updated dependencies - - @effect-app/schema@0.48.1 - - @effect-app/core@0.54.1 - -## 0.55.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.54.0 - - @effect-app/schema@0.48.0 - -## 0.54.0 - -### Minor Changes - -- Cleanup legacy cache - -## 0.53.2 - -### Patch Changes - -- EffectCache - -## 0.53.1 - -### Patch Changes - -- Add exports -- Updated dependencies - - @effect-app/core@0.53.1 - - @effect-app/schema@0.47.1 - -## 0.53.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.53.0 - - @effect-app/schema@0.47.0 - -## 0.52.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.52.0 - - @effect-app/schema@0.46.0 - -## 0.51.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.45.0 - - @effect-app/core@0.51.0 - -## 0.50.1 - -### Patch Changes - -- c78a54ff: Enhance TagClass - -## 0.50.0 - -### Minor Changes - -- update ids - -## 0.49.1 - -### Patch Changes - -- Add brandedStringId - -## 0.49.0 - -### Minor Changes - -- a - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @effect-app/schema@0.44.0 - - @effect-app/core@0.50.0 - -## 0.48.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.49.1 - - @effect-app/schema@0.43.3 - -## 0.48.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.43.2 - -## 0.48.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.43.1 - -## 0.48.0 - -### Minor Changes - -- Compiler test - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.49.0 - - @effect-app/schema@0.43.0 - -## 0.47.0 - -### Minor Changes - -- Update depdendencies - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.42.0 - - @effect-app/core@0.48.0 - -## 0.46.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.41.2 - -## 0.46.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.41.1 - -## 0.46.0 - -### Minor Changes - -- Cleanup schema ext location - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.41.0 - -## 0.45.0 - -### Minor Changes - -- Improve codegen - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.40.0 - -## 0.44.12 - -### Patch Changes - -- fix rest -- Updated dependencies - - @effect-app/schema@0.39.12 - -## 0.44.11 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.11 - -## 0.44.10 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.10 - -## 0.44.9 - -### Patch Changes - -- feat: property extensions -- Updated dependencies - - @effect-app/schema@0.39.9 - -## 0.44.8 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.8 - -## 0.44.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.7 - -## 0.44.6 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.6 - -## 0.44.5 - -### Patch Changes - -- fix -- Updated dependencies - - @effect-app/schema@0.39.5 - -## 0.44.4 - -### Patch Changes - -- fix global -- Updated dependencies - - @effect-app/core@0.47.1 - - @effect-app/schema@0.39.4 - -## 0.44.3 - -### Patch Changes - -- Improve schema prop api -- Updated dependencies - - @effect-app/schema@0.39.3 - -## 0.44.2 - -### Patch Changes - -- Improve withDefadult -- Updated dependencies - - @effect-app/schema@0.39.2 - -## 0.44.1 - -### Patch Changes - -- fix withDefault -- Updated dependencies - - @effect-app/schema@0.39.1 - -## 0.44.0 - -### Minor Changes - -- update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.39.0 - - @effect-app/core@0.47.0 - -## 0.43.0 - -### Minor Changes - -- Improve Schema withDefault - -## 0.42.0 - -### Minor Changes - -- update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.46.0 - - @effect-app/schema@0.38.0 - -## 0.41.0 - -### Minor Changes - -- Update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.37.0 - - @effect-app/core@0.45.0 - -## 0.40.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.44.0 - - @effect-app/schema@0.36.1 - -## 0.40.0 - -### Minor Changes - -- support optional constructors for schema with none or only optional input. - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.36.0 - -## 0.39.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.35.0 - - @effect-app/core@0.43.0 - -## 0.38.0 - -### Minor Changes - -- update packages and cleanup - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.34.0 - - @effect-app/core@0.42.0 - -## 0.37.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.33.0 - - @effect-app/core@0.41.0 - -## 0.36.1 - -### Patch Changes - -- fix error channel of allLowerFirst - -## 0.36.0 - -### Minor Changes - -- AllLowerFirst should be Par - -## 0.35.0 - -### Minor Changes - -- update dependencies - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.40.0 - - @effect-app/schema@0.32.0 - -## 0.34.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.39.1 - - @effect-app/schema@0.31.1 - -## 0.34.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies -- Updated dependencies [ba4849c9] - - @effect-app/schema@0.31.0 - - @effect-app/core@0.39.0 - -## 0.33.4 - -### Patch Changes - -- Update packages -- Updated dependencies - - @effect-app/schema@0.30.2 - - @effect-app/core@0.38.1 - -## 0.33.3 - -### Patch Changes - -- fup - -## 0.33.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.30.1 - -## 0.33.1 - -### Patch Changes - -- fup - -## 0.33.0 - -### Minor Changes - -- improve min/max/range for numbers. - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.30.0 - -## 0.32.0 - -### Minor Changes - -- Properly identify number schemas - -## 0.31.0 - -### Minor Changes - -- Update packages -- feat(form): number vs string - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.29.0 - - @effect-app/core@0.38.0 - -## 0.30.2 - -### Patch Changes - -- FUP - -## 0.30.1 - -### Patch Changes - -- rename: servicesOrEffects to allLowerFirst - -## 0.30.0 - -### Minor Changes - -- Update to latest effect - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.28.0 - - @effect-app/core@0.37.0 - -## 0.29.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.36.1 - - @effect-app/schema@0.27.1 - -## 0.29.1 - -### Patch Changes - -- Extend Services extensions - -## 0.29.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.27.0 - - @effect-app/core@0.36.0 - -## 0.28.0 - -### Minor Changes - -- Update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.26.0 - - @effect-app/core@0.35.0 - -## 0.27.1 - -### Patch Changes - -- fix dep -- Updated dependencies - - @effect-app/schema@0.25.1 - -## 0.27.0 - -### Minor Changes - -- update packages - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.25.0 - - @effect-app/core@0.34.0 - -## 0.26.2 - -### Patch Changes - -- fix clientFor E - -## 0.26.1 - -### Patch Changes - -- fix - -## 0.26.0 - -### Minor Changes - -- Update packages, add support for class features to schema and optic - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.33.0 - - @effect-app/schema@0.24.0 - -## 0.25.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.32.3 - - @effect-app/schema@0.23.3 - -## 0.25.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.32.2 - - @effect-app/schema@0.23.2 - -## 0.25.1 - -### Patch Changes - -- Cleanup - -## 0.25.0 - -### Minor Changes - -- Improve Encoded codegen - -## 0.24.2 - -### Patch Changes - -- Fix compilation -- Updated dependencies - - @effect-app/core@0.32.1 - - @effect-app/schema@0.23.1 - -## 0.24.1 - -### Patch Changes - -- fix svc tag - -## 0.24.0 - -### Minor Changes - -- Update tsplus with companion instance extensions - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.23.0 - - @effect-app/core@0.32.0 - -## 0.23.7 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.31.0 - - @effect-app/schema@0.22.4 - -## 0.23.6 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.30.3 - - @effect-app/schema@0.22.3 - -## 0.23.5 - -### Patch Changes - -- fix service tag -- Updated dependencies - - @effect-app/core@0.30.2 - - @effect-app/schema@0.22.2 - -## 0.23.4 - -### Patch Changes - -- tag workaround 3 - -## 0.23.3 - -### Patch Changes - -- Workaround tag - -## 0.23.2 - -### Patch Changes - -- tag workaround -- Updated dependencies - - @effect-app/core@0.30.1 - - @effect-app/schema@0.22.1 - -## 0.23.1 - -### Patch Changes - -- Fix service tag - -## 0.23.0 - -### Minor Changes - -- Update Effect - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.30.0 - - @effect-app/schema@0.22.0 - -## 0.22.5 - -### Patch Changes - -- Updated dependencies [00d34a69] - - @effect-app/schema@0.21.0 - -## 0.22.4 - -### Patch Changes - -- Improve curry support for PreparedLens - -## 0.22.3 - -### Patch Changes - -- Fix - -## 0.22.2 - -### Patch Changes - -- Update packages -- Updated dependencies - - @effect-app/schema@0.20.1 - - @effect-app/core@0.29.1 - -## 0.22.1 - -### Patch Changes - -- d89f88cb: add assignTag, TagClass, ServiceTaggedClass - -## 0.22.0 - -### Minor Changes - -- b967055c: Replace fp-ts/core with effect/data - -### Patch Changes - -- Updated dependencies [b967055c] - - @effect-app/schema@0.20.0 - - @effect-app/core@0.29.0 - -## 0.21.14 - -### Patch Changes - -- 28d5530b: Fix Encoded - -## 0.21.13 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.28.0 - - @effect-app/schema@0.19.4 - -## 0.21.12 - -### Patch Changes - -- Updated dependencies [39da5db8] - - @effect-app/core@0.27.0 - - @effect-app/schema@0.19.3 - -## 0.21.11 - -### Patch Changes - -- Updated dependencies [4679321f] - - @effect-app/core@0.26.0 - - @effect-app/schema@0.19.2 - -## 0.21.10 - -### Patch Changes - -- Updated dependencies [d66406c9] - - @effect-app/core@0.25.1 - - @effect-app/schema@0.19.1 - -## 0.21.9 - -### Patch Changes - -- Updated dependencies [b385e928] - - @effect-app/schema@0.19.0 - - @effect-app/core@0.25.0 - -## 0.21.8 - -### Patch Changes - -- Updated dependencies [c4b4a20a] - - @effect-app/core@0.24.1 - - @effect-app/schema@0.18.7 - -## 0.21.7 - -### Patch Changes - -- Updated dependencies [3d49bb4a] - - @effect-app/schema@0.18.6 - -## 0.21.6 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.24.0 - - @effect-app/schema@0.18.5 - -## 0.21.5 - -### Patch Changes - -- Updated dependencies [8da4c8fc] - - @effect-app/core@0.23.1 - - @effect-app/schema@0.18.4 - -## 0.21.4 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.23.0 - - @effect-app/schema@0.18.3 - -## 0.21.3 - -### Patch Changes - -- c29ab9fa: default Effect.servicesWith\* to lowercase first letter services. - -## 0.21.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.22.2 - - @effect-app/schema@0.18.2 - -## 0.21.1 - -### Patch Changes - -- Updated dependencies [df13134f] - - @effect-app/core@0.22.1 - - @effect-app/schema@0.18.1 - -## 0.21.0 - -### Minor Changes - -- Update annotations, drop Aspects for now - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.22.0 - - @effect-app/schema@0.18.0 - -## 0.20.3 - -### Patch Changes - -- Updated dependencies [e3e768a5] - - @effect-app/core@0.21.3 - - @effect-app/schema@0.17.3 - -## 0.20.2 - -### Patch Changes - -- Updated dependencies [d9751183] - - @effect-app/core@0.21.2 - - @effect-app/schema@0.17.2 - -## 0.20.1 - -### Patch Changes - -- Updated dependencies [46d8b7e1] - - @effect-app/core@0.21.1 - - @effect-app/schema@0.17.1 - -## 0.20.0 - -### Minor Changes - -- 338f1f19: Update packages - -### Patch Changes - -- Updated dependencies [338f1f19] - - @effect-app/schema@0.17.0 - - @effect-app/core@0.21.0 - -## 0.19.3 - -### Patch Changes - -- Updated dependencies [00ac8993] - - @effect-app/core@0.20.2 - - @effect-app/schema@0.16.3 - -## 0.19.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.20.1 - - @effect-app/schema@0.16.2 - -## 0.19.1 - -### Patch Changes - -- 02997e8f: FUP -- Updated dependencies [02997e8f] - - @effect-app/schema@0.16.1 - -## 0.19.0 - -### Minor Changes - -- 4d49f303: Upgrade to @effect/data - -### Patch Changes - -- Updated dependencies [4d49f303] - - @effect-app/schema@0.16.0 - - @effect-app/core@0.20.0 - -## 0.18.2 - -### Patch Changes - -- ca8b9a4e: Fix Function flow - -## 0.18.1 - -### Patch Changes - -- Updated dependencies [f8d8e4ec] - - @effect-app/core@0.19.0 - - @effect-app/schema@0.15.6 - -## 0.18.0 - -### Minor Changes - -- Update annotations and leverage ForceLazy on Effect \_\_call - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.18.0 - - @effect-app/schema@0.15.5 - -## 0.17.5 - -### Patch Changes - -- 912ba66c: Update packages -- Updated dependencies [912ba66c] - - @effect-app/schema@0.15.4 - - @effect-app/core@0.17.4 - -## 0.17.4 - -### Patch Changes - -- Updated dependencies [407cc13e] - - @effect-app/core@0.17.3 - - @effect-app/schema@0.15.3 - -## 0.17.3 - -### Patch Changes - -- e9e498ee: Update packages -- Updated dependencies [e9e498ee] - - @effect-app/schema@0.15.2 - - @effect-app/core@0.17.2 - -## 0.17.2 - -### Patch Changes - -- 33d2a60f: Cleanup - -## 0.17.1 - -### Patch Changes - -- Updated dependencies [8b10e403] - - @effect-app/core@0.17.1 - - @effect-app/schema@0.15.1 - -## 0.17.0 - -### Minor Changes - -- 9f9b2d32: Update effect and fp-ts - -### Patch Changes - -- Updated dependencies [9f9b2d32] - - @effect-app/schema@0.15.0 - - @effect-app/core@0.17.0 - -## 0.16.1 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.16.1 - - @effect-app/schema@0.14.1 - -## 0.16.0 - -### Minor Changes - -- 67716050: Update to TS5 beta - -### Patch Changes - -- Updated dependencies [67716050] - - @effect-app/schema@0.14.0 - - @effect-app/core@0.16.0 - -## 0.15.1 - -### Patch Changes - -- 5a69e08a: Reclaim Duration -- Updated dependencies [5a69e08a] - - @effect-app/core@0.15.1 - - @effect-app/schema@0.13.1 - -## 0.15.0 - -### Minor Changes - -- 25ec7aff: Reclaim the Option - -### Patch Changes - -- Updated dependencies [25ec7aff] - - @effect-app/schema@0.13.0 - - @effect-app/core@0.15.0 - -## 0.14.2 - -### Patch Changes - -- Updated dependencies [90d6e600] - - @effect-app/core@0.14.2 - - @effect-app/schema@0.12.9 - -## 0.14.1 - -### Patch Changes - -- Updated dependencies [8f97d979] - - @effect-app/core@0.14.1 - - @effect-app/schema@0.12.8 - -## 0.14.0 - -### Minor Changes - -- e4730778: Test dual api for Optic - -### Patch Changes - -- Updated dependencies [e4730778] - - @effect-app/core@0.14.0 - - @effect-app/schema@0.12.7 - -## 0.13.0 - -### Minor Changes - -- e7b4a0b9: Cleanup extensions - -### Patch Changes - -- Updated dependencies [e7b4a0b9] - - @effect-app/core@0.13.0 - - @effect-app/schema@0.12.6 - -## 0.12.5 - -### Patch Changes - -- Updated dependencies [a1bbd31f] - - @effect-app/schema@0.12.5 - -## 0.12.4 - -### Patch Changes - -- Updated dependencies [ffd11b1c] - - @effect-app/schema@0.12.4 - - @effect-app/core@0.12.4 - -## 0.12.3 - -### Patch Changes - -- 602ffbce: Update packages -- Updated dependencies [602ffbce] - - @effect-app/schema@0.12.3 - - @effect-app/core@0.12.3 - -## 0.12.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.12.2 - - @effect-app/schema@0.12.2 - -## 0.12.1 - -### Patch Changes - -- Disable trace -- Updated dependencies - - @effect-app/schema@0.12.1 - - @effect-app/core@0.12.1 - -## 0.12.0 - -### Minor Changes - -- Update to effect and fp-ts 0.1 line - -### Patch Changes - -- Updated dependencies - - @effect-app/schema@0.12.0 - - @effect-app/core@0.12.0 - -## 0.11.6 - -### Patch Changes - -- Fix ServiceBus should inherit runtime context - -## 0.11.5 - -### Patch Changes - -- e9a667fe: Add Operations - -## 0.11.4 - -### Patch Changes - -- 387dea6e: Fix Cache MapKey - -## 0.11.3 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.11.3 - - @effect-app/schema@0.11.3 - -## 0.11.2 - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.11.2 - - @effect-app/schema@0.11.2 - -## 0.11.1 - -### Patch Changes - -- 5ee19e1e: Improve exports -- Updated dependencies [5ee19e1e] - - @effect-app/core@0.11.1 - - @effect-app/schema@0.11.1 - -## 0.11.0 - -### Minor Changes - -- Remove @effect-ts/\* and replace monocle with fp-ts/optic - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.11.0 - - @effect-app/schema@0.11.0 - -## 0.10.2 - -### Patch Changes - -- Updated dependencies [cc4b37ea] - - @effect-app/core@0.10.2 - - @effect-app/schema@0.10.2 - -## 0.10.1 - -### Patch Changes - -- Updated dependencies [c4b95c0d] - - @effect-app/core@0.10.1 - - @effect-app/schema@0.10.1 - -## 0.10.0 - -### Minor Changes - -- Removed more of @effect-ts/core legacy - -### Patch Changes - -- Updated dependencies - - @effect-app/core@0.10.0 - - @effect-app/schema@0.10.0 - -## 0.9.0 - -### Minor Changes - -- 7d445f3e: Adopt Equivalence and Equal from @effect/data - -### Patch Changes - -- Updated dependencies [7d445f3e] - - @effect-app/schema@0.9.0 - - @effect-app/core@0.9.0 - -## 0.8.0 - -### Minor Changes - -- bba14672: Update to latest @effect/io and @effect/data - -### Patch Changes - -- Updated dependencies [bba14672] - - @effect-app/schema@0.8.0 - - @effect-app/core@0.8.0 - -## 0.7.1 - -### Patch Changes - -- Cleanup infra packages diff --git a/packages/effect-app/package.json b/packages/effect-app/package.json deleted file mode 100644 index fe0661b71..000000000 --- a/packages/effect-app/package.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "name": "effect-app", - "version": "4.0.1", - "license": "MIT", - "type": "module", - "repository": { - "type": "git", - "url": "https://github.com/effect-app/libs.git", - "directory": "packages/effect-app" - }, - "dependencies": { - "@tsconfig/strictest": "^2.0.8", - "nanoid": "^5.1.11", - "path-parser": "^6.1.0", - "query-string": "^9.4.0", - "ts-toolbelt": "^9.6.0", - "validator": "^13.15.35" - }, - "devDependencies": { - "@faker-js/faker": "^8.4.1", - "@types/node": "25.9.1", - "@types/validator": "^13.15.10", - "fast-check": "^4.9.0", - "typescript": "~6.0.3", - "vitest": "^4.1.7" - }, - "peerDependencies": { - "effect": "^4.0.2" - }, - "typesVersions": { - "*": { - "*": [ - "dist/*" - ] - } - }, - "exports": { - ".": { - "types": "./src/index.ts", - "default": "./src/index.ts" - }, - "./internal/*": null, - "./*": { - "types": "./src/*.ts", - "default": "./src/*.ts" - } - }, - "publishConfig": { - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./internal/*": null, - "./*": { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" - } - }, - "directory": ".publish", - "linkDirectory": false - }, - "scripts": { - "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && pnpm check && node ../../scripts/rewrite-dist-declaration-imports.mjs dist", - "check": "tsgo --build", - "build:tsc-src": "pnpm clean-dist && tsc --build ./tsconfig.src.json && node ../../scripts/rewrite-dist-declaration-imports.mjs dist", - "build:src": "pnpm build:tsc-src", - "build": "pnpm build:tsc", - "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsgo -w", - "clean": "rm -rf dist", - "clean-dist": "sh ../../scripts/clean-dist.sh", - "circular": "pnpm circular:src && pnpm circular:dist", - "circular:src": "madge --circular --ts-config ./tsconfig.json --extensions ts ./src", - "circular:dist": "madge --circular --extensions js ./dist", - "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsgo --noEmit", - "lint": "oxlint --type-aware ./src && pnpm exec dprint check --config ../../dprint.jsonc .", - "lint-fix": "oxlint --type-aware --fix ./src && pnpm exec dprint fmt --config ../../dprint.jsonc .", - "test": "vitest", - "test:run": "pnpm run test run --passWithNoTests", - "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", - "ncu": "ncu", - "pub": "pnpm publish --access public", - "prepack": "node ../../scripts/stagePublishPackage.mjs prepack", - "postpack": "node ../../scripts/stagePublishPackage.mjs postpack", - "postpublish": "node ../../scripts/stagePublishPackage.mjs postpublish" - }, - "gitHead": "bd8e27eea3eff97db8739d577d67e7336c078d28", - "sideEffects": [ - "./dist/faker.js", - "./faker", - "./src/faker", - "./dist/faker", - "./dist/src/faker.js", - "./dist/faker.js" - ] -} diff --git a/packages/effect-app/src/Array.ts b/packages/effect-app/src/Array.ts deleted file mode 100644 index 3ec7dca36..000000000 --- a/packages/effect-app/src/Array.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect/Array" -import * as Array from "effect/Array" -import * as Effect from "effect/Effect" -import { dual, type Predicate } from "./Function.ts" -import * as Option from "./Option.ts" - -export const groupByT = dual< - ( - f: (a: NoInfer) => Key - ) => (as: ReadonlyArray) => Array]>, - ( - as: ReadonlyArray, - f: (a: A) => Key - ) => Array]> ->(2, ( - as: ReadonlyArray, - f: (a: A) => Key -): Array]> => { - const r = new Map>() - for (const a of as) { - const k = f(a) - if (r.has(k)) { - r.get(k)!.push(a) - } else { - r.set(k, [a]) - } - } - return [...r.entries()] -}) - -export const groupByTNonEmpty = dual< - ( - f: (a: NoInfer) => Key - ) => (as: NonEmptyReadonlyArray) => NonEmptyArray]>, - ( - as: NonEmptyReadonlyArray, - f: (a: A) => Key - ) => NonEmptyArray]> ->(2, ( - as: NonEmptyReadonlyArray, - f: (a: A) => Key -): NonEmptyArray]> => { - const r = new Map>() - for (const a of as) { - const k = f(a) - if (r.has(k)) { - r.get(k)!.push(a) - } else { - r.set(k, [a]) - } - } - return [...r.entries()] as unknown as NonEmptyArray]> -}) - -// const a = [1, 2, 3] as const -// const b = [1, 2, 3] - -// const a1 = groupByTNonEmpty(a, (x) => x % 2) // $ExpectType NonEmptyReadonlyArray]> -// const b1 = groupByT(b, (x) => x % 2) // $ExpectType Array]> - -export function randomElement(a: NonEmptyReadonlyArray): A -export function randomElement(a: ReadonlyArray): A | undefined -export function randomElement(a: ReadonlyArray): A | undefined { - return a[Math.floor(Math.random() * a.length)] -} - -export function filterWith(self: ReadonlyArray, predicates: ReadonlyArray>) { - return self.filter((_) => predicates.every((f) => f(_))) -} - -export function forEachEffectNA(as: NonEmptyReadonlyArray, f: (a: A) => Effect.Effect) { - return Effect.map( - Effect.forEach(as, f), - (_) => Option.getOrNull(Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(_)) - ) -} - -export * from "effect/Array" diff --git a/packages/effect-app/src/Chunk.ts b/packages/effect-app/src/Chunk.ts deleted file mode 100644 index bde5c8a49..000000000 --- a/packages/effect-app/src/Chunk.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { pipe } from "./Function.ts" -import * as Option from "./Option.ts" - -import * as Chunk from "effect/Chunk" -import * as Array from "./Array.ts" - -export * from "effect/Chunk" - -export function groupByTChunk_(c: Chunk.Chunk, f: (a: A) => Key) { - return pipe(Chunk.toReadonlyArray(c), Array.groupByT(f), Chunk.fromIterable) -} - -/** - * Returns the first `Some` produced by `f`. - * - * Effect's `Chunk.findFirst` takes a predicate, not an `Option`-returning function. - */ -export function findFirstMap( - f: (a: A) => Option.Option -) { - return (as: Chunk.Chunk) => { - const ass = Chunk.toReadonlyArray(as) - const len = ass.length - for (let i = 0; i < len; i++) { - const v = f(ass[i]!) - if (Option.isSome(v)) { - return v - } - } - return Option.none() - } -} diff --git a/packages/effect-app/src/Config.ts b/packages/effect-app/src/Config.ts deleted file mode 100644 index 1dc6d489e..000000000 --- a/packages/effect-app/src/Config.ts +++ /dev/null @@ -1,7 +0,0 @@ -// `Config.nested` previously needed a local override because effect v3's `nested` -// did not run the provider's `mapInput`/`constantCase` over the nested namespace -// segment (see commit "fix Config.nested mapping"). As of effect 4.0.0-beta.84 the -// upstream `nested` threads the prefix through the lookup path, so the provider -// transform covers it — the override is obsolete and the old internals it used are -// gone. Re-export the native module directly. -export * from "effect/Config" diff --git a/packages/effect-app/src/Config/SecretURL.ts b/packages/effect-app/src/Config/SecretURL.ts deleted file mode 100644 index 684db9396..000000000 --- a/packages/effect-app/src/Config/SecretURL.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @since 1.0.0 - */ -import type * as Chunk from "effect/Chunk" -import * as Config from "effect/Config" -import type * as Equal from "effect/Equal" -import type * as Redacted from "effect/Redacted" -import * as internal from "./internal/configSecretURL.ts" - -// /** -// * @since 1.0.0 -// * @category symbols -// */ -// export const SecretURLTypeId: unique symbol = internal.SecretURLTypeId - -// /** -// * @since 1.0.0 -// * @category symbols -// */ -// export type SecretURLTypeId = typeof SecretURLTypeId - -/** - * @since 1.0.0 - * @category models - */ -// eslint-disable-next-line import/namespace -export interface SecretURL extends Redacted.Redacted, Equal.Equal { - /** @internal */ - readonly raw: Array -} - -export interface SecretURLOps {} - -export const SecretURL: SecretURLOps = {} - -/** - * @since 1.0.0 - */ -export declare namespace SecretURL {} - -/** - * @since 1.0.0 - * @category refinements - * @deprecated - */ -export const isSecretURL: (u: unknown) => u is SecretURL = internal.isSecretURL - -/** - * @since 1.0.0 - * @category constructors - */ -export const make: (bytes: Array) => SecretURL = internal.make - -/** - * @since 1.0.0 - * @category constructors - */ -export const fromChunk: (chunk: Chunk.Chunk) => SecretURL = internal.fromChunk - -/** - * @since 1.0.0 - * @category constructors - */ -export const fromString: (text: string) => SecretURL = internal.fromString - -/** - * @since 1.0.0 - * @category getters - */ -export const value: (self: SecretURL) => string = internal.value - -/** - * @since 1.0.0 - * @category unsafe - */ -export const unsafeWipe: (self: SecretURL) => void = internal.unsafeWipe - -export const secretURL = (name?: string): Config.Config => { - return Config.map(Config.NonEmptyString(name), fromString) -} diff --git a/packages/effect-app/src/Config/internal/configSecretURL.ts b/packages/effect-app/src/Config/internal/configSecretURL.ts deleted file mode 100644 index 36a3aa5b7..000000000 --- a/packages/effect-app/src/Config/internal/configSecretURL.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as Chunk from "effect/Chunk" -import * as Redacted from "effect/Redacted" -import type * as SecretURL from "../SecretURL.ts" - -/** @internal */ -export const isSecretURL = (u: unknown): u is SecretURL.SecretURL => { - return Redacted.isRedacted(u) && typeof (u as any).raw !== "undefined" -} - -/** @internal */ -export const make = (bytes: Array): SecretURL.SecretURL => { - const secret = Object.assign( - Redacted.make(bytes.map((byte) => String.fromCharCode(byte)).join("")), - { raw: undefined as any } as const - ) - let protocol = "unknown" - try { - const url = new URL(bytes.map((byte) => String.fromCharCode(byte)).join("")) - protocol = url.protocol.substring(0, url.protocol.length - 1) - } catch { - const matches = protocol.match(/^([^:]+):\/\//) - if (matches !== null) { - protocol = matches[1]! - } - } - Object.defineProperty(secret, "toJSON", { - enumerable: false, - value() { - return ({ - _tag: "SecretURL", - protocol - }) - } - }) - Object.defineProperty(secret, "toString", { - enumerable: false, - value() { - return `SecretURL(${protocol}://)` - } - }) - Object.defineProperty(secret, "raw", { - enumerable: false, - value: bytes - }) - return secret -} - -/** @internal */ -export const fromChunk = (chunk: Chunk.Chunk): SecretURL.SecretURL => { - return make(Chunk.toReadonlyArray(chunk).map((char) => char.charCodeAt(0))) -} - -/** @internal */ -export const fromString = (text: string): SecretURL.SecretURL => { - return make(text.split("").map((char) => char.charCodeAt(0))) -} - -/** @internal */ -export const value = (self: SecretURL.SecretURL): string => { - return self.raw.map((byte) => String.fromCharCode(byte)).join("") -} - -/** @internal */ -export const unsafeWipe = (self: SecretURL.SecretURL): void => { - for (let i = 0; i < self.raw.length; i++) { - self.raw[i] = 0 - } -} diff --git a/packages/effect-app/src/ConfigProvider.ts b/packages/effect-app/src/ConfigProvider.ts deleted file mode 100644 index 881a627a2..000000000 --- a/packages/effect-app/src/ConfigProvider.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Local `nested` override removed: it existed to make the nested namespace honour the -// provider's `mapInput`/`constantCase` (see commit "fix Config.nested mapping"), which -// effect 4.0.0-beta.84 now does natively. The old override also relied on internals -// (`provider.get`/`mapInput`/`prefix`) that no longer exist. Re-export native directly. -export * from "effect/ConfigProvider" diff --git a/packages/effect-app/src/Context.ts b/packages/effect-app/src/Context.ts deleted file mode 100644 index 8846a8fa8..000000000 --- a/packages/effect-app/src/Context.ts +++ /dev/null @@ -1,181 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable import/namespace */ - -import * as CTX from "effect/Context" -import type * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import type * as Scope from "effect/Scope" -import type * as Types from "effect/Types" - -export * from "effect/Context" - -export interface Opaque extends CTX.Key { - of(this: void, self: Shape): Self - context(self: Shape): CTX.Context - // a version that leverages the Shape -> Self conversion - toLayer: ( - eff: Effect.Effect - ) => Layer.Layer> - use(f: (service: Shape) => Effect.Effect): Effect.Effect - useSync(f: (service: Shape) => A): Effect.Effect -} - -// export interface OpaqueMake -// extends CTX.Service -// { -// // temp while sorting out https://github.com/Effect-TS/effect-smol/pull/1534 -// of(self: Shape): Self -// contextMap2(self: Shape): CTX.Context -// // a version that leverages the Shape -> Self conversion -// toLayer: { -// ( -// eff: Effect.Effect -// ): Layer.Layer> -// (): Layer.Layer> -// } -// } - -export function assignTag( - key: string, - creationError?: Error -) { - return (cls: S): S & Opaque => { - const tag = CTX.Service(key) - let fields = tag - if (Reflect.ownKeys(cls).includes("key")) { - const { key, ...rest } = tag - fields = rest as any - } - const t = Object.assign(cls, Object.getPrototypeOf(tag), fields) - if (!creationError) { - const limit = Error.stackTraceLimit - Error.stackTraceLimit = 2 - creationError = new Error() - Error.stackTraceLimit = limit - } - // the stack is used to get the location of the tag definition, if a service is not found in the registry - Object.defineProperty(t, "stack", { - get() { - return creationError!.stack - } - }) - return t - } -} - -/** Accessor for a service method that returns a plain value. Wraps via `useSync`. */ -export const accessFn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Shape[K] extends (...args: [...infer Args]) => infer A ? (...args: Readonly) => Effect.Effect - : never => ((...args: Array) => Tag.useSync((s: any) => s[key](...args))) as any - -/** Accessor for a service method that returns an Effect. Delegates via `use`. */ -export const accessEffectFn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Shape[K] extends (...args: [...infer Args]) => Effect.Effect - ? (...args: Readonly) => Effect.Effect - : never => ((...args: Array) => Tag.use((s: any) => s[key](...args))) as any - -/** Accessor for a service property (constant). Wraps via `useSync`. */ -export const accessCn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Effect.Effect => Tag.useSync((s) => s[key]) - -/** Accessor for a service property that is an Effect. Delegates via `use`. */ -export const accessEffectCn = < - Self extends object, - Shape extends Record, - K extends keyof Shape ->( - Tag: Opaque, - key: K -): Shape[K] extends Effect.Effect ? Effect.Effect - : never => Tag.use((s: any) => s[key]) as any - -export const TypeId = "~Context.Opaque" - -// export function Opaque(key: Key) { -// return () => { -// const limit = Error.stackTraceLimit -// Error.stackTraceLimit = 2 -// const creationError = new Error() -// Error.stackTraceLimit = limit -// const c: abstract new(_: never) => Shape & { readonly [TypeId]: Key } = class {} as any - -// return assignTag(key, creationError)(c) -// } -// } - -export interface OpaqueClass - extends Opaque -{ - new(_: never): Shape & { readonly [TypeId]: Identifier } - readonly key: Identifier -} - -// export interface OpaqueClassMake -// extends OpaqueMake -// { -// new(_: never): Shape & { readonly [TypeId]: Identifier } -// readonly key: Identifier -// } - -export const Opaque: { - (): < - const Identifier extends string, - E, - R = Types.unassigned, - Args extends ReadonlyArray = never - >( - id: Identifier, - options?: { - readonly make: ((...args: Args) => Effect.Effect) | Effect.Effect | undefined - } - ) => - & OpaqueClass - & ([Types.unassigned] extends [R] ? unknown - : { - readonly make: [Args] extends [never] ? Effect.Effect - : (...args: Args) => Effect.Effect - }) - (): < - const Identifier extends string, - Make extends Effect.Effect | ((...args: any) => Effect.Effect) - >( - id: Identifier, - options: { - readonly make: Make - } - ) => - & OpaqueClass< - Self, - Identifier, - Make extends - | Effect.Effect - | ((...args: infer _Args) => Effect.Effect) ? _A - : never - > - & { readonly make: Make } -} = () => (id: string, options: any) => { - const svc = CTX.Service()(id, options) as any - return Object.assign(svc, { - toLayer: (eff: Effect.Effect) => { - return Layer.effect(svc, eff) - } - }) -} diff --git a/packages/effect-app/src/DataDependencies.ts b/packages/effect-app/src/DataDependencies.ts deleted file mode 100644 index 920fc7cb2..000000000 --- a/packages/effect-app/src/DataDependencies.ts +++ /dev/null @@ -1,171 +0,0 @@ -import type { NonEmptyReadonlyArray } from "effect/Array" -import * as Ref from "effect/Ref" -import * as Stream from "effect/Stream" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as RequestScopedDependencies from "./RequestScopedDependencies.ts" -import * as S from "./Schema.ts" - -export const DataDependency = S.Union([ - S.Struct({ - type: S.Literal("repo"), - name: S.String, - ids: S.optional(S.NonEmptyArray(S.String)) - }), - S.Struct({ - type: S.Literal("signal"), - name: S.String, - ids: S.optional(S.NonEmptyArray(S.String)) - }) -]) -export type DataDependency = S.Schema.Type - -export const DataDependencies = S.ReadonlySet(DataDependency) -export type DataDependencies = S.Schema.Type - -export const empty = (): DataDependencies => new Set() - -export const DataDependencySet = S.Struct({ - reads: DataDependencies, - writes: DataDependencies -}) -export type DataDependencySet = S.Schema.Type - -export interface DataDependencyRecorderService { - readonly read: (dependency: DataDependency) => Effect.Effect - readonly write: (dependency: DataDependency) => Effect.Effect - readonly get: Effect.Effect - readonly drain: Effect.Effect - readonly drainWrites: Effect.Effect -} - -const sameDomain = (a: DataDependency, b: DataDependency) => a.type === b.type && a.name === b.name - -const dependenciesOverlap = (a: DataDependency, b: DataDependency) => { - if (!sameDomain(a, b)) return false - if (a.ids === undefined || b.ids === undefined) return true - return a.ids.some((id) => b.ids?.includes(id)) -} - -const containsDependency = (dependencies: ReadonlySet, dependency: DataDependency) => { - for (const _ of dependencies) { - if (dependenciesOverlap(_, dependency)) return true - } - return false -} - -const appendDependency = (dependency: DataDependency) => (dependencies: DataDependencies): DataDependencies => { - const existing = [...dependencies].find((_) => sameDomain(_, dependency)) - if (existing === undefined) return new Set([...dependencies, dependency]) - if (existing.ids === undefined) return dependencies - if (dependency.ids === undefined) { - return new Set([...dependencies].filter((_) => _ !== existing).concat(dependency)) - } - const first = existing.ids[0] - const ids: NonEmptyReadonlyArray = [ - first, - ...new Set([...existing.ids.slice(1), ...dependency.ids].filter((id) => id !== first)) - ] - if (ids.length === existing.ids.length) return dependencies - const merged = existing.type === "repo" ? repo(existing.name, ids) : signal(existing.name, ids) - return new Set([...dependencies].filter((_) => _ !== existing).concat(merged)) -} - -export const merge = (...groups: ReadonlyArray): DataDependencies => { - let merged = empty() - for (const group of groups) { - for (const dependency of group) merged = appendDependency(dependency)(merged) - } - return merged -} - -export const DataDependencyRecorder = RequestScopedDependencies.make( - "effect-app/DataDependencyRecorder", - Effect.gen(function*() { - const readsRef = yield* Ref.make(empty()) - const writesRef = yield* Ref.make(empty()) - return makeDataDependencyRecorder(readsRef, writesRef) - }) -) -export type DataDependencyRecorder = typeof DataDependencyRecorder - -export type DataDependencyRecorderNotStartedError = RequestScopedDependencies.RequestScopedDependencyNotStartedError - -export const getDataDependencyRecorder = DataDependencyRecorder.current - -/** - * Guarantee a recorder is in scope for `self`, providing a fresh one per request when none is - * (e.g. a top-level client call with no parent query/command tracking its dependencies). An - * existing recorder is inherited, so nested calls forward into their parent rather than shadow it. - */ -export const ensureDataDependencyRecorder = (self: Effect.Effect): Effect.Effect => - Effect.flatMap( - DataDependencyRecorder, - (current) => current === "root" ? Effect.provide(self, DataDependencyRecorder.layer) : self - ) - -/** Stream variant of {@link ensureDataDependencyRecorder}. */ -export const ensureDataDependencyRecorderStream = (self: Stream.Stream): Stream.Stream => - Stream.unwrap( - Effect.map( - DataDependencyRecorder, - (current) => current === "root" ? Stream.provide(self, DataDependencyRecorder.layer) : self - ) - ) - -export const makeDataDependencyRecorder = ( - readsRef: Ref.Ref, - writesRef: Ref.Ref -): DataDependencyRecorderService => ({ - read: (dependency) => Ref.update(readsRef, appendDependency(dependency)), - write: (dependency) => Ref.update(writesRef, appendDependency(dependency)), - get: Effect.all({ - reads: Ref.get(readsRef), - writes: Ref.get(writesRef) - }), - drain: Effect.all({ - reads: Ref.getAndSet(readsRef, empty()), - writes: Ref.getAndSet(writesRef, empty()) - }), - drainWrites: Ref.getAndSet(writesRef, empty()) -}) - -export const repo = (name: string, ids?: NonEmptyReadonlyArray): DataDependency => - ids === undefined ? { type: "repo", name } : { type: "repo", name, ids } -export const signal = (name: string, ids?: NonEmptyReadonlyArray): DataDependency => - ids === undefined ? { type: "signal", name } : { type: "signal", name, ids } - -const RepoReadScope = Context.Reference("effect-app/DataDependencies/RepoReadScope", { - defaultValue: () => undefined -}) - -export const readRepo = (name: string) => - Effect.flatMap( - RepoReadScope, - (scope) => read(scope?.type === "repo" && scope.name === name ? scope : repo(name)) - ) - -export const withRepoReadScope = - (name: string, ids: NonEmptyReadonlyArray) => - (self: Effect.Effect): Effect.Effect => - Effect.provideService(self, RepoReadScope, repo(name, ids)) - -export const QueryReadDependenciesMetaKey = "effect-app.query.readDependencies" - -export const read = (dependency: DataDependency) => - getDataDependencyRecorder.pipe(Effect.flatMap((_) => _.read(dependency))) - -export const write = (dependency: DataDependency) => - getDataDependencyRecorder.pipe(Effect.flatMap((_) => _.write(dependency))) - -export const intersects = ( - a: ReadonlySet, - b: ReadonlySet -) => { - for (const dependency of a) { - if (containsDependency(b, dependency)) return true - } - return false -} - -export const isNonEmpty = (dependencies: DataDependencies) => dependencies.size > 0 diff --git a/packages/effect-app/src/DateTime.ts b/packages/effect-app/src/DateTime.ts deleted file mode 100644 index 9b0bdad7b..000000000 --- a/packages/effect-app/src/DateTime.ts +++ /dev/null @@ -1,473 +0,0 @@ -import * as DateTime from "effect/DateTime" - -/** - * The same instant as `date`, zoned with the process timezone. - * - * A JavaScript `Date` stores no zone. This attaches `DateTime.zoneMakeLocal()` without moving the instant. - */ -export const makeZonedLocal = (date: Date): DateTime.Zoned => - DateTime.makeZonedUnsafe(date, { timeZone: DateTime.zoneMakeLocal() }) - -/** - * Zones `date` with the process timezone, runs the functions, then returns the instant with `DateTime.toDateUtc`. - * - * Accepts as many functions as `pipe`. The last function must return a `DateTime`. Calendar parts follow the process timezone, as `Date#setDate` does. - */ -export function pipeZonedLocal(date: Date): Date -export function pipeZonedLocal( - date: Date, - ab: (self: DateTime.Zoned) => A -): Date -export function pipeZonedLocal( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B -): Date -export function pipeZonedLocal( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M = never, - N extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M, - no: (m: M) => N -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M = never, - N = never, - O extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M, - no: (m: M) => N, - op: (n: N) => O -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M = never, - N = never, - O = never, - P extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M, - no: (m: M) => N, - op: (n: N) => O, - pq: (o: O) => P -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M = never, - N = never, - O = never, - P = never, - Q extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M, - no: (m: M) => N, - op: (n: N) => O, - pq: (o: O) => P, - qr: (p: P) => Q -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M = never, - N = never, - O = never, - P = never, - Q = never, - R extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M, - no: (m: M) => N, - op: (n: N) => O, - pq: (o: O) => P, - qr: (p: P) => Q, - rs: (q: Q) => R -): Date -export function pipeZonedLocal< - A = never, - B = never, - C = never, - D = never, - E = never, - F = never, - G = never, - H = never, - I = never, - J = never, - K = never, - L = never, - M = never, - N = never, - O = never, - P = never, - Q = never, - R = never, - S extends DateTime.DateTime = never ->( - date: Date, - ab: (self: DateTime.Zoned) => A, - bc: (a: A) => B, - cd: (b: B) => C, - de: (c: C) => D, - ef: (d: D) => E, - fg: (e: E) => F, - gh: (f: F) => G, - hi: (g: G) => H, - ij: (h: H) => I, - jk: (i: I) => J, - kl: (j: J) => K, - lm: (k: K) => L, - mn: (l: L) => M, - no: (m: M) => N, - op: (n: N) => O, - pq: (o: O) => P, - qr: (p: P) => Q, - rs: (q: Q) => R, - st: (r: R) => S -): Date -export function pipeZonedLocal(date: Date, ...steps: ReadonlyArray<(value: any) => any>): Date { - let current: unknown = makeZonedLocal(date) - for (const step of steps) { - current = step(current) - } - if (!DateTime.isDateTime(current)) { - throw new TypeError("pipeZonedLocal must finish on a DateTime") - } - return DateTime.toDateUtc(current) -} diff --git a/packages/effect-app/src/Effect.ts b/packages/effect-app/src/Effect.ts deleted file mode 100644 index b84056095..000000000 --- a/packages/effect-app/src/Effect.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable prefer-destructuring */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars - -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Ref from "effect/Ref" -import type { Semaphore } from "effect/Semaphore" -import * as Struct from "effect/Struct" -import type * as Context from "./Context.ts" -import { curry } from "./Function.ts" - -export * from "effect/Effect" -// v4: Effect interface not re-exported by `export *` due to local binding collision -export type { Effect } from "effect/Effect" - -export function flatMapOption( - self: Effect.Effect, E, R>, - fm: (a: A) => Effect.Effect -): Effect.Effect, E | E2, R | R2> { - return Effect.flatMap(self, (d) => - Option.match(d, { - onNone: () => Effect.sync(() => Option.none()), - onSome: (_) => Effect.map(fm(_), Option.some) - })) -} - -export function tapOption( - self: Effect.Effect, E, R>, - fm: (a: A) => Effect.Effect -): Effect.Effect, E | E2, R | R2> { - return Effect.flatMap(self, (d) => - Option.match(d, { - onNone: () => Effect.sync(() => Option.none()), - onSome: (_) => Effect.map(fm(_), () => Option.some(_)) - })) -} - -export function zipRightOption( - self: Effect.Effect, E, R>, - fm: Effect.Effect -) { - return Effect.flatMap(self, (d) => - Option.match(d, { - onNone: () => Effect.sync(() => Option.none()), - onSome: (_) => Effect.map(fm, () => Option.some(_)) - })) -} - -export function mapOption( - self: Effect.Effect, E, R>, - fm: (a: A) => A2 -): Effect.Effect, E, R> { - return Effect.map(self, (d) => - Option.match(d, { - onNone: () => Option.none(), - onSome: (_) => Option.some(fm(_)) - })) -} - -/** - * Takes [A, B], applies it to a curried Effect function, - * taps the Effect, returning A. - */ -export function tupleTap( - f: (b: B) => (a: A) => Effect.Effect -) { - return (t: readonly [A, B]) => Effect.sync(() => t[0]).pipe(Effect.tap(f(t[1]))) -} - -/** - * Takes [A, B], applies it to an Effect function, - * taps the Effect, returning A. - */ -export function tupleTap_(f: (a: A, b: B) => Effect.Effect) { - return tupleTap(curry(f)) -} - -export function ifDiffR(f: (i: I) => Effect.Effect) { - return (n: I, orig: I) => ifDiff_(n, orig, f) -} - -export function ifDiff_( - n: I, - orig: I, - f: (i: I) => Effect.Effect -) { - return n !== orig ? f(n) : Effect.void -} - -export function ifDiff(n: I, orig: I) { - return (f: (i: I) => Effect.Effect) => ifDiff_(n, orig, f) -} - -/** - * Ref has atomic modify support if synchronous, for Effect we need a Semaphore. - */ -export function modifyWithPermitWithEffect(ref: Ref.Ref, semaphore: Semaphore) { - const withPermit = semaphore.withPermits(1) - return (mod: (a: A) => Effect.Effect) => - withPermit( - Effect - .flatMap(Ref.get(ref), mod) - .pipe( - Effect.tap(([, _]) => Ref.set(ref, _)), - Effect.map(([_]) => _) - ) - ) -} - -type ServiceA = T extends Effect.Effect ? S - : T extends Context.Service ? S - : never -type ServiceR = T extends Effect.Effect ? R - : T extends Context.Service ? I - : never -type ServiceE = T extends Effect.Effect ? E : never -// type Values = T extends { [s: string]: infer S } ? ServiceA : never -type ValuesR = T extends { [s: string]: infer S } ? ServiceR : never -type ValuesE = T extends { [s: string]: infer S } ? ServiceE : never - -/** - * Due to tsplus unification (tsplus unify tag), when trying to use the Effect type in a type constraint - * the compiler will cause basically anything to match. as such, use this type instead. - * ```ts - * const a = < - * SVC extends Record< - * string, - * ((req: number) => Effect.Effect) | Effect.Effect - * > - * >(svc: SVC) => svc - * - * const b = a({ str: "" }) // valid, but shouldn't be! - * ``` - */ -// eslint-disable-next-line import/namespace -export interface EffectUnunified extends Effect.Effect {} - -export type LowerFirst = S extends `${infer First}${infer Rest}` ? `${Lowercase}${Rest}` - : S -export type LowerServices | Effect.Effect>> = { - [key in keyof T as LowerFirst]: ServiceA -} - -export function allLower | Effect.Effect>>( - services: T -) { - return Effect.all( - Struct.keys(services).reduce((prev, cur) => { - const svc = services[cur]! - prev[(cur[0]!.toLowerCase() + cur.slice(1)) as unknown as LowerFirst] = svc - return prev - }, {} as any), - { concurrency: "unbounded" } - ) as any as Effect.Effect, ValuesE, ValuesR> -} - -export function allLowerWith | Effect.Effect>, A>( - services: T, - fn: (services: LowerServices) => A -) { - return Effect.map(allLower(services), fn) -} - -export function allLowerWithEffect< - T extends Record | Effect.Effect>, - R, - E, - A ->( - services: T, - fn: (services: LowerServices) => Effect.Effect -) { - return Effect.flatMap(allLower(services), fn) -} - -/** - * Recovers from all errors. - */ -export function catchAllMap(f: (e: E) => A2) { - return (self: Effect.Effect): Effect.Effect => - Effect.catch(self, (err: E) => Effect.sync(() => f(err))) -} diff --git a/packages/effect-app/src/Emailer.ts b/packages/effect-app/src/Emailer.ts deleted file mode 100644 index 1fe5eab07..000000000 --- a/packages/effect-app/src/Emailer.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as Data from "effect/Data" -import type { NonEmptyReadonlyArray } from "./Array.ts" -import * as Context from "./Context.ts" -import type * as Effect from "./Effect.ts" -import type { Email } from "./Schema.ts" - -export class SendMailError extends Data.TaggedError("SendMailError")<{ - readonly raw: Error -}> {} - -export class Emailer extends Context.Opaque Effect.Effect -}>()("effect-app/Emailer") {} - -export type EmailData = Email | { - name?: string - email: Email -} - -export interface EmailContentPart { - type: string - value: string -} - -export type EmailRecipients = EmailData | NonEmptyReadonlyArray - -export interface EmailMsgBase { - readonly to: EmailRecipients - readonly cc?: EmailRecipients - readonly bcc?: EmailRecipients - readonly from: EmailData - readonly replyTo?: EmailData - readonly subject?: string - /** - * should multiple `to` addresess be considered multiple emails? - * defaults to `true`, not to leak email addresses - */ - readonly isMultiple?: boolean -} - -export type EmailContent = - | { text: string } - | { html: string } - | { templateId: string } - | { content: NonEmptyReadonlyArray } - -export type EmailMsg = - & EmailMsgBase - & EmailContent - -export type EmailMsgOptionalFrom = Omit & Partial> & EmailContent diff --git a/packages/effect-app/src/Function.ts b/packages/effect-app/src/Function.ts deleted file mode 100644 index 567367d23..000000000 --- a/packages/effect-app/src/Function.ts +++ /dev/null @@ -1,168 +0,0 @@ -export * from "effect/Function" -export type { Predicate, Refinement } from "effect/Predicate" - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -export function tupledCurry(f: (b: B) => (a: A) => C) { - return (t: [A, B]) => f(t[1])(t[0]) -} - -export function reverseCurry(f: (b: B) => (a: A) => C) { - return (a: A) => (b: B) => f(b)(a) -} - -export function curry(f: (a: A, b: B) => C) { - return (b: B) => (a: A) => f(a, b) -} - -export function uncurry(f: (b: B) => (a: A) => C) { - return (a: A, b: B) => f(b)(a) -} - -// It's better to have these expanded, so that on the call site you understand the functions better. -// type OptMagix = ((b?: B) => (a: A) => C) & { -// /** -// * Uncurried version of the parent function -// */ -// _: (a: A, b?: B) => C -// /** -// * Reverse curried version of the parent function -// */ -// r: (a: A) => (b?: B) => C -// } -// type Magix = ((b: B) => (a: A) => C) & { -// /** -// * Uncurried version of the parent function -// */ -// _: (a: A, b: B) => C -// /** -// * Reverse curried version of the parent function -// */ -// r: (a: A) => (b: B) => C -// } - -export function curriedMagix( - f: (b?: B) => (a: A) => C -): ((b?: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b?: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b?: B) => C -} -export function curriedMagix( - f: (b: B) => (a: A) => C -): ((b: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b: B) => C -} -export function curriedMagix(f: (b: B) => (a: A) => C) { - return Object.assign(f, { - /** - * Uncurried version of the parent function - */ - _: uncurry(f), - /** - * Reverse curried version of the parent function - */ - r: reverseCurry(f) - }) -} - -export function uncurriedMagix( - f: (a: A, b?: B) => C -): ((b?: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b?: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b?: B) => C -} -export function uncurriedMagix( - f: (a: A, b: B) => C -): ((b: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b: B) => C -} -export function uncurriedMagix(f: (a: A, b: B) => C) { - const curried = curry(f) - return Object.assign(curried, { - /** - * Uncurried version of the parent function - */ - _: f, - /** - * Reverse curried version of the parent function - */ - r: reverseCurry(curried) - }) -} - -export function reverseCurriedMagix( - f: (a: A) => (b: B) => C -): ((b?: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b?: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b?: B) => C -} -export function reverseCurriedMagix( - f: (a?: A) => (b: B) => C -): ((b: B) => (a: A) => C) & { - /** - * Uncurried version of the parent function - */ - _: (a: A, b: B) => C - /** - * Reverse curried version of the parent function - */ - r: (a: A) => (b: B) => C -} -export function reverseCurriedMagix(f: (a: A) => (b: B) => C) { - const curried = reverseCurry(f) - return Object.assign(curried, { - /** - * Uncurried version of the parent function - */ - _: uncurry(curried), - /** - * Reverse curried version of the parent function - */ - r: f - }) -} - -export function lazy(f: () => A): () => A { - let tmp: A | undefined - let done = false - return () => { - if (done) { - return tmp! - } - tmp = f() - done = true - return tmp - } -} diff --git a/packages/effect-app/src/Inputify.type.ts b/packages/effect-app/src/Inputify.type.ts deleted file mode 100644 index a47022685..000000000 --- a/packages/effect-app/src/Inputify.type.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { BuiltInObject } from "effect-app/utils" - -import type { Primitive } from "./Widen.type.ts" - -// Get rid of Date | string, and replace with Date | null -export type Inputify = Date extends T ? string extends T ? Date | null - : Date | null - : [T] extends [BuiltInObject] | [Primitive] ? T - : [T] extends [Array] ? { [K in keyof T]: Inputify } - : [T] extends [ReadonlyArray] ? { [K in keyof T]: Inputify } - : [T] extends [object] ? { [K in keyof T]: Inputify } - : T diff --git a/packages/effect-app/src/Layer.ts b/packages/effect-app/src/Layer.ts deleted file mode 100644 index 6f1c820b2..000000000 --- a/packages/effect-app/src/Layer.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type * as Array from "effect/Array" -import * as Effect from "effect/Effect" -import { dual } from "effect/Function" -import * as Layer from "effect/Layer" -import type * as Scope from "effect/Scope" -import type * as Types from "effect/Types" -import type * as Context from "./Context.ts" -import { type EffectGenUtils } from "./utils/gen.ts" - -export * from "effect/Layer" - -type Make = DependenciesOpt & (MakeEff | MakeGenNo | MakeGen) - -type MakeEff = { - readonly make: Effect.Effect -} -type MakeGen = { - readonly make: () => Generator, S> -} -type MakeGenNo = { - readonly make: () => Generator -} -type MakeErr = Opts extends { make: () => any } ? EffectGenUtils.Error : never -type MakeContext = Opts extends { make: () => any } ? EffectGenUtils.Context : never - -type DependenciesOpt = { dependencies?: Array.NonEmptyReadonlyArray } -type Dependencies = { dependencies: Array.NonEmptyReadonlyArray } - -// Local replacements for removed Effect.Service.MakeDeps* types -type MakeDepsE = Opts extends { dependencies: ReadonlyArray> } ? E : never -type MakeDepsOut = Opts extends { dependencies: ReadonlyArray> } ? Out : never - -type PackedLayers = - & Layer.Layer< - I, - MakeErr | MakeDepsE, - Exclude, Scope.Scope | MakeDepsOut> - > - & { - withoutDependencies: Layer.Layer, Exclude, Scope.Scope>> - } - -type PackedOrUnpackedLayer = Opts extends Dependencies ? PackedLayers & {} - : Layer.Layer, MakeContext> - -export const make: { - ( - tag: Context.Service - ): , any, any>>( - options: Opts - ) => PackedOrUnpackedLayer - , any, any>>( - tag: Context.Service, - options: Opts - ): PackedOrUnpackedLayer -} = dual(2, (tag, options) => { - const effect = options.make[Symbol.toStringTag] === "GeneratorFunction" - ? Effect.fnUntraced(options.make)() - : options.make - const withoutDependencies = Layer.effect(tag, effect) - if (options.dependencies) { - return Object.assign( - withoutDependencies.pipe(Layer.provide(options.dependencies)), - { withoutDependencies } - ) - } else { - return withoutDependencies - } -}) diff --git a/packages/effect-app/src/Model.ts b/packages/effect-app/src/Model.ts deleted file mode 100644 index fdfa6b568..000000000 --- a/packages/effect-app/src/Model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./Model/dsl.ts" -export * as Q from "./Model/query.ts" -export { makeRepo } from "./Model/Repository.ts" -export { repositoryDependency } from "./Model/Repository.ts" -export { type RegisteredRepository, RepositoryRegistry, RepositoryRegistryLive } from "./Model/Repository.ts" diff --git a/packages/effect-app/src/Model/Repository.ts b/packages/effect-app/src/Model/Repository.ts deleted file mode 100644 index f68b0b3e0..000000000 --- a/packages/effect-app/src/Model/Repository.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { repositoryDependency } from "./Repository/dependency.ts" -export * from "./Repository/ext.ts" -export * from "./Repository/legacy.ts" -export { makeRepo } from "./Repository/makeRepo.ts" -export * from "./Repository/Registry.ts" -export * from "./Repository/service.ts" -export * from "./Repository/validation.ts" diff --git a/packages/effect-app/src/Model/Repository/Registry.ts b/packages/effect-app/src/Model/Repository/Registry.ts deleted file mode 100644 index 6fb1a675b..000000000 --- a/packages/effect-app/src/Model/Repository/Registry.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { DatabaseError } from "../../client/errors.ts" -import * as Context from "../../Context.ts" -import * as Effect from "../../Effect.ts" - -export interface RegisteredRepository { - readonly seedNamespace: (namespace: string) => Effect.Effect -} - -const make = Effect.sync(() => { - const repos = new Map() - return { - register(modelName: string, repo: RegisteredRepository) { - repos.set(modelName, repo) - }, - seedNamespace: (namespace: string) => - Effect.suspend(() => - Effect.forEach( - repos.values(), - (r) => r.seedNamespace(namespace), - { concurrency: "unbounded", discard: true } - ) - ), - get entries(): ReadonlyMap { - return repos - } - } -}) - -export class RepositoryRegistry extends Context.Opaque void - readonly seedNamespace: (namespace: string) => Effect.Effect - readonly entries: ReadonlyMap -}>()("effect-app/RepositoryRegistry", { make }) {} - -export const RepositoryRegistryLive = RepositoryRegistry.toLayer(RepositoryRegistry.make) diff --git a/packages/effect-app/src/Model/Repository/dependency.ts b/packages/effect-app/src/Model/Repository/dependency.ts deleted file mode 100644 index adc7e64d9..000000000 --- a/packages/effect-app/src/Model/Repository/dependency.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as S from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" - -const RepositoryDependencyAnnotation = "effect-app/Repository/dependency" - -/** Marks an encoded model field as an identity alias for repository invalidation. */ -export const repositoryDependency = (schema: Schema): Schema["Rebuild"] => - S.annotateEncoded({ [RepositoryDependencyAnnotation]: true })(schema) - -/** @internal */ -export const repositoryDependencyPaths = (schema: S.Schema): readonly string[] => { - const visit = (ast: SchemaAST.AST, path: readonly string[]): readonly string[] => { - const annotations = ast.checks?.at(-1)?.annotations ?? ast.annotations - if (annotations?.[RepositoryDependencyAnnotation] === true) return [path.join(".")] - if (SchemaAST.isDeclaration(ast)) return ast.typeParameters.flatMap((parameter) => visit(parameter, path)) - if (SchemaAST.isUnion(ast)) return ast.types.flatMap((member) => visit(member, path)) - if (SchemaAST.isObjects(ast)) { - return ast.propertySignatures.flatMap((property) => - typeof property.name === "string" ? visit(property.type, [...path, property.name]) : [] - ) - } - if (SchemaAST.isArrays(ast)) { - return [ - ...ast.elements.flatMap((element, index) => visit(element, [...path, String(index)])), - ...ast.rest.flatMap((element) => visit(element, [...path, "-1"])) - ] - } - return [] - } - - return [...new Set(visit(SchemaAST.toEncoded(schema.ast), []))] -} diff --git a/packages/effect-app/src/Model/Repository/ext.ts b/packages/effect-app/src/Model/Repository/ext.ts deleted file mode 100644 index d8fedfbef..000000000 --- a/packages/effect-app/src/Model/Repository/ext.ts +++ /dev/null @@ -1,374 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -import * as Exit from "effect/Exit" -import * as Request from "effect/Request" -import * as RequestResolver from "effect/RequestResolver" -import * as Array from "../../Array.ts" -import type { NonEmptyArray } from "../../Array.ts" -import { type DatabaseError, type InvalidStateError, NotFoundError, type OptimisticConcurrencyException } from "../../client/errors.ts" -import * as Effect from "../../Effect.ts" -import * as Option from "../../Option.ts" -import { type FixEnv, type PureEnv, runTerm } from "../../Pure.ts" -import { AnyPureDSL } from "../dsl.ts" -import type { FieldValues } from "../filter/types.ts" -import type { Query, QueryEnd, QueryWhere } from "../query.ts" -import * as Q from "../query.ts" -import type { Repository } from "./service.ts" - -interface BatchOptions { - readonly batch?: true | number -} - -const asReadonlyArray = (itemOrItems: T | ReadonlyArray): ReadonlyArray => - globalThis.Array.isArray(itemOrItems) - ? itemOrItems as ReadonlyArray - : [itemOrItems as T] - -const getBatchSize = (batch?: true | number) => - batch === true - ? 100 - : typeof batch === "number" && Number.isFinite(batch) && batch > 0 - ? Math.floor(batch) - : undefined - -export const extendRepo = < - T, - Encoded extends FieldValues, - Evt, - ItemType extends string, - IdKey extends keyof T & keyof Encoded, - RSchema, - RPublish, - RProvided = never ->( - repo: Repository -) => { - const get = (id: T[IdKey]) => - repo.find(id).pipe( - Effect.flatMap(Option.match({ - onNone: () => Effect.fail(new NotFoundError({ type: repo.itemType, id })), - onSome: Effect.succeed - })) - ) - function saveManyWithPure_< - R, - A, - E, - S1 extends T, - S2 extends T - >( - items: Iterable, - pure: Effect.Effect> - ) { - return saveAllWithEffectInt( - runTerm(pure, [...items]) - ) - } - - function saveWithPure_< - R, - A, - E, - S1 extends T, - S2 extends T - >( - item: S1, - pure: Effect.Effect> - ) { - return saveAllWithEffectInt( - runTerm(pure, item) - .pipe(Effect - .map(([item, events, a]) => [[item], events, a])) - ) - } - - function saveAllWithEffectInt< - P extends T, - R, - E, - A - >( - gen: Effect.Effect, Iterable, A], E, R> - ): Effect.Effect< - A, - E | InvalidStateError | OptimisticConcurrencyException | DatabaseError, - R | RSchema | RPublish - > { - return Effect.flatMap(gen, ([items, events, a]) => repo.saveAndPublish(items, events).pipe(Effect.map(() => a))) - } - - function saveManyWithPureBatched_< - R, - A, - E, - S1 extends T, - S2 extends T - >( - items: Iterable, - pure: Effect.Effect>, - batchSize = 100 - ) { - return Effect.forEach( - Array.chunksOf(items, batchSize), - (batch) => - saveAllWithEffectInt( - runTerm(pure, batch) - ) - ) - } - - type PureStoreError = InvalidStateError | OptimisticConcurrencyException | DatabaseError - - const queryAndSavePure: { - ( - q: ( - q: Query - ) => QueryEnd, - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | NotFoundError | E2, - | RSchema - | RPublish - | Exclude - }> - > - ( - q: ( - q: Query - ) => - | Query - | QueryWhere - | QueryEnd, - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | E2, - | RSchema - | RPublish - | Exclude - }> - > - ( - q: ( - q: Query - ) => - | Query - | QueryWhere - | QueryEnd, - pure: Effect.Effect>, - batch: "batched" | number - ): Effect.Effect< - A[], - PureStoreError | E2, - | RSchema - | RPublish - | Exclude - }> - > - } = ((q: any, pure: any, batch?: "batched" | number) => - // Overload dispatch: query returns T | T[]; pure helpers are generic on item shape. - // Runtime path is query → pure term → saveAndPublish (which raises DatabaseError). - repo.query(q).pipe( - Effect.andThen((_) => - Array.isArray(_) - ? batch === undefined - ? saveManyWithPure_(_ as T[], pure) - : saveManyWithPureBatched_(_ as T[], pure, batch === "batched" ? 100 : batch) - : saveWithPure_(_ as T, pure) - ) - )) as typeof queryAndSavePure - - const saveManyWithPure: { - ( - items: Iterable, - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | E, - | RSchema - | RPublish - | Exclude - }> - > - ( - items: Iterable, - pure: Effect.Effect>, - batch: "batched" | number - ): Effect.Effect< - A[], - PureStoreError | E, - | RSchema - | RPublish - | Exclude - }> - > - } = ((items: Iterable, pure: any, batch?: "batched" | number) => - batch - ? Effect.forEach( - Array.chunksOf([...items], batch === "batched" ? 100 : batch), - (batchItems) => - saveAllWithEffectInt( - runTerm(pure, batchItems as any) - ) - ) - : saveAllWithEffectInt( - runTerm(pure, [...items] as any) - )) as typeof saveManyWithPure - - const byIdAndSaveWithPure: { - ( - id: T[IdKey], - pure: Effect.Effect> - ): Effect.Effect< - A, - PureStoreError | NotFoundError | E, - | RSchema - | RPublish - | Exclude - }> - > - } = (id, pure) => get(id).pipe(Effect.flatMap((item) => saveWithPure_(item, pure))) - - // query can raise DatabaseError; NotFound is completed per-entry. Dual package - // round-trips may still surface other store errors via failCause — keep channel honest. - type Req = - & Request.Request | DatabaseError> - & { _tag: `Get${ItemType}`; id: T[IdKey] } - const _request = Request.tagged(`Get${repo.itemType}`) - - const requestResolver = RequestResolver - .make(( - entries: NonEmptyArray>, - _key: unknown - ) => - (repo.query(Q.where(repo.idKey as any, "in" as any, entries.map((_) => _.request.id)) as any) as Effect.Effect< - readonly T[], - DatabaseError - >) - .pipe( - Effect.andThen((items) => - Effect.forEach(entries, (entry) => - Request.complete( - Array - .findFirst(items, (_) => _[repo.idKey] === entry.request.id) - .pipe(Option.match({ - onNone: () => Exit.fail(new NotFoundError({ type: repo.itemType, id: entry.request.id })), - onSome: Exit.succeed - })) - )(entry), { discard: true }) - ), - Effect - .catchCause((cause) => - Effect.forEach(entries, (entry) => Request.complete(Exit.failCause(cause))(entry), { discard: true }) - ) - ) - ) - .pipe( - RequestResolver.batchN(20) - ) - - const exts = { - request: (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver), - get, - log: (evt: Evt) => AnyPureDSL.log(evt), - /** - * Enables chunked writes for large batches via `options.batch`. - * Note: batching breaks transactional properties because chunks are saved independently. - */ - save: (itemOrItems: T | ReadonlyArray, options?: BatchOptions) => { - const items = asReadonlyArray(itemOrItems) - if (!Array.isReadonlyArrayNonEmpty(items)) { - return Effect.void - } - const batchSize = getBatchSize(options?.batch) - if (batchSize === undefined) { - return repo.saveAndPublish(items) - } - return Effect.forEach( - Array.chunksOf(items, batchSize), - (batch) => repo.saveAndPublish(batch), - { discard: true } - ) - }, - saveWithEvents: (events: Iterable) => (...items: NonEmptyArray) => repo.saveAndPublish(items, events), - /** - * Enables chunked deletes for large batches via `options.batch`. - * Note: batching breaks transactional properties because chunks are removed independently. - */ - remove: (itemOrItems: T | ReadonlyArray, options?: BatchOptions) => { - const items = asReadonlyArray(itemOrItems) - if (!Array.isReadonlyArrayNonEmpty(items)) { - return Effect.void as Effect.Effect - } - const batchSize = getBatchSize(options?.batch) - if (batchSize === undefined) { - return repo.removeAndPublish(items) - } - return Effect.forEach( - Array.chunksOf(items, batchSize), - (batch) => repo.removeAndPublish(batch), - { discard: true } - ) - }, - /** - * Enables chunked deletes for large batches via `options.batch`. - * Note: batching breaks transactional properties because chunks are removed independently. - */ - removeById: (idOrIds: T[IdKey] | ReadonlyArray, options?: BatchOptions) => { - const ids = asReadonlyArray(idOrIds) - if (!Array.isReadonlyArrayNonEmpty(ids)) { - return Effect.void as Effect.Effect - } - const batchSize = getBatchSize(options?.batch) - if (batchSize === undefined) { - return repo.removeById(ids) - } - return Effect.forEach( - Array.chunksOf(ids, batchSize), - (batch) => repo.removeById(batch), - { discard: true } - ) - }, - queryAndSavePure, - saveManyWithPure, - byIdAndSaveWithPure, - saveWithPure: < - R, - A, - E, - S1 extends T, - S2 extends T - >( - item: S1, - pure: Effect.Effect> - ) => - saveAllWithEffectInt( - runTerm(pure, item) - .pipe(Effect.map(([item, events, a]) => [[item], events, a])) - ) - } - - return { - ...repo, - ...exts - } as Repository & typeof exts -} - -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -export interface ExtendedRepository< - T, - Encoded extends FieldValues, - Evt, - ItemType extends string, - IdKey extends keyof T & keyof Encoded, - RSchema, - RPublish, - RProvided = never -> extends ReturnType> {} diff --git a/packages/effect-app/src/Model/Repository/internal/internal.ts b/packages/effect-app/src/Model/Repository/internal/internal.ts deleted file mode 100644 index 99bdf2b4d..000000000 --- a/packages/effect-app/src/Model/Repository/internal/internal.ts +++ /dev/null @@ -1,987 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as Equivalence from "effect/Equivalence" -import { flow, pipe } from "effect/Function" -import * as HashMap from "effect/HashMap" -import * as HashSet from "effect/HashSet" -import * as Metric from "effect/Metric" -import * as Pipeable from "effect/Pipeable" -import * as Ref from "effect/Ref" -import * as Result from "effect/Result" -import type * as Scope from "effect/Scope" -import * as Unify from "effect/Unify" -import * as Array from "../../../Array.ts" -import type { NonEmptyReadonlyArray } from "../../../Array.ts" -import * as Chunk from "../../../Chunk.ts" -import { NotFoundError } from "../../../client/errors.ts" -import * as Context from "../../../Context.ts" -import * as DataDependencies from "../../../DataDependencies.ts" -import * as Effect from "../../../Effect.ts" -import { flatMapOption } from "../../../Effect.ts" -import * as Option from "../../../Option.ts" -import * as S from "../../../Schema.ts" -import { type Codec, NonNegativeInt } from "../../../Schema.ts" -import * as SchemaAST from "../../../SchemaAST.ts" -import { setupRequestContextFromCurrent } from "../../../setupRequest.ts" -import { type FilterArgs, getContextMap, type PersistenceModelType, type StoreConfig, storeId, StoreMaker } from "../../../Store.ts" -import type { FilterResult } from "../../filter/filterApi.ts" -import type { FieldValues } from "../../filter/types.ts" -import * as Q from "../../query.ts" -import { repositoryDependencyPaths } from "../dependency.ts" -import type { ChangeFeed, ChangeFeedEvent, Repository } from "../service.ts" -import { ValidationError, ValidationResult } from "../validation.ts" - -const dedupe = Array.dedupeWith(Equivalence.String) - -// ms buckets: dense under 100ms (common path), then mid-tail and multi-second stalls. -// Rare 0.5–1s+ encodes (fat aggregates) must not collapse into a single overflow bin. -const schemaDurationBoundaries = [ - 0.1, - 0.5, - 1, - 2, - 5, - 10, - 25, - 50, - 100, - 150, - 200, - 300, - 500, - 750, - 1_000, - 1_500, - 2_000, - 5_000 -] -// Event-loop-relevant stall floor: encodes/decodes above this are rare but block Node. -const SCHEMA_SLOW_MS = 100 -const schemaDecodeDuration = Metric.histogram("app.schema.decode.duration", { boundaries: schemaDurationBoundaries }) -const schemaEncodeDuration = Metric.histogram("app.schema.encode.duration", { boundaries: schemaDurationBoundaries }) -const schemaItemCount = Metric.histogram("app.schema.item_count", { - boundaries: [0, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1_000, 5_000] -}) -const schemaSlow = Metric.counter("app.schema.slow", { - description: `Repository schema encode/decode slower than ${SCHEMA_SLOW_MS}ms`, - incremental: true -}) - -const entityStateFromItems = (items: readonly unknown[]): string | undefined => { - const first = items[0] - if (first === null || typeof first !== "object" || !("_tag" in first)) return undefined - const tag = (first as { readonly _tag: unknown })._tag - return typeof tag === "string" ? tag : undefined -} - -const timeSchema = ( - operation: "decode" | "encode", - entity: string, - queryMode: "aggregate" | "collect" | "project" | "transform" | undefined, - itemCount: number, - entityState?: string -) => -(self: Effect.Effect): Effect.Effect => - Effect.clockWith((clock) => { - const startedAt = clock.currentTimeNanosUnsafe() - const attributes = { - "app.entity": entity, - "app.schema.operation": operation, - ...(queryMode !== undefined && { "app.query.mode": queryMode }), - ...(entityState !== undefined && { "app.entity.state": entityState }) - } - return Effect.onExit(self, () => { - const durationMs = Number(clock.currentTimeNanosUnsafe() - startedAt) / 1_000_000 - const slow = durationMs >= SCHEMA_SLOW_MS - return Effect.all([ - Effect.annotateCurrentSpan({ - [`app.schema.${operation}.duration_ms`]: durationMs, - "app.schema.item_count": itemCount, - "app.schema.slow": slow, - ...(queryMode !== undefined && { "app.query.mode": queryMode }), - ...(entityState !== undefined && { "app.entity.state": entityState }) - }), - Metric.update( - Metric.withAttributes( - operation === "decode" ? schemaDecodeDuration : schemaEncodeDuration, - attributes - ), - durationMs - ), - Metric.update(Metric.withAttributes(schemaItemCount, attributes), itemCount), - ...(slow - ? [Metric.update(Metric.withAttributes(schemaSlow, attributes), 1)] as const - : []) - ], { discard: true }) - }) - }) - -/** - * A base implementation to create a repository. - */ -export function makeRepoInternal< - Evt = never ->() { - return < - ItemType extends string, - R, - Encoded extends FieldValues, - T, - IdKey extends keyof T & keyof Encoded - >( - name: ItemType, - schema: S.Codec, - mapTo: (e: Encoded, etag: string | undefined) => PersistenceModelType, - idKey: IdKey - ) => { - type PM = PersistenceModelType - function mapToPersistenceModel( - e: Encoded, - getEtag: (id: string) => string | undefined - ): PM { - return mapTo(e, getEtag(e[idKey])) - } - - function mapReverse( - { _etag, ...e }: PM, - setEtag: (id: string, eTag: string | undefined) => void - ): Encoded { - setEtag((e as any)[idKey], _etag) - // the store already applied `jitM` and decoded JSON→Encoded - return e as unknown as Encoded - } - - const mkStore = makeStore()(name, schema, mapTo, idKey) - - function make( - args: [Evt] extends [never] ? { - schemaContext?: Context.Context - makeInitial?: Effect.Effect | undefined - dependencyIds?: (item: T) => NonEmptyReadonlyArray - additionalWriteDependencies?: (item: T) => ReadonlyArray - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - : { - schemaContext?: Context.Context - publishEvents: (evt: NonEmptyReadonlyArray) => Effect.Effect - makeInitial?: Effect.Effect | undefined - dependencyIds?: (item: T) => NonEmptyReadonlyArray - additionalWriteDependencies?: (item: T) => ReadonlyArray - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - ) { - return Effect - .gen(function*() { - const rctx: Context.Context = args.schemaContext ?? Context.empty() as any - const provideRctx = Effect.provide(rctx) - const encodeMany = (items: readonly T[]) => - S.encodeEffect(S.Array(schema))(items).pipe( - provideRctx, - timeSchema("encode", name, undefined, items.length, entityStateFromItems(items)) - ) - const decode = flow(S.decodeEffectConcurrently(schema), provideRctx) - const decodeMany = flow( - S.decodeEffectConcurrently(S.Array(schema)), - provideRctx - ) - - const store = yield* mkStore(args.makeInitial, args.config) - const dependencyPaths = repositoryDependencyPaths(schema) - const recordRead = DataDependencies.readRepo(name) - const entityDependency = (ids: NonEmptyReadonlyArray) => - DataDependencies.repo(name, [String(ids[0]), ...ids.slice(1).map(String)]) - const valuesAtPath = (value: unknown, path: string): readonly unknown[] => { - if (path.length === 0) return [value] - const [head, ...tail] = path.split(".") - if (head === undefined) return [value] - if (head === "-1") { - return globalThis.Array.isArray(value) - ? value.flatMap((item) => valuesAtPath(item, tail.join("."))) - : [] - } - return typeof value === "object" && value !== null - ? valuesAtPath((value as Record)[head], tail.join(".")) - : [] - } - const dependencyIds = (item: T): NonEmptyReadonlyArray => { - const configured = dependencyPaths - .flatMap((path) => valuesAtPath(item, String(path))) - .filter((value): value is string => typeof value === "string") ?? [] - const ids = args.dependencyIds?.(item) ?? [String(item[idKey]), ...configured] - return [ids[0], ...ids.slice(1)] - } - const itemDependencies = (item: T) => { - const ids = dependencyIds(item) - return [ - DataDependencies.repo(name, ids), - ...(args.additionalWriteDependencies?.(item) ?? []) - ] - } - const queryDependencyIds = (filter: readonly FilterResult[] | undefined): readonly string[] => { - if (!filter) return [] - const paths = new Set([String(idKey), ...dependencyPaths]) - const visit = (items: readonly FilterResult[]): readonly string[] => { - if (items.some((item) => item.t === "or" || item.t === "or-scope")) return [] - return items.flatMap((item) => { - if ("result" in item) return visit(item.result) - if (!paths.has(item.path)) return [] - const value: unknown = item.value - if (item.op === "eq" && typeof value === "string") return [value] - if (item.op === "in" && globalThis.Array.isArray(value)) { - return value.filter((entry): entry is string => typeof entry === "string") - } - return [] - }) - } - return [...new Set(visit(filter))] - } - const recordEntityRead = (id: T[IdKey]) => DataDependencies.read(entityDependency([id])) - const recordEntityWrite = (ids: NonEmptyReadonlyArray) => - DataDependencies.write(entityDependency(ids)) - const recordItemWrite = (items: NonEmptyReadonlyArray) => - Effect.forEach( - DataDependencies.merge(new Set(items.flatMap(itemDependencies))), - DataDependencies.write, - { discard: true } - ) - const cms = Effect.map(getContextMap.pipe(Effect.orDie), (_) => ({ - get: (id: string) => _.get(`${name}.${id}`), - set: (id: string, etag: string | undefined) => _.set(`${name}.${id}`, etag) - })) - - const pub = "publishEvents" in args - ? args.publishEvents - : () => Effect.void - - type ChangeFeedHandler = (evt: ChangeFeedEvent) => Effect.Effect - const changeFeedByNamespace = yield* Ref.make(HashMap.empty>()) - const changeFeedWildcard = yield* Ref.make(HashSet.empty()) - // clear all handlers when the repository's scope closes - yield* Effect.addFinalizer(() => - Effect.all([ - Ref.set(changeFeedByNamespace, HashMap.empty>()), - Ref.set(changeFeedWildcard, HashSet.empty()) - ], { discard: true }) - ) - const changeFeed: ChangeFeed = { - publish: ([items, op]) => - Effect.gen(function*() { - const ns = yield* storeId - const evt: ChangeFeedEvent = [items, op, ns] - const map = yield* Ref.get(changeFeedByNamespace) - const wild = yield* Ref.get(changeFeedWildcard) - const targeted = Option.getOrElse(HashMap.get(map, ns), () => HashSet.empty()) - const all = HashSet.union(targeted, wild) - yield* Effect.forEach(all, (h) => h(evt), { concurrency: "unbounded", discard: true }) - }), - subscribe: (handler, options) => { - const ns = options?.namespace - if (ns === undefined) { - return Effect.acquireRelease( - Ref.update(changeFeedWildcard, HashSet.add(handler)), - () => Ref.update(changeFeedWildcard, HashSet.remove(handler)) - ) - } - return Effect.acquireRelease( - Ref.update(changeFeedByNamespace, (m) => { - const cur = Option.getOrElse(HashMap.get(m, ns), () => HashSet.empty()) - return HashMap.set(m, ns, HashSet.add(cur, handler)) - }), - () => - Ref.update(changeFeedByNamespace, (m) => { - const cur = Option.getOrElse(HashMap.get(m, ns), () => HashSet.empty()) - const next = HashSet.remove(cur, handler) - return HashSet.size(next) === 0 ? HashMap.remove(m, ns) : HashMap.set(m, ns, next) - }) - ) - } - } - - const allE = cms - .pipe(Effect.flatMap((cm) => Effect.map(store.all, (_) => _.map((_) => mapReverse(_, cm.set))))) - - const all = Effect - .flatMap( - allE, - (items) => - decodeMany(items).pipe( - Effect.orDie, - timeSchema("decode", name, undefined, items.length) - ) - ) - .pipe( - Effect.tap(() => recordRead), - Effect.map((_) => _ as T[]), - Effect.withSpan("Repository.all", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - - const fieldsSchema = schema as unknown as { fields: any } - // assumes the id field never needs a service... - const i = ("fields" in fieldsSchema ? S.Struct(fieldsSchema["fields"]) as unknown as typeof schema : schema) - .pipe((_) => { - let ast = _.ast - if (ast._tag === "Declaration") ast = ast.typeParameters[0]! - - const pickIdFromAst = (a: SchemaAST.AST) => { - // Unwrap Declaration (e.g. TaggedClass) to get the underlying Objects AST - let inner = a - if (inner._tag === "Declaration") inner = inner.typeParameters[0]! - // Pick from the original AST to preserve the full encoding chain (e.g. decodeTo transformations). - // Using toEncoded would lose transformation info needed to encode Type -> Encoded. - if (SchemaAST.isObjects(inner)) { - const field = inner.propertySignatures.find((_) => _.name === idKey) - if (field) { - return S.Struct({ [idKey]: S.make(field.type) }) as unknown as Codec - } - } - return S.make(a) as unknown as Codec - } - - return ast._tag === "Union" - // we need to get the Objects (TypeLiteral), in case of class it has encoding chain... - ? S.Union( - ast.types.map((_) => pickIdFromAst(_)) - ) - : pickIdFromAst(ast) - }) - const encodeId = flow(S.encodeEffect(i), provideRctx) - const encodeIdOnly = (id: string) => - encodeId({ [idKey]: id } as any).pipe( - Effect.map((_: Record) => _[idKey as string] as Encoded[IdKey]) - ) - const findEId = Effect.fnUntraced(function*(id: Encoded[IdKey]) { - yield* Effect.annotateCurrentSpan({ "app.entity.id": id }) - - return yield* Effect.flatMap( - store.find(id), - (item) => - Effect.gen(function*() { - const { set } = yield* cms - return item.pipe(Option.map((_) => mapReverse(_, set))) - }) - ) - }) - // TODO: select the particular field, instead of as struct - const findE = Effect.fnUntraced(function*(id: T[IdKey]) { - yield* Effect.annotateCurrentSpan({ "app.entity.id": id }) - - return yield* pipe( - encodeId({ [idKey]: id } as any), - Effect.orDie, - Effect.map((_) => (_ as any)[idKey]), - Effect.flatMap(findEId) - ) - }) - - const loadExistingItems = (ids: ReadonlyArray) => - Effect - .forEach( - ids, - (id) => - findE(id).pipe( - Effect.flatMap(Option.match({ - onNone: () => Effect.succeed(Option.none()), - onSome: (encoded) => decode(encoded).pipe(Effect.orDie, Effect.map(Option.some)) - })) - ) - ) - .pipe(Effect.map(Array.getSomes)) - - const find = Effect.fn("Repository.find", { - kind: "client", - attributes: { "app.entity": name } - })(function*(id: T[IdKey]) { - yield* recordEntityRead(id) - yield* Effect.annotateCurrentSpan({ "app.entity.id": id }) - return yield* flatMapOption(findE(id), (_) => Effect.orDie(decode(_))) - }) - - const saveAllE = (a: Iterable) => - flatMapOption( - Effect - .sync(() => Option.liftPredicate(Array.isReadonlyArrayNonEmpty)([...a])), - (a) => - Effect.gen(function*() { - const { get, set } = yield* cms - const items = a.map((_) => mapToPersistenceModel(_, get)) - const ret = yield* store.batchSet(items) - ret.forEach((_) => set(_[idKey], _._etag)) - }) - ) - .pipe(Effect.asVoid) - - const saveAll = (a: Iterable) => - encodeMany(Array.fromIterable(a)) - .pipe( - Effect.orDie, - Effect.andThen(saveAllE) - ) - - const saveAndPublish = Effect.fn("Repository.saveAndPublish", { attributes: { "app.entity": name } })( - function*(items: Iterable, events: Iterable = []) { - const it = Chunk.fromIterable(items) - if (Chunk.isNonEmpty(it)) { - const values = Chunk.toReadonlyArray(it) - const previous = args.additionalWriteDependencies || args.dependencyIds || dependencyPaths.length > 0 - ? yield* loadExistingItems(values.map((item) => item[idKey])) - : [] - yield* recordItemWrite([values[0], ...values.slice(1), ...previous]) - } - const evts = [...events] - yield* Effect.annotateCurrentSpan({ - "app.entity.ids": Chunk.map(it, (_) => _[idKey]), - "app.event.count": evts.length - }) - return yield* saveAll(it) - .pipe( - Effect.andThen(Effect.sync(() => Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(evts))), - // TODO: for full consistency the events should be stored within the same database transaction, and then picked up. - (_) => flatMapOption(_, pub), - Effect.andThen(changeFeed.publish([Chunk.toArray(it), "save"] as [T[], "save" | "remove"])), - Effect.asVoid - ) - } - ) - - const removeAndPublish = Effect.fn("Repository.removeAndPublish", { attributes: { "app.entity": name } })( - function*(a: Iterable, events: Iterable = []) { - const { set } = yield* cms - const it = [...a] - if (Array.isReadonlyArrayNonEmpty(it)) { - yield* recordItemWrite(it) - } - const evts = [...events] - yield* Effect.annotateCurrentSpan({ - "app.entity.ids": it.map((_) => _[idKey]), - "app.event.count": evts.length - }) - const items = yield* encodeMany(it).pipe(Effect.orDie) - if (Array.isReadonlyArrayNonEmpty(items)) { - yield* store.batchRemove( - items.map((_) => (_[idKey])), - args.config?.partitionValue?.(items[0]) - ) - for (const e of items) { - set(e[idKey], undefined) - } - yield* Effect - .sync(() => Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(evts)) - // TODO: for full consistency the events should be stored within the same database transaction, and then picked up. - .pipe((_) => flatMapOption(_, pub)) - - yield* changeFeed.publish([it, "remove"] as [T[], "save" | "remove"]) - } - } - ) - - const removeById = Effect.fn("Repository.removeById", { attributes: { "app.entity": name } })( - function*(idOrIds: T[IdKey] | ReadonlyArray) { - const ids = globalThis.Array.isArray(idOrIds) - ? idOrIds as readonly T[IdKey][] - : [idOrIds as T[IdKey]] - if (!Array.isReadonlyArrayNonEmpty(ids)) { - return - } - yield* recordEntityWrite(ids) - if (args.additionalWriteDependencies || args.dependencyIds || dependencyPaths.length > 0) { - const previous = yield* loadExistingItems(ids) - if (Array.isReadonlyArrayNonEmpty(previous)) yield* recordItemWrite(previous) - } - const { set } = yield* cms - const eids = yield* Effect.forEach(ids, (_) => encodeIdOnly(_ as any)).pipe(Effect.orDie) - yield* Effect.annotateCurrentSpan({ "app.entity.ids": eids }) - yield* store.batchRemove(eids) - for (const id of eids) { - set(id, undefined) - } - yield* changeFeed.publish([[], "remove"] as [T[], "save" | "remove"]) - } - ) - - const parseMany = Effect.fnUntraced( - function*(items: readonly PM[]) { - const cm = yield* cms - return yield* decodeMany(items.map((_) => mapReverse(_, cm.set))).pipe( - Effect.orDie, - timeSchema("decode", name, "transform", items.length) - ) - } - ) - const decodeManyCache = new WeakMap< - S.Codec, - (i: readonly any[]) => Effect.Effect - >() - const getDecodeMany = (s: S.Codec) => { - let dec = decodeManyCache.get(s) - if (!dec) { - dec = S.decodeEffectConcurrently(S.Array(s)) - decodeManyCache.set(s, dec) - } - return dec - } - const parseMany2 = Effect.fnUntraced( - function*(items: readonly PM[], schema: S.Codec) { - const cm = yield* cms - return yield* getDecodeMany(schema)(items.map((_) => mapReverse(_, cm.set))).pipe( - Effect.orDie, - timeSchema("decode", name, "transform", items.length) - ) - } - ) - const filter = (args: FilterArgs) => - store - .filter( - // always enforce id and _etag because they are system fields, required for etag tracking etc - { - ...args, - select: args.select - ? dedupe([...args.select, idKey, "_etag" as any]) - : undefined - } as typeof args - ) - .pipe( - Effect.tap((items) => - Effect.map(cms, ({ set }) => items.forEach((_) => set((_ as Encoded)[idKey], (_ as PM)._etag))) - ) - ) - - // TODO: For raw we should use S.from, and drop the R... - const query: { - ( - q: Q.QueryProjection - ): Effect.Effect> - ( - q: Q.QAll, NoInfer, A, R> - ): Effect.Effect> - } = ((q: Q.QAll) => { - const a = Q.toFilter(q, schema) - const scopedIds = queryDependencyIds(a.filter) - const recordQueryRead = Array.isReadonlyArrayNonEmpty(scopedIds) - ? DataDependencies.read(DataDependencies.repo(name, scopedIds)) - : recordRead - // Mode dispatch — see `Q.project` JSDoc for the contract: - // aggregate: GROUP BY + aggregate functions at DB level; decode raw rows with schema; SchemaError surfaces. - // project : decode raw encoded rows with schema; no PM reverse-mapping; SchemaError surfaces. - // collect : same as project, but schema yields Option and None rows are dropped. - // transform: PM reverse-map (re-inject _etag/PM state from cms cache) then decode; orDie. - const eff = a.mode === "aggregate" - ? store - // `a.select` contains `{ key, aggregate }` items not expressible in FilterFunc's - // `U extends keyof Encoded` generic. Cast is unavoidable until FilterFunc supports aggregate mode. - .filter(a as any) - // Decode raw aggregate rows directly — no PM reverse-mapping, no id/_etag needed. - .pipe( - Effect.andThen( - (items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema ?? schema)))(items).pipe( - provideRctx, - timeSchema("decode", name, "aggregate", items.length) - ) - ) - ) - : a.mode === "project" - ? filter(a) - // TODO: mapFrom but need to support per field and dependencies - .pipe( - Effect.andThen( - (items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema ?? schema)))(items).pipe( - provideRctx, - timeSchema("decode", name, "project", items.length) - ) - ) - ) - : a.mode === "collect" - ? filter(a) - // TODO: mapFrom but need to support per field and dependencies - .pipe( - Effect.flatMap((items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema)))(items).pipe( - Effect.map(Array.getSomes), - provideRctx, - timeSchema("decode", name, "collect", items.length) - ) - ) - ) - : Effect.flatMap( - filter(a), - (_) => - Unify.unify( - a.schema - // TODO: partial may not match? - ? parseMany2(_ as any, a.schema as any) - : parseMany(_ as any) - ) - ) - return pipe( - a.ttype === "one" - ? Effect.flatMap( - eff, - flow( - Array.head, - Option.match({ - onNone: () => Effect.fail(new NotFoundError({ id: "query", /* TODO */ type: name })), - onSome: Effect.succeed - }) - ) - ) - : a.ttype === "count" - ? Effect - .map(eff, (_) => NonNegativeInt(_.length)) - .pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))) - : eff, - Effect.tap((r) => - Effect.annotateCurrentSpan({ - "app.query.ttype": a.ttype, - "app.query.mode": a.mode, - "db.response.returned_rows": Array.isArray(r) ? r.length : 1 - }) - ), - Effect.tap(() => recordQueryRead), - Effect.withSpan("Repository.query", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - }) as any - - const validateSample = Effect.fn("Repository.validateSample", { attributes: { "app.entity": name } })( - function*(options?: { - percentage?: number - maxItems?: number - }) { - const percentage = options?.percentage ?? 0.1 // default 10% - const maxItems = options?.maxItems - - // 1. get all IDs with projection (bypasses main schema decode) - const allIds = yield* store - .filter({ - t: null as unknown as Encoded, - select: [idKey as keyof Encoded] - }) - .pipe( - // a projection never decodes a whole document, so the store's - // document decode cannot fail here - Effect.catchTag("SchemaError", (e) => Effect.die(e)), - Effect.withSpan("Repository.filter", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - - // 2. random subset - const shuffled = [...allIds].sort(() => Math.random() - 0.5) - const sampleSize = Math.min( - maxItems ?? Infinity, - Math.ceil(allIds.length * percentage) - ) - const sample = shuffled.slice(0, sampleSize) - - // 3. validate each item - const errors: ValidationError[] = [] - - for (const item of sample) { - const id = item[idKey] - const stored = yield* store.find(id).pipe( - Effect.result, - Effect.withSpan("Repository.find", { - kind: "client", - attributes: { "app.entity": name, "app.entity.id": id } - }, { captureStackTrace: false }) - ) - - // a store-boundary failure: `defaultValues` + `jitM` did not - // produce a document the Encoded shape accepts, so the store - // failed with `S.SchemaError` instead of returning one. Report - // that document and keep sampling - it used to kill the run. - if (Result.isFailure(stored)) { - const failure = stored.failure - if (!S.isSchemaError(failure)) return yield* Effect.fail(failure) - errors.push( - ValidationError.make({ - id, - // the store failed before it could hand back a document - rawData: undefined, - jitMResult: undefined, - error: failure - }) - ) - continue - } - - const rawResult = stored.success - if (Option.isNone(rawResult)) continue - - // the store merged `defaultValues`, applied `jitM` on the raw - // JSON document and decoded JSON→Encoded, so this is exactly - // what the repository decodes into the domain type. - const rawData = rawResult.value as Encoded - - // the repository's own decode of a document the store accepted - const decodeResult = yield* S.decodeEffectConcurrently(schema)(rawData).pipe( - Effect.result, - provideRctx - ) - - if (Result.isFailure(decodeResult)) { - errors.push( - ValidationError.make({ - id, - rawData, - jitMResult: rawData, - error: decodeResult.failure - }) - ) - } - } - - return ValidationResult.make({ - total: NonNegativeInt(allIds.length), - sampled: NonNegativeInt(sample.length), - valid: NonNegativeInt(sample.length - errors.length), - errors - }) - } - ) - - // The store fails with `S.SchemaError` when a stored document does not - // decode - see `Store.all` / `Store.find`. The internals above let that - // failure propagate; producing the public members is the single - // boundary where it becomes a defect, because these signatures declare - // only `DatabaseError`. `queryRaw`, `query` and `mapped` already - // declare `S.SchemaError`, and `validateSample` reports it per - // document instead of dying. - const r = { - changeFeed, - itemType: name, - idKey, - find: (id: T[IdKey]) => find(id).pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - all: all.pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - saveAndPublish: (items: Iterable, events?: Iterable) => - saveAndPublish(items, events).pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - removeAndPublish, - removeById: (idOrIds: T[IdKey] | ReadonlyArray) => - removeById(idOrIds).pipe(Effect.catchTag("SchemaError", (e) => Effect.die(e))), - seedNamespace: (namespace: string) => store.seedNamespace(namespace), - validateSample, - queryRaw(schema: S.Codec, q: Q.RawQuery) { - return store.queryRaw(q).pipe( - Effect.flatMap((items) => - S.decodeEffectConcurrently(S.Array(S.toCodecJson(schema)))(items as readonly S.Json[]).pipe( - timeSchema("decode", name, undefined, items.length) - ) - ), - Effect.tap(() => recordRead), - Effect.withSpan("Repository.queryRaw", { - kind: "client", - attributes: { "app.entity": name } - }, { captureStackTrace: false }) - ) - }, - query(q: any) { - // eslint-disable-next-line prefer-rest-params - return query(typeof q === "function" ? Pipeable.pipeArguments(Q.make(), arguments) : q) as any - }, - /** - * @internal - */ - mapped: (schema: S.Codec) => { - const dec = S.decodeEffectConcurrently(schema) - const encMany = S.encodeEffect(S.Array(schema)) - const decMany = S.decodeEffectConcurrently(S.Array(schema)) - const spanAttrs = { kind: "client" as const, attributes: { "app.entity": name } } - return { - all: allE.pipe( - Effect.flatMap((items) => - decMany(items).pipe( - timeSchema("decode", name, undefined, items.length) - ) - ), - Effect.tap(() => recordRead), - Effect.map((_) => _ as any[]), - Effect.withSpan("Repository.mapped.all", spanAttrs, { captureStackTrace: false }) - ), - find: (id: T[IdKey]) => - flatMapOption(findE(id), dec).pipe( - Effect.tap(() => recordEntityRead(id)), - Effect.withSpan("Repository.mapped.find", { - ...spanAttrs, - attributes: { ...spanAttrs.attributes, "app.entity.id": id } - }, { captureStackTrace: false }) - ), - // query: (q: any) => { - // const a = Q.toFilter(q) - - // return filter(a) - // .pipe( - // Effect.flatMap(decMany), - // Effect.map((_) => _ as any[]), - // Effect.withSpan("Repository.mapped.query [effect-app/infra]", { - // captureStackTrace: false, - // attributes: { - // "repository.model_name": name, - // query: { ...a, schema: a.schema ? "__SCHEMA__" : a.schema, filter: a.filter.build() } - // } - // }) - // ) - // }, - save: (...xes: any[]) => - Effect - .flatMap( - encMany(xes).pipe( - timeSchema("encode", name, undefined, xes.length, entityStateFromItems(xes)) - ), - (_) => saveAllE(_) - ) - .pipe( - Effect.tap(() => - Array.isReadonlyArrayNonEmpty(xes) - ? recordEntityWrite([xes[0][idKey], ...xes.slice(1).map((_) => _[idKey])]) - : Effect.void - ), - Effect.withSpan("Repository.mapped.save", spanAttrs, { captureStackTrace: false }) - ) - } - } - } - return r as Repository, RPublish, RCtx> - }) - .pipe(Effect - // .withSpan("Repository.make [effect-app/infra]", { attributes: { "repository.model_name": name } }) - .withLogSpan("Repository.make: " + name)) - } - - return { - make, - Q: Q.make() - } - } -} - -const pluralize = (s: string) => - s.endsWith("s") - ? s + "es" - : s.endsWith("y") - ? s.substring(0, s.length - 1) + "ies" - : s + "s" - -export function makeStore() { - return < - ItemType extends string, - R, - E, - T, - IdKey extends keyof Encoded - >( - name: ItemType, - schema: S.Codec, - mapTo: (e: E, etag: string | undefined) => Encoded, - idKey: IdKey - ) => { - function makeStore( - makeInitial?: Effect.Effect, - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - ) { - function encodeToEncoded() { - const getEtag = () => undefined - return (t: T) => - S.encodeEffect(schema)(t).pipe( - Effect.orDie, - Effect.map((_) => mapToPersistenceModel(_, getEtag)) - ) - } - - function mapToPersistenceModel( - e: E, - getEtag: (id: string) => string | undefined - ): Encoded { - return mapTo(e, getEtag((e as any)[idKey] as string)) - } - - return Effect.gen(function*() { - const { make } = yield* StoreMaker - - const store = yield* make( - pluralize(name), - idKey, - makeInitial - ? makeInitial - .pipe( - Effect.flatMap(Effect.forEach(encodeToEncoded())), - setupRequestContextFromCurrent("Repository.makeInitial [effect-app/infra]", { - attributes: { "app.entity": name } - }) - ) - : undefined, - { - ...config, - schema, - partitionValue: config?.partitionValue - ?? ((_) => "primary") /*(isIntegrationEvent(r) ? r.companyId : r.id*/ - } - ) - - return store - }) - } - - return makeStore - } -} - -export interface Repos< - T, - Encoded extends { id: string }, - RSchema, - Evt, - ItemType extends string, - IdKey extends keyof T, - RPublish -> { - make( - args: [Evt] extends [never] ? { - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - : { - publishEvents: (evt: NonEmptyReadonlyArray) => Effect.Effect - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - ): Effect.Effect< - Repository, - E, - StoreMaker | RInitial | R2 | Scope.Scope - > - makeWith( - args: [Evt] extends [never] ? { - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - } - : { - publishEvents: (evt: NonEmptyReadonlyArray) => Effect.Effect - makeInitial?: Effect.Effect | undefined - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - }, - f: (r: Repository) => Out - ): Effect.Effect - readonly Q: ReturnType> - readonly type: Repository -} diff --git a/packages/effect-app/src/Model/Repository/legacy.ts b/packages/effect-app/src/Model/Repository/legacy.ts deleted file mode 100644 index bac20bb3e..000000000 --- a/packages/effect-app/src/Model/Repository/legacy.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { DatabaseError, OptimisticConcurrencyException } from "../../client/errors.ts" -import type * as Effect from "../../Effect.ts" -import type * as Option from "../../Option.ts" -import type * as S from "../../Schema.ts" - -export interface Mapped1 { - all: Effect.Effect - save: (...xes: readonly A[]) => Effect.Effect - find: (id: A[IdKey]) => Effect.Effect, S.SchemaError | DatabaseError, R> -} - -// TODO: auto use project, and select fields from the From side of schema only -export interface Mapped2 { - all: Effect.Effect -} - -export interface Mapped { - (schema: S.Codec): Mapped1 - // TODO: constrain on Encoded2 having to contain only fields that fit Encoded - (schema: S.Codec): Mapped2 -} - -export interface MM { - (schema: S.Codec): Effect.Effect, never, Repo> - // TODO: constrain on Encoded2 having to contain only fields that fit Encoded - (schema: S.Codec): Effect.Effect, never, Repo> -} diff --git a/packages/effect-app/src/Model/Repository/makeRepo.ts b/packages/effect-app/src/Model/Repository/makeRepo.ts deleted file mode 100644 index 30979b03b..000000000 --- a/packages/effect-app/src/Model/Repository/makeRepo.ts +++ /dev/null @@ -1,168 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-object-type */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -// import type { ParserEnv } from "../../Schema/custom/Parser.ts" - -import type * as Scope from "effect/Scope" -import type { NonEmptyReadonlyArray } from "../../Array.ts" -import type * as Context from "../../Context.ts" -import type * as DataDependencies from "../../DataDependencies.ts" -import * as Effect from "../../Effect.ts" -import type * as S from "../../Schema.ts" -import type { JsonRecord, StoreConfig, StoreMaker } from "../../Store.ts" -import type { FieldValues } from "../filter/types.ts" -import { type ExtendedRepository, extendRepo } from "./ext.ts" -import { makeRepoInternal } from "./internal/internal.ts" -import { RepositoryRegistry } from "./Registry.ts" -import type { Repository } from "./service.ts" - -export interface RepositoryOptions< - IdKey extends keyof T & keyof Encoded, - Encoded extends FieldValues, - T, - ItemType extends string, - Evt = never, - RPublish = never, - E = never, - RInitial = never, - RCtx = never, - RSchema = never -> { - /** - * Specify the idKey of the Type side, if it's different from the default "id". - * Does not change the Encoded side, which is always "id" to support database drivers. - * At this time as queries are operating on the Encoded side, the queries must still specify "id" regardless. - */ - idKey: IdKey - /** - * just in time Migration: for complex migrations that aren't just default simple values - * use the config.defaultValues instead for simple default values - * - * Runs at the **store boundary** on the raw JSON document, after - * `config.defaultValues` have been merged in and *before* the JSON→Encoded - * decode. It sees the document as stored - a `Date` field as an ISO string, - * a `ReadonlyMap` as an array of pairs, an explicit `null` as `null` - and - * must return JSON again, not native Encoded values. - * - * @see StoreConfig.jitM - */ - jitM?: (json: JsonRecord) => JsonRecord - config?: Omit, "partitionValue"> & { - partitionValue?: (e?: Encoded) => string - } - /** - * Optional handler to be able to publish events after successfull save. - */ - publishEvents?: (evt: NonEmptyReadonlyArray) => Effect.Effect - /** - * Optional creator for initial data in the table when it's created for the first itme. - */ - makeInitial?: Effect.Effect | undefined - /** - * Optional context to be provided to Schema decode/encode. - * Useful for effectful transformations like XWithItems, where items is a transformation retrieving elements from another database table or other source. - */ - schemaContext?: Context.Context - - /** IDs, including aliases, that identify an entity for dependency invalidation. */ - dependencyIds?: (item: T) => NonEmptyReadonlyArray - - /** - * Additional derived queries or resources affected by writing an item. - * Saves invalidate dependencies derived from both the previous and next item; - * removals derive them from the removed item. - */ - additionalWriteDependencies?: (item: T) => ReadonlyArray - - overrides?: ( - repo: Repository, RPublish, RCtx> - ) => Repository, RPublish, RCtx> -} - -/** - * Create a repository instance. - * @param itemType an identifier used for the table name and e.g NotFoundError - * @param schema the Schema used for this Repository - * @param options @see RepositoryOptions - * @returns a Repository - */ -export const makeRepo: { - < - ItemType extends string, - RSchema, - Encoded extends FieldValues, - T, - IdKey extends keyof T & keyof Encoded, - E = never, - Evt = never, - RInitial = never, - RPublish = never, - RCtx = never - >( - itemType: ItemType, - schema: S.Codec, - options: RepositoryOptions - ): Effect.Effect< - ExtendedRepository, RPublish, RCtx>, - E, - RInitial | StoreMaker | RepositoryRegistry | Scope.Scope - > - < - ItemType extends string, - RSchema, - Encoded extends FieldValues, - T extends { id: unknown }, - E = never, - Evt = never, - RInitial = never, - RPublish = never, - RCtx = never - >( - itemType: ItemType, - schema: S.Codec, - options: Omit, "idKey"> - ): Effect.Effect< - ExtendedRepository, RPublish, RCtx>, - E, - RInitial | StoreMaker | RepositoryRegistry | Scope.Scope - > -} = < - ItemType extends string, - RSchema, - Encoded extends FieldValues, - T, - IdKey extends keyof T & keyof Encoded, - E = never, - RInitial = never, - RPublish = never, - Evt = never, - RCtx = never ->( - itemType: ItemType, - schema: S.Codec, - options: Omit, "idKey"> & { - idKey?: IdKey - } -) => - Effect.gen(function*() { - const mkRepo = makeRepoInternal()( - itemType, - schema, - (e, _etag) => ({ ...e, _etag }), - options.idKey ?? "id" as any - ) - // `jitM` belongs to the store: it migrates the raw JSON document before the - // store decodes JSON→Encoded, so it travels as part of the store config. - const { jitM, ...rest } = options - let r = yield* mkRepo.make( - (jitM ? { ...rest, config: { ...rest.config, jitM } } : rest) as any - ) - if (options.overrides) r = options.overrides(r) - const repo = extendRepo(r) - const registry = yield* RepositoryRegistry - registry.register(itemType, repo) - return repo - }) diff --git a/packages/effect-app/src/Model/Repository/service.ts b/packages/effect-app/src/Model/Repository/service.ts deleted file mode 100644 index 875a0cd17..000000000 --- a/packages/effect-app/src/Model/Repository/service.ts +++ /dev/null @@ -1,676 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type * as Scope from "effect/Scope" -import type { DatabaseError, InvalidStateError, NotFoundError, OptimisticConcurrencyException } from "../../client/errors.ts" -import type * as Effect from "../../Effect.ts" -import type * as Option from "../../Option.ts" -import type * as S from "../../Schema.ts" -import type { NonNegativeInt } from "../../Schema/numbers.ts" -import type { FieldValues, IsNever, ResolveFirstLevel } from "../filter/types.ts" -import type { QAll, Query, QueryProjection, RawQuery } from "../query.ts" -import type { Mapped } from "./legacy.ts" -import type { ValidationResult } from "./validation.ts" - -/** - * Event emitted by a repository's ChangeFeed. - * - * Tuple shape: `[items, op, namespace]` — `namespace` is the `storeId` - * context value at publish time (defaults to `"primary"`). - */ -export type ChangeFeedEvent = readonly [items: T[], op: "save" | "remove", namespace: string] - -/** - * Synchronous broadcast channel for repository change events. - * - * `publish` only completes after every matching handler's Effect has - * finished — callers must await all handlers before continuing. Subscribers - * can target a single namespace via `options.namespace`, or omit it to - * receive events from every namespace (wildcard). - * - * Handlers are auto-removed when the subscriber's Scope closes; the full - * handler set is cleared when the repository's own scope closes. - */ -export interface ChangeFeed { - readonly publish: (evt: [T[], "save" | "remove"]) => Effect.Effect - readonly subscribe: ( - handler: (evt: ChangeFeedEvent) => Effect.Effect, - options?: { readonly namespace?: string } - ) => Effect.Effect -} - -export interface Repository< - T, - Encoded extends FieldValues, - Evt, - ItemType extends string, - IdKey extends keyof T, - RSchema, - RPublish, - RProvided = never -> { - readonly itemType: ItemType - readonly idKey: IdKey - readonly find: (id: T[IdKey]) => Effect.Effect, DatabaseError, RSchema> - readonly all: Effect.Effect - readonly saveAndPublish: ( - items: Iterable, - events?: Iterable - ) => Effect.Effect - readonly changeFeed: ChangeFeed - readonly removeAndPublish: ( - items: Iterable, - events?: Iterable - ) => Effect.Effect - - readonly removeById: ( - idOrIds: T[IdKey] | ReadonlyArray - ) => Effect.Effect - - readonly queryRaw: ( - schema: S.Codec, - raw: RawQuery - ) => Effect.Effect - - /** - * Explicitly seed a namespace. Primary is seeded eagerly on initialization. - * Non-primary namespaces must be seeded explicitly before use. - */ - readonly seedNamespace: (namespace: string) => Effect.Effect - - readonly query: { - // ending with projection - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - E extends boolean = false - >( - q: ( - initial: Query - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: ( - _: $A - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: ( - _: $B - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: ( - _: $C - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: ( - _: $D - ) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: (_: $H) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - < - A, - R, - From extends FieldValues, - TType extends "one" | "many" | "count" = "many", - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - $I = never, - E extends boolean = false - >( - q1: ( - initial: Query - ) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: (_: $H) => $I, - q10: (_: $I) => QueryProjection - ): Effect.Effect< - TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A, - | (TType extends "many" ? never : NotFoundError) - | (TType extends "count" ? never : S.SchemaError) - | DatabaseError, - Exclude | RSchema - > - - // ending with generic query - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - E extends boolean = false - >( - q: (initial: Query) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: ( - _: $A - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: ( - _: $A - ) => $B, - q3: ( - _: $B - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: ( - _: $C - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: ( - _: $D - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: ( - _: $E - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: ( - _: $F - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: ( - _: $G - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray - : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: ( - _: $H - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray - : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - < - R = never, - TType extends "one" | "many" = "many", - EncodedRefined extends Encoded = Encoded, - $A = never, - $B = never, - $C = never, - $D = never, - $E = never, - $F = never, - $G = never, - $H = never, - $I = never, - E extends boolean = false - >( - q1: (initial: Query) => $A, - q2: (_: $A) => $B, - q3: (_: $B) => $C, - q4: (_: $C) => $D, - q5: (_: $D) => $E, - q6: (_: $E) => $F, - q7: (_: $F) => $G, - q8: (_: $G) => $H, - q9: (_: $H) => $I, - q10: ( - _: $I - ) => QAll, R, TType, E> - ): Effect.Effect< - TType extends "many" ? DistributeQueryIfExclusiveOnArray - : RefineTHelper, - (TType extends "many" ? never : NotFoundError) | DatabaseError, - Exclude | RSchema - > - } - - /** @deprecated use query */ - readonly mapped: Mapped - - /** - * Validates a random sample of repository items by applying jitM and schema decode. - * Useful for testing that migrations and schema changes work correctly on existing data. - */ - readonly validateSample: (options?: { - /** percentage of items to sample (0.0-1.0), default 0.1 (10%) */ - percentage?: number - /** optional maximum number of items to sample */ - maxItems?: number - }) => Effect.Effect -} - -type DistributeQueryIfExclusiveOnArray = [Exclusive] extends [true] - ? T extends any ? RefineTHelper extends infer R ? IsNever extends true ? never - : readonly R[] - : never - : never - : readonly RefineTHelper[] - -type NullableRefined = { - // EncodedRefined may be a union, so if you just keyof you'll get just common keys - // p.s. NullableRefined is homomorphic in T so it distributes itself over T - [k in keyof T]: [null] extends [T[k]] ? [null] extends [Extract[k]] ? T[k] - : Exclude - : T[k] -} - -// for DX purposes -type ShouldRecursiveExtractTagged = true extends { - [k in keyof T]: "_tag" extends keyof T[k] - ? k extends keyof EncodedRefined ? "_tag" extends keyof EncodedRefined[k] ? true - : false - : false - : false -}[keyof T] ? true - : false - -// support is not 100% but we have to make compromises -type RecursiveExtractTagged = ShouldRecursiveExtractTagged extends true ? { - [k in keyof T]: "_tag" extends keyof T[k] - ? k extends keyof EncodedRefined ? "_tag" extends keyof EncodedRefined[k] ? ExtractTagged - : T[k] - : T[k] - : T[k] - } - : T - -type ExtractTagged_ = EncodedRefined extends { _tag: string } - ? T extends { _tag: string } ? Extract - : T - : T - -type ExtractTagged = ExtractTagged_ extends infer R - ? RecursiveExtractTagged, EncodedRefined> - : never - -type ShouldRecursiveExtractIded = true extends { - [k in keyof T]: "id" extends keyof T[k] ? k extends keyof EncodedRefined ? "id" extends keyof EncodedRefined[k] ? true - : false - : false - : false -}[keyof T] ? true - : false - -type RecusiveExtractIded = ShouldRecursiveExtractIded extends true ? { - [k in keyof T]: "id" extends keyof T[k] - ? k extends keyof EncodedRefined ? "id" extends keyof EncodedRefined[k] ? ExtractIded - : T[k] - : T[k] - : T[k] - } - : T - -type ExtractIded_ = EncodedRefined extends { id: string } - ? T extends { id: string } ? Extract - : T - : T - -type ExtractIded = ExtractIded_ extends infer R - ? RecusiveExtractIded, EncodedRefined> - : never - -export type RefineTHelper = ResolveFirstLevel< - NullableRefined< - ExtractIded, EncodedRefined>, - EncodedRefined - > -> diff --git a/packages/effect-app/src/Model/Repository/validation.ts b/packages/effect-app/src/Model/Repository/validation.ts deleted file mode 100644 index 25b34b058..000000000 --- a/packages/effect-app/src/Model/Repository/validation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as S from "../../Schema.ts" -import { NonNegativeInt } from "../../Schema.ts" - -/** - * Represents a single validation error when decoding a repository item. - * Contains full context for debugging: the stored data and the decode error. - */ -export class ValidationError extends S.Opaque()(S.Struct({ - /** the id of the item that failed validation */ - id: S.Unknown, - /** - * the data as returned by the store: `defaultValues` merged, `jitM` applied - * to the raw JSON document, and JSON decoded to the Encoded shape - */ - rawData: S.Unknown, - /** - * @deprecated identical to {@link rawData}. `jitM` now runs inside the store, - * before the JSON→Encoded decode, so the repository never sees the document - * as it was before the migration. - */ - jitMResult: S.Unknown, - /** the S.SchemaError from schema decode */ - error: S.Unknown -})) {} - -/** - * Result of validating a sample of repository items. - */ -export class ValidationResult extends S.Opaque()(S.Struct({ - /** total number of items in the repository */ - total: NonNegativeInt, - /** number of items that were sampled for validation */ - sampled: NonNegativeInt, - /** number of items that passed validation */ - valid: NonNegativeInt, - /** list of validation errors with full context */ - errors: S.Array(ValidationError) -})) {} diff --git a/packages/effect-app/src/Model/dsl.ts b/packages/effect-app/src/Model/dsl.ts deleted file mode 100644 index 0cfef0a68..000000000 --- a/packages/effect-app/src/Model/dsl.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { isReadonlyArrayNonEmpty, type NonEmptyReadonlyArray } from "../Array.ts" -import * as Effect from "../Effect.ts" -import * as Option from "../Option.ts" -import { type FixEnv, get, logMany, type Pure, type PureEnvEnv, type PureLogT, set } from "../Pure.ts" - -export interface PureDSL { - get: Pure - set: (s: S2) => Pure - log: (...w: W[]) => PureLogT -} - -export const AnyPureDSL: PureDSL = { - get: get(), - set: set as any, - log: (...evt: any[]) => logMany(evt) -} - -const anyDSL = makeDSL() - -export type AllDSL = - & (( - pure: (dsl: PureDSL) => Effect.Effect - ) => Effect.Effect>) - & AllDSLExt - -export interface AllDSLExt { - modify: ( - pure: (items: readonly S1[], dsl: PureDSL) => Effect.Effect - ) => Effect.Effect> - update: ( - pure: (items: readonly S1[], log: (...evt: Evt[]) => PureLogT) => Effect.Effect - ) => Effect.Effect> - updateWith: ( - upd: (item: readonly S1[]) => readonly S2[] - ) => Effect.Effect> -} - -export function makeAllDSL() { - const dsl: AllDSL = anyDSL - return dsl -} - -export type OneDSL = - & (( - pure: (dsl: PureDSL) => Effect.Effect - ) => Effect.Effect>) - & OneDSLExt - -export interface OneDSLExt { - modify: ( - pure: (items: S1, dsl: PureDSL) => Effect.Effect - ) => Effect.Effect | PureEnvEnv> - update: ( - pure: (items: S1, log: (...evt: Evt[]) => PureLogT) => Effect.Effect - ) => Effect.Effect> - updateWith: ( - upd: (item: S1) => S2 - ) => Effect.Effect> -} - -export function makeOneDSL(): OneDSL { - return anyDSL -} - -export function makeDSL() { - const dsl: PureDSL = AnyPureDSL - - function modify< - R, - E, - A - >( - pure: ( - items: S1, - dsl: PureDSL - ) => Effect.Effect - ): Effect.Effect> { - return dsl.get.pipe(Effect.flatMap((items) => pure(items, dsl))) as any - } - - function update< - R, - E - >( - pure: ( - items: S1, - log: (...evt: Evt[]) => PureLogT - ) => Effect.Effect - ): Effect.Effect> { - return dsl.get.pipe(Effect.flatMap((items) => pure(items, dsl.log).pipe(Effect.tap(dsl.set)))) as any - } - - function withDSL< - R, - A, - E - >( - pure: (dsl: PureDSL) => Effect.Effect - ): Effect.Effect> { - return pure(AnyPureDSL) as any - } - - function updateWith(upd: (item: S1) => S2) { - return update((_: S1) => Effect.sync(() => upd(_))) - } - - return Object.assign( - withDSL, - { - modify, - update, - updateWith - } - ) -} - -export interface DSLExt extends ReturnType> {} - -export function ifAny(fn: (items: NonEmptyReadonlyArray) => Effect.Effect) { - return (items: Iterable) => - Effect.flatMapOption(Effect.sync(() => Option.liftPredicate(isReadonlyArrayNonEmpty)([...items])), fn) -} - -export function ifAny_( - items: Iterable, - fn: (items: NonEmptyReadonlyArray) => Effect.Effect -) { - return Effect.flatMapOption(Effect.sync(() => Option.liftPredicate(isReadonlyArrayNonEmpty)([...items])), fn) -} diff --git a/packages/effect-app/src/Model/filter/filterApi.ts b/packages/effect-app/src/Model/filter/filterApi.ts deleted file mode 100644 index 81c5eb250..000000000 --- a/packages/effect-app/src/Model/filter/filterApi.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { type RelationDirection } from "../query.ts" - -export type InOps = - | "in" - | "notIn" - -export type OtherOps = - | "endsWith" - | "startsWith" - | "notEndsWith" - | "notStartsWith" - | "contains" - | "notContains" - | "includes" - | "notIncludes" - | "includes-any" - | "notIncludes-any" - | "includes-all" - | "notIncludes-all" - | "hasKey" - | "notHasKey" - | "hasValue" - | "notHasValue" - | "hasKeyValue" - | "notHasKeyValue" - | "hasKey-any" - | "notHasKey-any" - | "hasKey-all" - | "notHasKey-all" - | "hasValue-any" - | "notHasValue-any" - | "hasValue-all" - | "notHasValue-all" - | "hasKeyValue-any" - | "notHasKeyValue-any" - | "hasKeyValue-all" - | "notHasKeyValue-all" - | "eq" - | "neq" - | "gt" - | "gte" - | "lt" - | "lte" - -export type Ops = OtherOps | InOps - -export type FilterScopes = { - t: "or-scope" - result: readonly FilterResult[] - relation: RelationDirection -} | { - t: "and-scope" - result: readonly FilterResult[] - relation: RelationDirection -} | { - t: "where-scope" - result: readonly FilterResult[] - relation: RelationDirection -} - -export type FilterR = { - op: Ops - - path: string - value: unknown -} - -export type FilterResult = - | { - t: "where" - } & FilterR - | { - t: "or" - } & FilterR - | { - t: "and" - } & FilterR - | FilterScopes diff --git a/packages/effect-app/src/Model/filter/types.ts b/packages/effect-app/src/Model/filter/types.ts deleted file mode 100644 index d4cca62cf..000000000 --- a/packages/effect-app/src/Model/filter/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -// codegen:start {preset: barrel, include: ./types/*.ts} -export * from "./types/errors.ts" -export * from "./types/fields.ts" -export * from "./types/utils.ts" -export * from "./types/validator.ts" -// codegen:end diff --git a/packages/effect-app/src/Model/filter/types/errors.ts b/packages/effect-app/src/Model/filter/types/errors.ts deleted file mode 100644 index f6501cb94..000000000 --- a/packages/effect-app/src/Model/filter/types/errors.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { FieldValues, InternalFieldName } from "./fields.ts" -import type { BrowserNativeObject, IsAny, LiteralUnion, Merge } from "./utils.ts" -import type { RegisterOptions, ValidateResult } from "./validator.ts" - -export type Message = string - -export type MultipleFieldErrors = - & { - [K in keyof RegisterOptions]?: ValidateResult - } - & { - [key: string]: ValidateResult - } - -export type FieldError = { - type: LiteralUnion - root?: FieldError - // ref?: Ref - types?: MultipleFieldErrors - message?: Message -} - -export type ErrorOption = { - message?: Message - type?: LiteralUnion - types?: MultipleFieldErrors -} - -export type DeepRequired = T extends BrowserNativeObject | Blob ? T - : { - [K in keyof T]-?: NonNullable> - } - -export type FieldErrorsImpl = { - [K in keyof T]?: T[K] extends BrowserNativeObject | Blob ? FieldError - : T[K] extends object ? Merge> - : FieldError -} - -export type FieldErrors = Partial< - FieldValues extends IsAny ? any - : FieldErrorsImpl> -> - -export type InternalFieldErrors = Partial< - Record -> diff --git a/packages/effect-app/src/Model/filter/types/fields.ts b/packages/effect-app/src/Model/filter/types/fields.ts deleted file mode 100644 index 73808a451..000000000 --- a/packages/effect-app/src/Model/filter/types/fields.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { IsFlatObject } from "./utils.ts" -import type { RegisterOptions } from "./validator.ts" - -export type InternalFieldName = string - -export type FieldName = IsFlatObject extends true - ? Extract - : string - -// export type CustomElement = { -// name: FieldName -// type?: string -// value?: any -// disabled?: boolean -// checked?: boolean -// options?: HTMLOptionsCollection -// files?: FileList | null -// focus?: Noop -// } - -export type FieldValue = TFieldValues[InternalFieldName] - -export type FieldValues = Record - -export type NativeFieldValue = - | string - | number - | boolean - | null - | undefined - | unknown[] - -// export type FieldElement = -// | HTMLInputElement -// | HTMLSelectElement -// | HTMLTextAreaElement -// | CustomElement - -// export type Ref = FieldElement - -export type Field = { - _f: { - // ref: Ref - name: InternalFieldName - // refs?: HTMLInputElement[] - mount?: boolean - } & RegisterOptions -} - -export type FieldRefs = Partial> diff --git a/packages/effect-app/src/Model/filter/types/path/common.ts b/packages/effect-app/src/Model/filter/types/path/common.ts deleted file mode 100644 index 40324c0d0..000000000 --- a/packages/effect-app/src/Model/filter/types/path/common.ts +++ /dev/null @@ -1,404 +0,0 @@ -import type { IsAny, IsNever } from "../utils.ts" - -/** - * Type alias to `string` which describes a lodash-like path through an object. - * E.g. `'foo.bar.0.baz'` - */ -export type PathString = string - -/** - * Type which can be traversed through with a {@link PathString}. - * I.e. objects, arrays, and tuples - */ -export type Traversable = object - -/** - * Type to query whether an array type T is a tuple type. - * @typeParam T - type which may be an array or tuple - * @example - * ``` - * IsTuple<[number]> = true - * IsTuple = false - * ``` - */ -export type IsTuple> = number extends T["length"] ? false - : true - -/** - * Type which can be used to index an array or tuple type. - */ -export type ArrayKey = number - -/** - * Type which can be used to index an object. - */ -export type Key = string - -/** - * Type to assert that a type is a {@link Key}. - * @typeParam T - type which may be a {@link Key} - */ -export type AsKey = Extract - -/** - * Type to convert a type to a {@link Key}. - * @typeParam T - type which may be converted to a {@link Key} - */ -export type ToKey = T extends ArrayKey ? `${T}` : AsKey - -/** - * Type which describes a path through an object - * as a list of individual {@link Key}s. - */ -export type PathTuple = Key[] - -/** - * Type to assert that a type is a {@link PathTuple}. - * @typeParam T - type which may be a {@link PathTuple} - */ -export type AsPathTuple = Extract - -/** - * Type to intersect a union type. - * See https://fettblog.eu/typescript-union-to-intersection/ - * @typeParam U - union - * @example - * ``` - * UnionToIntersection<{ foo: string } | { bar: number }> - * = { foo: string; bar: number } - * ``` - */ -export type UnionToIntersection = ( - U extends any ? (_: U) => any : never -) extends (_: infer I) => any ? I - : never - -/** - * Type which appends a {@link Key} to the {@link PathTuple} only if it is not - * blank, i.e. not the empty string. - * @typeParam PT - path - * @typeParam K - key - * @example - * ``` - * AppendNonBlankKey<['foo'], 'bar'> = ['foo', 'bar'] - * AppendNonBlankKey<['foo'], ''> = ['foo'] - * ``` - */ -type AppendNonBlankKey = K extends "" ? PT - : [...PT, K] - -/** - * Type to implement {@link SplitPathString} tail recursively. - * @typeParam PS - remaining {@link PathString} which should be split into its - * individual {@link Key}s - * @typeParam PT - accumulator of the {@link Key}s which have been split from - * the original {@link PathString} already - */ -type SplitPathStringImpl< - PS extends PathString, - PT extends PathTuple -> = PS extends `${infer K}.${infer R}` ? SplitPathStringImpl> - : AppendNonBlankKey - -/** - * Type to split a {@link PathString} into a {@link PathTuple}. - * The individual {@link Key}s may be empty strings. - * @typeParam PS - {@link PathString} which should be split into its - * individual {@link Key}s - * @example - * ``` - * SplitPathString<'foo'> = ['foo'] - * SplitPathString<'foo.bar.0.baz'> = ['foo', 'bar', '0', 'baz'] - * SplitPathString<'.'> = [] - * ``` - */ -export type SplitPathString = SplitPathStringImpl< - PS, - [] -> - -/** - * Type to implement {@link JoinPathTuple} tail-recursively. - * @typeParam PT - remaining {@link Key}s which needs to be joined - * @typeParam PS - accumulator of the already joined {@link Key}s - */ -type JoinPathTupleImpl< - PT extends PathTuple, - PS extends PathString -> = PT extends [infer K, ...infer R] ? JoinPathTupleImpl, `${PS}.${AsKey}`> - : PS - -/** - * Type to join a {@link PathTuple} to a {@link PathString}. - * @typeParam PT - {@link PathTuple} which should be joined. - * @example - * ``` - * JoinPathTuple<['foo']> = 'foo' - * JoinPathTuple<['foo', 'bar', '0', 'baz']> = 'foo.bar.0.baz' - * JoinPathTuple<[]> = never - * ``` - */ -export type JoinPathTuple = PT extends [ - infer K, - ...infer R -] ? JoinPathTupleImpl, AsKey> - : never - -/** - * Type which converts all keys of an object to {@link Key}s. - * @typeParam T - object type - * @example - * ``` - * MapKeys<{0: string}> = {'0': string} - * ``` - */ -type MapKeys = { [K in keyof T as ToKey]: T[K] } - -/** - * Type to access a type by a key. - * - Returns undefined if it can't be indexed by that key. - * - Returns null if the type is null. - * - Returns undefined if the type is not traversable. - * @typeParam T - type which is indexed by the key - * @typeParam K - key into the type - * ``` - * TryAccess<{foo: string}, 'foo'> = string - * TryAccess<{foo: string}, 'bar'> = undefined - * TryAccess = null - * TryAccess = undefined - * ``` - */ -type TryAccess = K extends keyof T ? T[K] - : T extends null ? null - : undefined - -/** - * Type to access an array type by a key. - * Returns undefined if the key is non-numeric. - * @typeParam T - type which is indexed by the key - * @typeParam K - key into the type - * ``` - * TryAccessArray = string - * TryAccessArray = undefined - * ``` - */ -type TryAccessArray< - T extends ReadonlyArray, - K extends Key -> = K extends `${ArrayKey}` ? T[number] : TryAccess - -/** - * Type to evaluate the type which the given key points to. - * @typeParam T - type which is indexed by the key - * @typeParam K - key into the type - * @example - * ``` - * EvaluateKey<{foo: string}, 'foo'> = string - * EvaluateKey<[number, string], '1'> = string - * EvaluateKey = string - * ``` - */ -export type EvaluateKey = T extends ReadonlyArray ? IsTuple extends true ? TryAccess - : TryAccessArray - : TryAccess, K> - -/** - * Type to evaluate the type which the given path points to. - * @typeParam T - deeply nested type which is indexed by the path - * @typeParam PT - path into the deeply nested type - * @example - * ``` - * EvaluatePath<{foo: {bar: string}}, ['foo', 'bar']> = string - * EvaluatePath<[number, string], ['1']> = string - * EvaluatePath = number - * EvaluatePath = undefined - * ``` - */ -export type EvaluatePath = PT extends [ - infer K, - ...infer R -] ? EvaluatePath>, AsPathTuple> - : T - -/** - * Type which given a tuple type returns its own keys, i.e. only its indices. - * @typeParam T - tuple type - * @example - * ``` - * TupleKeys<[number, string]> = '0' | '1' - * ``` - */ -export type TupleKeys> = Exclude< - keyof T, - keyof any[] -> - -/** - * Type which extracts all numeric keys from an object. - * @typeParam T - type - * @example - * ``` - * NumericObjectKeys<{0: string, '1': string, foo: string}> = '0' | '1' - * ``` - */ -type NumericObjectKeys = ToKey< - Extract -> - -/** - * Type which extracts all numeric keys from an object, tuple, or array. - * If a union is passed, it evaluates to the overlapping numeric keys. - * @typeParam T - type - * @example - * ``` - * NumericKeys<{0: string, '1': string, foo: string}> = '0' | '1' - * NumericKeys = `${number}` - * NumericKeys<[string, number]> = '0' | '1' - * NumericKeys<{0: string, '1': string} | [number] | number[]> = '0' - * ``` - */ -export type NumericKeys = UnionToIntersection< - T extends ReadonlyArray ? IsTuple extends true ? [TupleKeys] - : [ToKey] - : [NumericObjectKeys] ->[never] - -/** - * Type which extracts all keys from an object. - * If a union is passed, it evaluates to the overlapping keys. - * @typeParam T - object type - * @example - * ``` - * ObjectKeys<{foo: string, bar: string}, string> = 'foo' | 'bar' - * ObjectKeys<{foo: string, bar: number}, string> = 'foo' - * ``` - */ -export type ObjectKeys = Exclude< - ToKey, - `${string}.${string}` | "" -> - -/** - * Type to check whether a type's property matches the constraint type - * and return its key. Converts the key to a {@link Key}. - * @typeParam T - type whose property should be checked - * @typeParam K - key of the property - * @typeParam U - constraint type - * @example - * ``` - * CheckKeyConstraint<{foo: string}, 'foo', string> = 'foo' - * CheckKeyConstraint<{foo: string}, 'foo', number> = never - * CheckKeyConstraint = `${number}` - * ``` - */ -export type CheckKeyConstraint = K extends any ? EvaluateKey extends U ? K - : never - : never - -/** - * Type which evaluates to true when the type is an array or tuple or is a union - * which contains an array or tuple. - * @typeParam T - type - * @example - * ``` - * ContainsIndexable<{foo: string}> = false - * ContainsIndexable<{foo: string} | number[]> = true - * ``` - */ -export type ContainsIndexable = IsNever< - Extract> -> extends true ? false - : true - -/** - * Type to implement {@link Keys} for non-nullable values. - * @typeParam T - non-nullable type whose property should be checked - */ -type KeysImpl = [T] extends [Traversable] ? ContainsIndexable extends true ? NumericKeys - : ObjectKeys - : never - -/** - * Type to find all properties of a type that match the constraint type - * and return their keys. - * If a union is passed, it evaluates to the overlapping keys. - * @typeParam T - type whose property should be checked - * @typeParam U - constraint type - * @example - * ``` - * Keys<{foo: string, bar: string}, string> = 'foo' | 'bar' - * Keys<{foo?: string, bar?: string}> = 'foo' | 'bar' - * Keys<{foo: string, bar: number}, string> = 'foo' - * Keys<[string, number], string> = '0' - * Keys = `${number}` - * Keys<{0: string, '1': string} | [number] | number[]> = '0' - * ``` - */ -export type Keys = IsAny extends true ? Key - : IsNever extends true ? Key - : IsNever> extends true ? never - : CheckKeyConstraint>, U> - -/** - * Type to check whether a {@link Key} is present in a type. - * If a union of {@link Key}s is passed, all {@link Key}s have to be present - * in the type. - * @typeParam T - type which is introspected - * @typeParam K - key - * @example - * ``` - * HasKey<{foo: string}, 'foo'> = true - * HasKey<{foo: string}, 'bar'> = false - * HasKey<{foo: string}, 'foo' | 'bar'> = false - * ``` - */ -export type HasKey = IsNever>> - -/** - * Type to implement {@link ValidPathPrefix} tail recursively. - * @typeParam T - type which the path should be checked against - * @typeParam PT - path which should exist within the given type - * @typeParam VPT - accumulates the prefix of {@link Key}s which have been - * confirmed to exist already - */ -type ValidPathPrefixImpl< - T, - PT extends PathTuple, - VPT extends PathTuple -> = PT extends [infer K, ...infer R] ? HasKey> extends true ? ValidPathPrefixImpl< - EvaluateKey>, - AsPathTuple, - AsPathTuple<[...VPT, K]> - > - : VPT - : VPT - -/** - * Type to find the longest path prefix which is still valid, - * i.e. exists within the given type. - * @typeParam T - type which the path should be checked against - * @typeParam PT - path which should exist within the given type - * @example - * ``` - * ValidPathPrefix<{foo: {bar: string}}, ['foo', 'bar']> = ['foo', 'bar'] - * ValidPathPrefix<{foo: {bar: string}}, ['foo', 'ba']> = ['foo'] - * ``` - */ -export type ValidPathPrefix = ValidPathPrefixImpl< - T, - PT, - [] -> - -/** - * Type to check whether a path through a type exists. - * @typeParam T - type which the path should be checked against - * @typeParam PT - path which should exist within the given type - * @example - * ``` - * HasPath<{foo: {bar: string}}, ['foo', 'bar']> = true - * HasPath<{foo: {bar: string}}, ['foo', 'ba']> = false - * ``` - */ -export type HasPath = ValidPathPrefix extends PT ? true - : false diff --git a/packages/effect-app/src/Model/filter/types/path/eager.ts b/packages/effect-app/src/Model/filter/types/path/eager.ts deleted file mode 100644 index 26e8ef271..000000000 --- a/packages/effect-app/src/Model/filter/types/path/eager.ts +++ /dev/null @@ -1,329 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { expectTypeOf } from "@effect/vitest" -import type { FieldValues } from "../fields.ts" -import type { BrowserNativeObject, Equals, IsLiteral, IsNever, Primitive } from "../utils.ts" - -import type { ArrayKey, IsTuple, TupleKeys } from "./common.ts" - -/** - * Helper type for recursively constructing paths through a type. - * `Seen` carries the set of object types already entered on this branch so - * recursion terminates on self-referential types (see {@link Path}). - */ -type PathImpl = V extends - | Primitive - | BrowserNativeObject ? `${K}` - // Stop before re-entering a type already on this branch. Recursive types - // (notably `Json`, the encoded form of `Schema.Defect()`: - // `JsonObject = { [x: string]: Json }`) would otherwise descend forever and - // blow past TS's instantiation limit => TS2589. The key itself stays a valid - // leaf path; only the unbounded descent is cut. - : [V] extends [Seen] ? `${K}` - : `${K}` | `${K}.${Path}` - -/** - * Type which eagerly collects all paths through a type - * @typeParam T - type which should be introspected - * @typeParam Seen - object types already entered on this recursion branch; - * used to terminate on self-referential types - * @example - * ``` - * Path<{foo: {bar: string}}> = 'foo' | 'foo.bar' - * ``` - */ -export type Path = T extends ReadonlyArray ? IsTuple extends true ? { - [K in TupleKeys]-?: PathImpl | "length" - }[TupleKeys] - : PathImpl | "length" - : { - [K in keyof T]-?: PathImpl - }[keyof T] - -/** - * See {@link Path} - */ -export type FieldPath = Path - -{ - // Regression: self-referential types must terminate instead of blowing past - // TS's instantiation limit (TS2589). `JsonLike` mirrors effect's `Json` — the - // encoded form of `Schema.Defect()` — whose `{ [x: string]: Json }` index - // signature previously made `Path` recurse forever. These aliases failing to - // compile (rather than resolving to a string union) signals the regression. - type JsonLike = - | string - | number - | boolean - | null - | ReadonlyArray - | { readonly [x: string]: JsonLike } - type _RawDefect = Path<{ id: string; raw: JsonLike }> - type _NestedDefect = Path<{ a: { b: { error: { raw: JsonLike } } } }> - type _ArrayOfDefect = Path<{ items: ReadonlyArray<{ raw: JsonLike }> }> - // finite paths are still produced - expectTypeOf<"id">().toExtend<_RawDefect>() - expectTypeOf<"raw">().toExtend<_RawDefect>() - expectTypeOf<"a.b.error.raw">().toExtend<_NestedDefect>() - expectTypeOf<`items.${number}.raw`>().toExtend<_ArrayOfDefect>() -} - -/** - * Type to evaluate the type which the given path points to. - * @typeParam T - deeply nested type which is indexed by the path - * @typeParam P - path into the deeply nested type - * @example - * ``` - * PathValue<{foo: {bar: string}}, 'foo.bar'> = string - * PathValue<[number, string], '1'> = string - * ``` - */ -/* dprint-ignore-start */ -export type PathValue> = - T extends any - ? P extends `${infer K}.${infer R}` - ? K extends keyof T - ? R extends Path - ? PathValue - : never - : K extends `${ArrayKey}` - ? T extends ReadonlyArray - ? PathValue> - : never - : never - : P extends keyof T - ? T[P] - : P extends `${ArrayKey}` - ? T extends ReadonlyArray - ? V - : never - : never - : never -/* dprint-ignore-end */ - -/** - * See {@link PathValue} - */ -export type FieldPathValue< - TFieldValues extends FieldValues, - TFieldPath extends FieldPath -> = PathValue - -/* dprint-ignore-start */ -export type SetPathValue, X> = - T extends any - ? P extends `${infer K}.${infer R}` - ? K extends keyof T - ? R extends Path - ? { [_ in keyof T]: _ extends K ? SetPathValue : T[_] } - : never - : K extends `${ArrayKey}` - ? T extends ReadonlyArray - ? { [_ in keyof T]: SetPathValue, X> } - : never - : never - : P extends keyof T - ? { [_ in keyof T]: _ extends P ? X : T[_] } - : P extends `${ArrayKey}` - ? T extends ReadonlyArray - ? { [_ in keyof T]: X } - : never - : never - : never -/* dprint-ignore-end */ - -export type SetFieldPathValue< - TFieldValues extends FieldValues, - TFieldPath extends FieldPath, - X -> = SetPathValue - -/* dprint-ignore-start */ -export type RefinePathValue, X extends string | number | boolean | null | bigint, Exclde extends boolean = false> = - T extends any - // recursive cases - ? P extends `${infer K}.${infer R}` - ? K extends keyof T - ? R extends Path - ? { [_ in keyof T]: _ extends K ? RefinePathValue : T[_] } - : never - : K extends `${ArrayKey}` - ? T extends ReadonlyArray - ? { [_ in keyof T]: RefinePathValue, X, Exclde> } - : never - : never - // base cases - : P extends keyof T - ? X extends T[P] - ? ({ [_ in keyof T]: _ extends P - ? Equals< - Exclde, - true, - Exclude, - IsLiteral>> - > - : T[_] - }) extends infer $T - ? Equals, true, never, $T> - : never - : Equals - : P extends `${ArrayKey}` - ? T extends ReadonlyArray - ? X extends V - ? { [_ in keyof T]: Equals< - Exclde, - true, - Exclude, - IsLiteral>> - > } - : never - : never - : never - : never -/* dprint-ignore-end */ - -export type EraseNeverContainingUnionElements = T extends any ? { - [K in keyof T]: IsNever extends true ? true : false -} extends infer $NC ? true extends $NC[keyof $NC] ? never : T -: never : never - -export type RefineFieldPathValue< - TFieldValues extends FieldValues, - TFieldPath extends FieldPath, - X extends string | number | boolean | null | bigint, - Exclde extends boolean = false -> = EraseNeverContainingUnionElements> - -{ - type test1 = RefineFieldPathValue<{ a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } }, "a.b", "tag1"> - expectTypeOf().toEqualTypeOf<{ a: { b: "tag1"; v1: string } }>() - - type test2 = RefineFieldPathValue<{ b: "tag1"; v1: string } | { b: "tag2"; v2: number }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1"; v1: string }>() - - type test3 = RefineFieldPathValue<{ b: "tag1" | "tag2" }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1" }>() - - type test4 = RefineFieldPathValue<{ b: ("tag1" | "tag2")[] }, `b.${number}`, "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1"[] }>() - - type test5 = RefineFieldPathValue<{ b: "tag1"; v1: unknown } | { b: "tag2"; v2: unknown }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: "tag1"; v1: unknown }>() - - type test6 = RefineFieldPathValue< - { a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } } | { something: "else " }, - "a.b", - "tag2" - > - expectTypeOf().toEqualTypeOf<{ a: { b: "tag2"; v2: number } }>() - - type test7 = RefineFieldPathValue<{ b: string | null }, "b", "tag1"> - expectTypeOf().toEqualTypeOf<{ b: string }>() - - type test8 = RefineFieldPathValue<{ b: string | null }, "b", null> - expectTypeOf().toEqualTypeOf<{ b: null }>() - - type test1E = RefineFieldPathValue<{ a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } }, "a.b", "tag1", true> - expectTypeOf().toEqualTypeOf<{ a: { b: "tag2"; v2: number } }>() - - type test2E = RefineFieldPathValue<{ b: "tag1"; v1: string } | { b: "tag2"; v2: number }, "b", "tag2", true> - expectTypeOf().toEqualTypeOf<{ b: "tag1"; v1: string }>() - - type test3E = RefineFieldPathValue<{ b: "tag1" | "tag2" | null }, "b", null, true> - expectTypeOf().toEqualTypeOf<{ b: "tag1" | "tag2" }>() - - type test4E = RefineFieldPathValue<{ b: ("tag1" | "tag2")[] }, `b.${number}`, "tag1", true> - expectTypeOf().toEqualTypeOf<{ b: "tag2"[] }>() - - type test5E = RefineFieldPathValue<{ b: "tag1"; v1: unknown } | { b: "tag2"; v2: unknown }, "b", "tag1", true> - expectTypeOf().toEqualTypeOf<{ b: "tag2"; v2: unknown }>() - - type test6E = RefineFieldPathValue< - { a: { b: "tag1"; v1: string } | { b: "tag2"; v2: number } } | { something: "else " }, - "a.b", - "tag2", - true - > - expectTypeOf().toEqualTypeOf<{ a: { b: "tag1"; v1: string } }>() - - type test10 = RefineFieldPathValue< - | { nested: { a: string; _tag: "a" } | { b: number; _tag: "b" }; _tag: "ab" } - | { nested: { b: number; _tag: "b" } | { c: boolean; _tag: "c" }; _tag: "bc" }, - "nested._tag", - "a" - > - - expectTypeOf().toEqualTypeOf<{ - nested: { - a: string - _tag: "a" - } - _tag: "ab" - }>() -} - -{ - type test1 = SetFieldPathValue<{ foo: { bar: string[] } }, `foo.bar`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: boolean } }>() - - type test1a = SetFieldPathValue<{ foo: { bar: string[]; baz: 12 } }, `foo.bar`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: boolean; baz: 12 } }>() - - type test2 = SetFieldPathValue<{ foo: { bar: string[] } }, `foo.bar.${number}`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: boolean[] } }>() - - type test2a = SetFieldPathValue<{ foo: { bar: readonly string[]; baz: 3 }; ban: 123 }, `foo.bar.${number}`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: { bar: readonly boolean[]; baz: 3 }; ban: 123 }>() - - type test2b = SetFieldPathValue< - { foo: { bar: readonly { a: 1; b: 2 }[]; baz: 3 }; ban: 123 }, - `foo.bar.${number}.b`, - "b" - > - expectTypeOf().toEqualTypeOf<{ foo: { bar: readonly { a: 1; b: "b" }[]; baz: 3 }; ban: 123 }>() - - type test3 = SetFieldPathValue<{ foo: ["a", "b"] }, `foo.0`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: [boolean, "b"] }>() - - type test3a = SetFieldPathValue<{ foo: [{ a: 123 }, "b"] }, `foo.0.a`, boolean> - expectTypeOf().toEqualTypeOf<{ foo: [{ a: boolean }, "b"] }>() -} - -/** - * Type to evaluate the type which the given paths point to. - * @typeParam TFieldValues - field values which are indexed by the paths - * @typeParam TPath - paths into the deeply nested field values - * @example - * ``` - * FieldPathValues<{foo: {bar: string}}, ['foo', 'foo.bar']> - * = [{bar: string}, string] - * ``` - */ -export type FieldPathValues< - TFieldValues extends FieldValues, - TPath extends FieldPath[] | readonly FieldPath[] -> = - & {} - & { - [K in keyof TPath]: FieldPathValue< - TFieldValues, - TPath[K] & FieldPath - > - } - -/** - * Type which eagerly collects all paths through a fieldType that matches a give type - * @typeParam TFieldValues - field values which are indexed by the paths - * @typeParam TValue - the value you want to match into each type - * @example - * ```typescript - * FieldPathByValue<{foo: {bar: number}, baz: number, bar: string}, number> - * = 'foo.bar' | 'baz' - * ``` - */ -export type FieldPathByValue = { - [Key in FieldPath]: FieldPathValue< - TFieldValues, - Key - > extends TValue ? Key - : never -}[FieldPath] diff --git a/packages/effect-app/src/Model/filter/types/path/index.ts b/packages/effect-app/src/Model/filter/types/path/index.ts deleted file mode 100644 index 67d7d71b2..000000000 --- a/packages/effect-app/src/Model/filter/types/path/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** Re-export public API */ - -export type { PathString } from "./common.ts" -export type { FieldPath, FieldPathByValue, FieldPathValue, FieldPathValues, Path, PathValue } from "./eager.ts" diff --git a/packages/effect-app/src/Model/filter/types/utils.ts b/packages/effect-app/src/Model/filter/types/utils.ts deleted file mode 100644 index 95d8aa7db..000000000 --- a/packages/effect-app/src/Model/filter/types/utils.ts +++ /dev/null @@ -1,128 +0,0 @@ -declare const $NestedValue: unique symbol - -/** - * @deprecated to be removed in the next major version - */ -export type NestedValue = { - [$NestedValue]: never -} & TValue - -/** - * @deprecated to be removed in the next major version - */ -export type UnpackNestedValue = T extends NestedValue ? U - : T extends Date | FileList | File | Blob ? T - : T extends object ? { [K in keyof T]: UnpackNestedValue } - : T - -/* -Projects that React Hook Form installed don't include the DOM library need these interfaces to compile. -React Native applications is no DOM available. The JavaScript runtime is ES6/ES2015 only. -These definitions allow such projects to compile with only --lib ES6. - -Warning: all of these interfaces are empty. -If you want type definitions for various properties, you need to add `--lib DOM` (via command line or tsconfig.json). -*/ - -export type Noop = () => void - -interface File extends Blob { - readonly lastModified: number - readonly name: string -} - -interface FileList { - readonly length: number - item(index: number): File | null - [index: number]: File -} - -export type Primitive = - | null - | undefined - | string - | number - | boolean - | symbol - | bigint - -export type BrowserNativeObject = Date | FileList | File - -export type EmptyObject = { [K in string | number]: never } - -export type NonUndefined = T extends undefined ? never : T - -export type LiteralUnion = - | T - | (U & { _?: never }) - -export type DeepPartial = T extends BrowserNativeObject | NestedValue ? T - : { [K in keyof T]?: DeepPartial } - -export type DeepPartialSkipArrayKey = T extends - | BrowserNativeObject - | NestedValue ? T - : T extends ReadonlyArray ? { [K in keyof T]: DeepPartialSkipArrayKey } - : { [K in keyof T]?: DeepPartialSkipArrayKey } - -/** - * Checks whether the type is any - * See {@link https://stackoverflow.com/a/49928360/3406963} - * @typeParam T - type which may be any - * ``` - * IsAny = true - * IsAny = false - * ``` - */ -export type IsAny = 0 extends 1 & T ? true : false - -/** - * Checks whether the type is never - * @typeParam T - type which may be never - * ``` - * IsAny = true - * IsAny = false - * ``` - */ -export type IsNever = [T] extends [never] ? true : false - -export type DeepMap = IsAny extends true ? any - : T extends BrowserNativeObject | NestedValue ? TValue - : T extends object ? { [K in keyof T]: DeepMap, TValue> } - : TValue - -export type IsFlatObject = Extract< - Exclude, - any[] | object -> extends never ? true - : false - -export type Merge = { - [K in keyof A | keyof B]?: K extends keyof A & keyof B ? [A[K], B[K]] extends [object, object] ? Merge - : A[K] | B[K] - : K extends keyof A ? A[K] - : K extends keyof B ? B[K] - : never -} - -export type Resolve = - & { - [K in keyof T]: Resolve - } - & {} - -export type ResolveFirstLevel = - & { - [K in keyof T]: T[K] - } - & {} - -export type Cast = T extends U ? T : U - -export type IsLiteral = string extends T ? False : number extends T ? False : True - -export type Extends = T extends U ? True : False - -export type IsEqual = (<_>() => _ extends T ? 1 : 2) extends (<_>() => _ extends U ? 1 : 2) ? true : false - -export type Equals = IsEqual extends true ? True : False diff --git a/packages/effect-app/src/Model/filter/types/validator.ts b/packages/effect-app/src/Model/filter/types/validator.ts deleted file mode 100644 index 062346819..000000000 --- a/packages/effect-app/src/Model/filter/types/validator.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Message } from "./errors.ts" -import type { FieldValues, InternalFieldName } from "./fields.ts" -import type { FieldPath, FieldPathValue } from "./path/index.ts" - -export type ValidationValue = boolean | number | string | RegExp - -export type ValidationRule< - TValidationValue extends ValidationValue = ValidationValue -> = TValidationValue | ValidationValueMessage - -export type ValidationValueMessage< - TValidationValue extends ValidationValue = ValidationValue -> = { - value: TValidationValue - message: Message -} - -export type ValidateResult = Message | Message[] | boolean | undefined - -export type Validate = ( - value: TFieldValue -) => ValidateResult | Promise - -export type RegisterOptions< - TFieldValues extends FieldValues = FieldValues, - TFieldName extends FieldPath = FieldPath -> = Partial<{ - required: Message | ValidationRule - min: ValidationRule - max: ValidationRule - maxLength: ValidationRule - minLength: ValidationRule - pattern: ValidationRule - validate: - | Validate> - | Record>> - valueAsNumber: boolean - valueAsDate: boolean - value: FieldPathValue - setValueAs: (value: any) => any - shouldUnregister?: boolean - onChange?: (event: any) => void - onBlur?: (event: any) => void - disabled: boolean - deps: InternalFieldName | InternalFieldName[] -}> diff --git a/packages/effect-app/src/Model/query.ts b/packages/effect-app/src/Model/query.ts deleted file mode 100644 index 22d242aca..000000000 --- a/packages/effect-app/src/Model/query.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from "./query/dsl.ts" -export * from "./query/new-kid-interpreter.ts" - -export interface RawQuery { - cosmos: (vals: { name: string }) => { - query: string - parameters: { - name: string - value: any - }[] - } - memory: (t: readonly Encoded[]) => readonly Out[] -} diff --git a/packages/effect-app/src/Model/query/dsl.ts b/packages/effect-app/src/Model/query/dsl.ts deleted file mode 100644 index b91351471..000000000 --- a/packages/effect-app/src/Model/query/dsl.ts +++ /dev/null @@ -1,3092 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as Data from "effect/Data" -import { flow } from "effect/Function" -import * as Pipeable from "effect/Pipeable" -import type { Covariant } from "effect/Types" -import type * as Option from "../../Option.ts" -import type * as S from "../../Schema.ts" -import type { NonNegativeInt } from "../../Schema.ts" -import type { Ops } from "../filter/filterApi.ts" -import type { FieldValues } from "../filter/types.ts" -import type { FieldPath, FieldPathValue, RefineFieldPathValue } from "../filter/types/path/eager.ts" - -export type QAll< - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - A = TFieldValues, - R = never, - TType extends "one" | "many" = "many", - Exclusive extends boolean = false -> = - | Query - | QueryWhere - | QueryEnd - | QueryProjection - -export const QId = Symbol() -export type QId = typeof QId - -export interface QueryTogether< - out TFieldValues extends FieldValues, - out TFieldValuesRefined extends TFieldValues = TFieldValues, - out Exclusive extends boolean = false, - out T extends "initial" | "where" | "end" | "projection" = "initial", - out A = TFieldValues, - out R = never, - out TType extends "many" | "one" | "count" = "many" -> extends Pipeable.Pipeable { - readonly [QId]: { - readonly _TFieldValues: Covariant - readonly _T: Covariant - readonly _TFieldValuesRefined: Covariant - readonly _A: Covariant - readonly _R: Covariant - readonly _TT: Covariant - readonly _Exclusive: Covariant - } -} - -type ExtractTType = T extends QueryTogether ? TType : never -type ExtractExclusiveness = T extends QueryTogether - ? Exclusive - : never -type ExtractFieldValues = T extends QueryTogether ? TFieldValues - : never -type ExtractFieldValuesRefined = T extends QueryTogether - ? TFieldValuesRefined - : never -type KeysOfUnion = T extends T ? keyof T : never -/** Peel Schema `tag`/`Literal` brands down to the underlying literal value. */ -type LiteralValue = T extends { readonly literal: infer L } ? L - : T extends string | number | boolean | null | bigint ? T - // Schema.tag<"x"> / withConstructorDefault wrappers are object brands whose - // nominal shape is not a PropertyKey — fall back to never so callers treat - // them via UnwrapTag below. - : never -// Last-resort: if T is a single object brand wrapping a string literal in its -// structure, treat non-union object tags as single-literal for same-tag duals. -type TagKey = [LiteralValue] extends [never] ? ( - string extends T ? string - : [T] extends [PropertyKey] ? T - // object brand (e.g. tag<"packing">): treat as single opaque tag token - : T - ) - : LiteralValue -// One-directional: domain tag may be a single literal that is a member of a -// multi-tag projection (`"picking" extends "picking"|"picked"`). Bidirectional -// equality would reject every multi-tag flat projection. -type ExtractTagged = From extends { readonly _tag: infer FromTag } - ? [TagKey] extends [TagKey] ? From : never - : never -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never -/** - * True when `Tag` is a single tag token (string literal or opaque brand like - * Schema.tag<"packing">), not a union of tags and not bare `string`. - */ -type IsSingleLiteralTag = string extends Tag ? false - : [Tag] extends [UnionToIntersection] ? true - : false -/** - * Domain shape that may supply stored fields for projection member `I`. - * - * - True tagged unions (`A | B` with different fields per `_tag`) resolve to the - * matching member so state-owned keys are not treated as universal. - * - Flat models that only carry `_tag: "a" | "b"` on a shared shape (no - * per-tag members in the Encoded union) fall back to the full `From` so - * existing Class+Literals projections keep typechecking. - */ -type ProjectableSource = I extends { readonly _tag: infer Tag } ? ( - [ExtractTagged] extends [never] ? From : ExtractTagged - ) - : From - -/** - * One projection member is projectable when every key is either: - * - in `ExtraKeys` (computed by the query / not stored on the domain row), or - * - a key of the matching domain source member (key presence only; nested - * field types may be narrowed by the projection). - * - * Uses `keyof Source` (not `KeysOfUnion` of the whole domain union) so a field - * owned only by some tags cannot be required on every branch — except for a - * single literal tag that maps to several domain variants (same `_tag`, - * different payloads), where `KeysOfUnion` allows each variant's keys. - */ -/** - * Keys the domain may supply for projection member `I`. - * - Single-literal tagged `I` (`_tag: "packing"`): keys of *any* same-tag - * domain variant (`KeysOfUnion`) — dual packing/closed shapes stay projectable. - * - Multi-tag / string-tagged `I` (`_tag: "a"|"b"` or `string`): keys of the - * *intersection* of matched domain members (`keyof` of the source union) so - * a flat multi-tag DTO cannot claim state-only fields (e.g. `batchId` on - * `initial`). - * - Untagged `I` (plain project DTOs): keys present on *any* domain member - * (`KeysOfUnion`), matching historical `project()` behavior. - */ -type ProjectableDomainKeys = I extends { readonly _tag: infer Tag } ? ( - IsSingleLiteralTag extends true ? KeysOfUnion> - : keyof ProjectableSource - ) - : KeysOfUnion - -/** - * Keys on projection member `I` that are neither computed (`ExtraKeys`) nor - * present on the matching domain source. Key presence only — nested field - * types may be narrowed by the projection, and optional domain/projection - * keys must not fail via `{ k?: T } extends { k: T }` (the old `-?` mapped - * assignability check rejected legitimate optionalKey fields like closed.batchId). - */ -type UnprojectableKeys = { - [K in keyof I]-?: K extends ExtraKeys ? never - : K extends ProjectableDomainKeys ? never - : K -}[keyof I] - -/** - * Distribute over tagged-union projection Encoded types. A non-distributive - * check against a union only sees `keyof (A|B)` (key intersection) and misses - * branch-only fields like cancel-only omissions. - */ -type IsProjectableMember = [I] extends [FieldValues] - ? ([UnprojectableKeys] extends [never] ? true : false) - : false - -/** - * `unknown` when every member of projection Encoded `I` is projectable from - * domain Encoded `From` (plus optional ExtraKeys for computed fields); `never` - * otherwise — use as an intersection constraint on a schema argument. - */ -type ProjectableGuard = false extends ( - I extends any ? IsProjectableMember : never -) ? never - : unknown - -/** - * Compile-time proof that a projection Encoded shape only requires stored keys - * that exist on the matching domain tagged state (or non-tagged source), plus - * any `ExtraKeys` filled by `projectComputed` (counts, flags, collects, …). - * - * Catches the class of Overview.List SchemaError where a cancel/recovery state - * omits a workflow lock field (`activeRequest`) in the domain model but the - * projection still requires it on every branch. - * - * @example - * ```ts - * type _ok = ProjectableFromDomain< - * { readonly _tag: "cancelled"; readonly id: string }, - * DomainEnc - * > // unknown - * - * type _bad = ProjectableFromDomain< - * { readonly _tag: "cancelled"; readonly id: string; readonly activeRequest: null }, - * DomainEnc - * > // never — activeRequest is not on domain cancelled - * ``` - */ -export type ProjectableFromDomain< - ProjectionEncoded, - DomainEncoded, - ExtraKeys extends PropertyKey = never -> = ProjectableGuard - -export type RelationDirection = "some" | "every" -export type Relation = { relation: RelationDirection } -export type Query = QueryTogether -export type QueryWhere< - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - Exclusive extends boolean = false -> = - & QueryTogether< - TFieldValues, - TFieldValuesRefined, - Exclusive, - "where" - > - & Relation - -export type QueryEnd< - TFieldValues extends FieldValues, - TType extends "many" | "one" | "count" = "many", - Exclusive extends boolean = false -> = QueryTogether< - TFieldValues, - TFieldValues, - Exclusive, - "end", - TFieldValues, - never, - TType -> - -export type QueryProjection< - TFieldValues extends FieldValues, - A = TFieldValues, - R = never, - TType extends "many" | "one" | "count" = "many", - Exclusive extends boolean = false -> = QueryTogether< - TFieldValues, - TFieldValues, - Exclusive, - "projection", - A, - R, - TType -> - -export type ComputedProjectionOperation = (q: Query) => QueryWhere - -export type ComputedProjectionMathExpression = - | { - readonly _tag: "field" - readonly field: string - } - | { - readonly _tag: "mul" - readonly left: ComputedProjectionMathExpression - readonly right: ComputedProjectionMathExpression - } - -declare const ComputedProjectionExpressionTypeId: unique symbol - -type ComputedProjectionOutput = { - readonly [ComputedProjectionExpressionTypeId]?: Covariant -} - -export type ComputedProjectionExpression = - & ComputedProjectionOutput - & ( - | { - readonly _tag: "relation-count" - readonly path: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-any" - readonly path: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-every" - readonly path: string - readonly operation: ComputedProjectionOperation - } - | { - readonly _tag: "relation-distinct-count" - readonly path: string - readonly field: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum" - readonly path: string - readonly field: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum-expr" - readonly path: string - readonly expression: ComputedProjectionMathExpression - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum-expr-by" - readonly path: string - readonly expression: ComputedProjectionMathExpression - readonly unit: string - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-sum-expr-normalized" - readonly path: string - readonly expression: ComputedProjectionMathExpression - readonly unit: string - readonly toBase: string - readonly factors: Readonly> - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-collect" - readonly path: string - readonly field: string - readonly distinct: boolean - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-collect-fields" - readonly path: string - readonly fields: readonly string[] - readonly distinct: boolean - readonly operation?: ComputedProjectionOperation - } - | { - readonly _tag: "relation-length" - readonly path: string - } - ) - -type NoExtraComputedKeys = string extends keyof M ? unknown - : Exclude> extends never ? unknown - : never - -/** - * An expression that aggregates values across documents (for use with {@link aggregate}). - * `agg-field` references a document field to group by; other tags are aggregate functions. - */ -export type AggregateExpression = - | { - readonly _tag: "agg-field" - readonly path: string - } - | { - readonly _tag: "agg-count" - } - | { - readonly _tag: "agg-count-when" - readonly operation: ComputedProjectionOperation - } - | { - readonly _tag: "agg-sum" - readonly field: string - } - | { - readonly _tag: "agg-min" - readonly field: string - } - | { - readonly _tag: "agg-max" - readonly field: string - } - -export type AggregateMap = Readonly> - -export type ComputedProjectionMap = Readonly> -export type Q = - | Initial - | Where - | And - | Or - | Order - | Page - | Project - | One - | Count - -export class Initial extends Data.TaggedClass("value")<{ value: "initial" }> - implements Query -{ - readonly [QId]!: any - constructor() { - super({ value: "initial" as const }) - } - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Where extends Data.TaggedClass("where")<{ - current: Query - operation: [string, Ops, any] | [string, any] | ((q: Query) => QueryWhere) - relation: RelationDirection - subPath?: string -}> implements QueryWhere { - readonly [QId]!: any - - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class And extends Data.TaggedClass("and")<{ - current: Query - operation: [string, Ops, any] | [string, any] | ((q: Query) => QueryWhere) - relation: RelationDirection -}> implements QueryWhere { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Or extends Data.TaggedClass("or")<{ - current: Query - operation: [string, Ops, any] | [string, any] | ((q: Query) => QueryWhere) - relation: RelationDirection -}> implements QueryWhere { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Page extends Data.TaggedClass("page")<{ - current: Query | QueryWhere | QueryEnd - take?: number | undefined - skip?: number | undefined -}> implements QueryEnd { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class One extends Data.TaggedClass("one")<{ - current: Query | QueryWhere | QueryEnd -}> implements QueryEnd { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Count extends Data.TaggedClass("count")<{ - current: Query | QueryWhere | QueryEnd -}> implements QueryEnd { - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Order> - extends Data.TaggedClass("order")<{ - current: Query | QueryWhere | QueryEnd - field: TFieldName - direction: "ASC" | "DESC" - }> - implements QueryEnd -{ - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export class Project - extends Data.TaggedClass("project")<{ - current: Query | QueryWhere | QueryEnd - schema: S.Codec - mode: "collect" | "project" | "transform" | "aggregate" - computed?: ComputedProjectionMap - aggregateMap?: AggregateMap - }> - implements QueryProjection -{ - readonly [QId]!: any - override pipe() { - // eslint-disable-next-line prefer-rest-params - return Pipeable.pipeArguments(this, arguments) - } -} - -export const make: () => Query = () => new Initial() - -export const where: FilterWhere = (...operation: any[]) => (current: any) => - new Where({ current, operation: typeof operation[0] === "function" ? flow(...operation as [any]) : operation } as any) - -export const and: FilterContinuationAnd = (...operation: any[]) => (current: any) => - new And({ current, operation: typeof operation[0] === "function" ? flow(...operation as [any]) : operation } as any) - -export const or: FilterContinuationOr = (...operation: any[]) => (current: any) => - new Or({ current, operation: typeof operation[0] === "function" ? flow(...operation as [any]) : operation } as any) - -export const whereEvery: WhereEveryOrSome = - ((subPath: string, ...[first, ...rest]: any[]) => (current: any) => - new Where( - { - current, - operation: typeof first === "function" - ? flow(...[first, ...rest] as [any]) - : [`${subPath}.-1.${first}`, ...rest], - relation: "every", - subPath - } as any - )) as unknown as WhereEveryOrSome -export const whereSome: WhereEveryOrSome = - ((subPath: string, ...[first, ...rest]: any[]) => (current: any) => - new Where( - { - current, - operation: typeof first === "function" - ? flow(...[first, ...rest] as [any]) - : [`${subPath}.-1.${first}`, ...rest], - relation: "some", - subPath - } as any - )) as unknown as WhereEveryOrSome - -export const order: { - < - Q extends Query | QueryWhere | QueryEnd - >( - field: FieldPath>, - direction?: "ASC" | "DESC" - ): ( - current: Q - ) => QueryEnd, "many", ExtractExclusiveness> -} = (field, direction = "ASC") => (current) => new Order({ current, field: field as any, direction }) - -export const page: { - ( - page: { skip?: number; take?: number } - ): | QueryWhere | QueryEnd>( - current: Q - ) => QueryEnd, "many", ExtractExclusiveness> -} = ({ skip, take }) => (current) => - new Page({ - current, - take, - skip - }) - -export const one: { - | QueryWhere | QueryEnd>( - current: Q - ): QueryEnd, "one", ExtractExclusiveness> -} = (current) => - new One({ - current - }) - -// it's better to implement a distinct count so that the implementation can be optimised per adapter -export const count: { - < - Q extends Query | QueryWhere | QueryEnd - >( - current: Q - ): QueryProjection, NonNegativeInt, never, "count", ExtractExclusiveness> -} = (current) => new Count({ current }) - -/** - * Attach a projection schema to a query. - * - * The `select` clause sent to the store is derived from the schema's encoded - * AST property names (top-level + per-array sub-keys), so a projection always - * narrows what is read from the store. The repository augments that select - * with `id` and `_etag` for change tracking. See {@link toFilter} and the - * dispatch in `Repository/internal/internal.ts`. - * - * Modes — pick based on shape of the decoded value and on whether the - * persistence-model (PM) reverse-mapping is needed: - * - * - `"transform"` (default when `mode` omitted): goes through the repo's - * `parseMany` pipeline. The raw row is reverse-mapped via the - * etag/PM cache (re-injecting `_etag` and any PM-shape state) before - * decoding. Decode failures `orDie` (error channel = `never`). Use when - * the schema operates on the full PM shape (e.g. full-entity reads that - * must preserve etag tracking). - * - * - `"project"`: decodes the raw encoded row directly with the supplied - * schema. No PM reverse-mapping, no etag cache merge. Decode failures - * surface as `S.SchemaError`. Use for slim DTOs / aggregations that do not - * need etag tracking and whose schema input is a plain subset of `Encoded`. - * - * - `"collect"`: like `"project"`, but the schema yields `Option` and - * `None` values are dropped post-decode (`Array.getSomes`). Use to filter - * rows during decode (e.g. discriminated-union narrowing where some rows - * should not appear in the result). - */ -export const project: { - < - Q extends Query | QueryWhere | QueryEnd, - I extends ExtractFieldValuesRefined, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, I, R>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - I extends ExtractFieldValuesRefined, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, - mode: "project" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - < - Q extends Query | QueryWhere | QueryEnd, - I extends ExtractFieldValuesRefined, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - I extends FieldValues, - A, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: - & S.Codec, I, R> - & ProjectableGuard>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - I extends FieldValues, - A, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: - & S.Codec - & ProjectableGuard>, - mode: "project" - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - < - Q extends Query | QueryWhere | QueryEnd, - I extends FieldValues, - A, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: - & S.Codec - & ProjectableGuard> - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> -} = (schema: any, mode = "transform") => (current: any) => new Project({ current, schema, mode } as any) - -/** - * Element type of an array-valued field path on `TFieldValues`. Falls back to - * `FieldValues` when the path does not resolve to an array of structs so that - * `FieldPath<...>` stays defined (it just degrades to `string`). - * - * Uses `Extract` so that when `P` defaults to the full `FieldPath` - * union, only the array-valued branches contribute to the element type. - */ -export type RelationElement< - TFieldValues extends FieldValues, - P extends FieldPath -> = Extract, ReadonlyArray> extends ReadonlyArray - ? (E extends FieldValues ? E : FieldValues) - : FieldValues - -export const relation = < - TFieldValues extends FieldValues, - const P extends FieldPath = FieldPath ->( - path: P -) => ({ - /** - * Typed math-expression builder bound to the relation's element scope: - * `relation("items").expr.field("weight")` constrains the field path to - * `FieldPath>`. - */ - expr: { - field: (field: FieldPath>): ComputedProjectionMathExpression => ({ - _tag: "field", - field - }), - mul: ( - left: ComputedProjectionMathExpression, - right: ComputedProjectionMathExpression - ): ComputedProjectionMathExpression => ({ _tag: "mul", left, right }) - }, - length: (): ComputedProjectionExpression => ({ - _tag: "relation-length", - path - }), - count: (operation?: ComputedProjectionOperation): ComputedProjectionExpression => - operation - ? { - _tag: "relation-count", - path, - operation - } - : { - _tag: "relation-count", - path - }, - any: (operation?: ComputedProjectionOperation): ComputedProjectionExpression => - operation - ? { - _tag: "relation-any", - path, - operation - } - : { - _tag: "relation-any", - path - }, - every: (operation: ComputedProjectionOperation): ComputedProjectionExpression => ({ - _tag: "relation-every", - path, - operation - }), - distinctCount: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-distinct-count", - path, - field, - operation - } - : { - _tag: "relation-distinct-count", - path, - field - }, - sum: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-sum", - path, - field, - operation - } - : { - _tag: "relation-sum", - path, - field - }, - sumExpr: ( - expression: ComputedProjectionMathExpression, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-sum-expr", - path, - expression, - operation - } - : { - _tag: "relation-sum-expr", - path, - expression - }, - sumExprBy: >>( - expression: ComputedProjectionMathExpression, - options: { unit: F }, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression< - ReadonlyArray<{ readonly unit: FieldPathValue, F>; readonly total: number }> - > => - operation - ? { - _tag: "relation-sum-expr-by", - path, - expression, - unit: options.unit, - operation - } - : { - _tag: "relation-sum-expr-by", - path, - expression, - unit: options.unit - }, - sumExprNormalized: ( - expression: ComputedProjectionMathExpression, - options: { - unit: FieldPath> - toBase: string - factors: Readonly> - }, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression => - operation - ? { - _tag: "relation-sum-expr-normalized", - path, - expression, - unit: options.unit, - toBase: options.toBase, - factors: options.factors, - operation - } - : { - _tag: "relation-sum-expr-normalized", - path, - expression, - unit: options.unit, - toBase: options.toBase, - factors: options.factors - }, - collect: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression, F>>> => - operation - ? { - _tag: "relation-collect", - path, - field, - distinct: false, - operation - } - : { - _tag: "relation-collect", - path, - field, - distinct: false - }, - collectDistinct: >>( - field: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression, F>>> => - operation - ? { - _tag: "relation-collect", - path, - field, - distinct: true, - operation - } - : { - _tag: "relation-collect", - path, - field, - distinct: true - }, - collectFields: >[]>( - fields: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression< - ReadonlyArray, F[number]>> - > => - operation - ? { - _tag: "relation-collect-fields", - path, - fields, - distinct: false, - operation - } - : { - _tag: "relation-collect-fields", - path, - fields, - distinct: false - }, - collectDistinctFields: >[]>( - fields: F, - operation?: ComputedProjectionOperation - ): ComputedProjectionExpression< - ReadonlyArray, F[number]>> - > => - operation - ? { - _tag: "relation-collect-fields", - path, - fields, - distinct: true, - operation - } - : { - _tag: "relation-collect-fields", - path, - fields, - distinct: true - } -}) - -/** - * Untyped math-expression builder. Field paths are not statically validated — - * prefer the scope-bound `relation(path).expr` builder when the element type - * is known, since it constrains the field argument to `FieldPath`. - * - * The generic parameter is accepted for symmetry with the scoped builder so - * callers may opt into a tighter check via `expr.field("x")`. - */ -export const expr = { - field: ( - field: FieldPath - ): ComputedProjectionMathExpression => ({ _tag: "field", field }), - mul: ( - left: ComputedProjectionMathExpression, - right: ComputedProjectionMathExpression - ): ComputedProjectionMathExpression => ({ _tag: "mul", left, right }) -} as const - -export const computed = (value: T): T => value - -/** - * Helpers passed to the inline-builder form of {@link projectComputed}. The - * `relation` factory is bound to the source row shape so paths/fields inside - * the projection are validated against the document shape inferred from the - * pipe. - */ -export interface ComputedHelpers { - relation: >(path: P) => ReturnType> -} - -const makeComputedHelpers = (): ComputedHelpers => ({ - relation: (path) => relation(path) -}) - -/** - * Only treat computed-map keys as ExtraKeys when `M` is a concrete object type. - * `ComputedProjectionMap` is `Record`, so `keyof M` is `string` and - * would otherwise allow every projection field as "computed". - */ -type ConcreteComputedKeys = string extends keyof M ? never : Extract - -/** - * Proof that projection Encoded `I` is projectable from domain Encoded, allowing - * concrete computed keys. Intersected onto the computed-map argument so - * inference of `M` is complete before the check runs. - */ -type ProjectableComputedMap< - M extends ComputedProjectionMap, - I extends FieldValues, - Domain -> = - & M - & NoExtraComputedKeys - & ( - [ProjectableGuard>] extends [never] ? { - readonly __projectableFromDomain: - "projection fields must exist on the matching domain tagged state or be computed keys" - } - : unknown - ) - -export const projectComputed: { - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - build: (helpers: ComputedHelpers>) => ProjectableComputedMap>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection< - ExtractFieldValuesRefined, - S.Schema.Type extends Option.Option ? A : never, - never, - ExtractTType, - E - > - - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - build: (helpers: ComputedHelpers>) => ProjectableComputedMap>, - mode?: "project" - ): ( - current: Q - ) => QueryProjection, S.Schema.Type, never, ExtractTType, E> - - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - computedProjection: ProjectableComputedMap>, - mode: "collect" - ): ( - current: Q - ) => QueryProjection< - ExtractFieldValuesRefined, - S.Schema.Type extends Option.Option ? A : never, - never, - ExtractTType, - E - > - - < - Q extends Query | QueryWhere | QueryEnd, - Schema extends S.Codec, - M extends ComputedProjectionMap, - I extends FieldValues = S.Codec.Encoded, - E extends boolean = ExtractExclusiveness - >( - schema: Schema, - computedProjection: ProjectableComputedMap>, - mode?: "project" - ): ( - current: Q - ) => QueryProjection, S.Schema.Type, never, ExtractTType, E> -} = ( - schema: any, - mapOrBuild: - | ComputedProjectionMap - | ((helpers: ComputedHelpers) => ComputedProjectionMap), - mode = "project" -) => -(current: any) => { - const computedProjection = typeof mapOrBuild === "function" - ? mapOrBuild(makeComputedHelpers()) - : mapOrBuild - return new Project({ current, schema, mode, computed: computedProjection }) -} - -/** - * Builder shape returned by {@link agg}. Field-taking methods are constrained - * to `FieldPath` so paths are validated against the document - * shape. - */ -export interface AggBuilder { - field: (path: FieldPath) => AggregateExpression - count: () => AggregateExpression - countWhen: (operation: ComputedProjectionOperation) => AggregateExpression - sum: (field: FieldPath) => AggregateExpression - min: (field: FieldPath) => AggregateExpression - max: (field: FieldPath) => AggregateExpression -} - -const makeAggBuilder = (): AggBuilder => ({ - field: (path) => ({ _tag: "agg-field", path }), - count: () => ({ _tag: "agg-count" }), - countWhen: (operation) => ({ _tag: "agg-count-when", operation }), - sum: (field) => ({ _tag: "agg-sum", field }), - min: (field) => ({ _tag: "agg-min", field }), - max: (field) => ({ _tag: "agg-max", field }) -}) - -/** - * Scope-bound aggregate-expression builder factory. Invoke with the source - * document field-value shape to get a builder whose `field`/`sum`/`min`/`max` - * arguments are constrained to `FieldPath`. - * - * Prefer the inline callback form of {@link aggregate} (`aggregate(schema, - * ($) => ({...}))`) — there the source shape is inferred from the pipe so no - * explicit type argument is needed. This factory is the escape hatch when - * the builder is constructed outside the pipe. - */ -export const agg = (): AggBuilder => - makeAggBuilder() - -/** - * Attach an aggregate projection to a query, performing GROUP BY + aggregate functions at the - * database level instead of fetching all rows and grouping in memory. - * - * Pass a builder callback to get a typed `agg` bound to the source row shape - * (inferred from the pipe — no explicit generic needed): - * - * @example - * ```ts - * repo.query( - * where("status", "active"), - * aggregate( - * S.Struct({ city: S.String, count: S.Number }), - * ($) => ({ - * city: $.field("address.city"), - * count: $.countWhen((q) => q.pipe(where("active", true))) - * }) - * ) - * ) - * ``` - * - * A plain {@link AggregateMap} is also accepted for the rare case where the - * map is built outside the pipe (loses path inference). - * - * The output is decoded directly with `schema` (no PM reverse-mapping, no etag tracking). - * Decode failures surface as `S.SchemaError`. - */ -export const aggregate: { - < - Q extends Query | QueryWhere | QueryEnd, - I extends Record, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, - build: (agg: AggBuilder>) => AggregateMap - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> - < - Q extends Query | QueryWhere | QueryEnd, - I extends Record, - A = ExtractFieldValuesRefined, - R = never, - E extends boolean = ExtractExclusiveness - >( - schema: S.Codec, - aggregateMap: AggregateMap - ): ( - current: Q - ) => QueryProjection, A, R, ExtractTType, E> -} = (schema: any, mapOrBuild: AggregateMap | ((agg: AggBuilder) => AggregateMap)) => (current: any) => { - const aggregateMap = typeof mapOrBuild === "function" - ? mapOrBuild(makeAggBuilder()) - : mapOrBuild - return new Project({ current, schema, mode: "aggregate", aggregateMap } as any) -} - -type GetArV = T extends ReadonlySet ? R - : T extends readonly (infer R)[] ? R - : never - -type GetMapK = T extends ReadonlyMap ? K : never -type GetMapV = T extends ReadonlyMap ? V : never -type GetMapEntry = T extends ReadonlyMap ? readonly [K, V] : never - -type InValues = readonly T[] | ReadonlySet - -export type FilterContinuations = { - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "gt" | "gte" | "lt" | "lte" | "neq", - value: V // only numbers? - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith", - value: FieldPathValue extends string ? string : never - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "in" | "notIn", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: - | "includes" - | "notIncludes", - value: GetArV - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: - | "includes-any" - | "notIncludes-any" - | "includes-all" - | "notIncludes-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKey" | "notHasKey", - value: GetMapK - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasValue" | "notHasValue", - value: GetMapV - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKeyValue" | "notHasKeyValue", - value: GetMapEntry - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKey-any" | "notHasKey-any" | "hasKey-all" | "notHasKey-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasValue-any" | "notHasValue-any" | "hasValue-all" | "notHasValue-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "hasKeyValue-any" | "notHasKeyValue-any" | "hasKeyValue-all" | "notHasKeyValue-all", - value: InValues> - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >(f: { - path: TFieldName - op: "eq" - value: V - }): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => IsCurrentInitial extends true ? QueryWhere - : QueryWhere -} - -export type FilterContinuationsWithSubpath = { - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - value: V - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "gt" | "gte" | "lt" | "lte" | "neq", - value: V // only numbers? - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith", - value: FieldPathValue extends string ? string : never - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "in" | "notIn", - value: InValues - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: - | "includes" - | "notIncludes", - value: GetArV - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: - | "includes-any" - | "notIncludes-any" - | "includes-all" - | "notIncludes-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKey" | "notHasKey", - value: GetMapK - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasValue" | "notHasValue", - value: GetMapV - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKeyValue" | "notHasKeyValue", - value: GetMapEntry - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKey-any" | "notHasKey-any" | "hasKey-all" | "notHasKey-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasValue-any" | "notHasValue-any" | "hasValue-all" | "notHasValue-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >( - subPath: TFieldName, - restPath: TFieldNameSub, - op: "hasKeyValue-any" | "notHasKeyValue-any" | "hasKeyValue-all" | "notHasKeyValue-all", - value: InValues> - ): ( - current: Query - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - TFieldValuesSub extends TFieldValues[TFieldName][number], - TFieldNameSub extends FieldPath, - V extends FieldPathValue - >(f: { - subPath: TFieldName - restPath: TFieldNameSub - op: "eq" - value: V - }): ( - current: Query - ) => QueryWhere -} - -/* dprint-ignore-start */ -export type RefineWithLiteral< - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - V, - Exclde extends boolean = false -> = - // refine only if the value is a primitive - [V] extends [string | number | boolean | null | bigint] - ? RefineFieldPathValue - : TFieldValues -/* dprint-ignore-end */ - -export type FilteringRefinements = { - < - TFieldValues extends FieldValues, - const TFieldName extends FieldPath, - const V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => QueryWhere< - TFieldValues, - // @ts-expect-error it's TS - RefineWithLiteral, - TFieldName extends "_tag" ? true : false // consider only _tag as an exclusive field and only in the positive case - > - < - TFieldValues extends FieldValues, - const TFieldName extends FieldPath, - const V extends FieldPathValue, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "neq", - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => QueryWhere< - TFieldValues, - // @ts-expect-error it's TS - RefineWithLiteral, - E - > - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath, - const V extends readonly FieldPathValue[], - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false - >( - path: TFieldName, - op: "in", - value: V - ): ( - current: IsCurrentInitial extends true ? Query - : QueryWhere - ) => QueryWhere< - TFieldValues, - // @ts-expect-error it's TS - RefineWithLiteral>, - E - > -} - -export type NestedQueriesFixedRefinement = { - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false - >( - fb: (current: Query) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false, - E14 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: (query: QueryWhere) => QueryWhere, - fd: (query: QueryWhere) => QueryWhere, - fe: (query: QueryWhere) => QueryWhere, - ff: (query: QueryWhere) => QueryWhere, - fg: (query: QueryWhere) => QueryWhere, - fh: (query: QueryWhere) => QueryWhere, - fi: (query: QueryWhere) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere, - fn: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: IsCurrentInitial extends true ? Query : QueryWhere - ) => QueryWhere -} - -export type NestedQueriesFreeIntersectionRefinement = { - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false - >( - fb: (current: Query) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - TFieldValuesRefined14 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false, - E14 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere, - fn: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere -} - -// to be safe, or forces the output to be exclusive because you can always or _tag filters -export type NestedQueriesFreeDisjointRefinement = { - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false - >( - fb: (current: Query) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere - - < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - TFieldValuesRefined2 extends TFieldValues = TFieldValues, - TFieldValuesRefined3 extends TFieldValues = TFieldValues, - TFieldValuesRefined4 extends TFieldValues = TFieldValues, - TFieldValuesRefined5 extends TFieldValues = TFieldValues, - TFieldValuesRefined6 extends TFieldValues = TFieldValues, - TFieldValuesRefined7 extends TFieldValues = TFieldValues, - TFieldValuesRefined8 extends TFieldValues = TFieldValues, - TFieldValuesRefined9 extends TFieldValues = TFieldValues, - TFieldValuesRefined10 extends TFieldValues = TFieldValues, - TFieldValuesRefined11 extends TFieldValues = TFieldValues, - TFieldValuesRefined12 extends TFieldValues = TFieldValues, - TFieldValuesRefined13 extends TFieldValues = TFieldValues, - TFieldValuesRefined14 extends TFieldValues = TFieldValues, - E extends boolean = false, - E2 extends boolean = false, - E3 extends boolean = false, - E4 extends boolean = false, - E5 extends boolean = false, - E6 extends boolean = false, - E7 extends boolean = false, - E8 extends boolean = false, - E9 extends boolean = false, - E10 extends boolean = false, - E11 extends boolean = false, - E12 extends boolean = false, - E13 extends boolean = false, - E14 extends boolean = false - >( - fb: (current: Query) => QueryWhere, - fc: ( - query: QueryWhere - ) => QueryWhere, - fd: ( - query: QueryWhere - ) => QueryWhere, - fe: ( - query: QueryWhere - ) => QueryWhere, - ff: ( - query: QueryWhere - ) => QueryWhere, - fg: ( - query: QueryWhere - ) => QueryWhere, - fh: ( - query: QueryWhere - ) => QueryWhere, - fi: ( - query: QueryWhere - ) => QueryWhere, - fj: ( - query: QueryWhere - ) => QueryWhere, - fk: ( - query: QueryWhere - ) => QueryWhere, - fl: ( - query: QueryWhere - ) => QueryWhere, - fm: ( - query: QueryWhere - ) => QueryWhere, - fn: ( - query: QueryWhere - ) => QueryWhere - ): ( - current: QueryWhere - ) => QueryWhere -} - -export type FilterWhere = - & NestedQueriesFixedRefinement - & FilteringRefinements - & FilterContinuations - -export type FilterContinuationAnd = - & NestedQueriesFreeIntersectionRefinement - & FilteringRefinements - & FilterContinuations - -export type FilterContinuationOr = - & NestedQueriesFreeDisjointRefinement - & FilterContinuations - -// it does not support refinements by choice (for now) -export type WhereEveryOrSome = - & { - < - TFieldValues extends FieldValues, - TFieldName extends FieldPath - >( - subPath: TFieldName, - dude: ( - current: Query - ) => QueryWhere, - ...dudes: (( - current: QueryWhere - ) => QueryWhere)[] - ): ( - current: Query - ) => QueryWhere - } - & FilterContinuationsWithSubpath diff --git a/packages/effect-app/src/Model/query/new-kid-interpreter.ts b/packages/effect-app/src/Model/query/new-kid-interpreter.ts deleted file mode 100644 index a0c38e24b..000000000 --- a/packages/effect-app/src/Model/query/new-kid-interpreter.ts +++ /dev/null @@ -1,531 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { identity } from "effect/Function" -import * as Array from "../../Array.ts" -import * as Option from "../../Option.ts" -import * as S from "../../Schema.ts" -import * as SchemaAST from "../../SchemaAST.ts" -import { dropUndefinedT } from "../../utils.ts" -import type { FilterResult } from "../filter/filterApi.ts" -import type { FieldValues } from "../filter/types.ts" -import type { FieldPath } from "../filter/types/path/eager.ts" -import { make, type Q, type QAll } from "../query/dsl.ts" - -export type AggregateIrExpression = - | { readonly _tag: "agg-count" } - | { readonly _tag: "agg-count-when"; readonly filter: readonly FilterResult[] } - | { readonly _tag: "agg-sum"; readonly field: string } - | { readonly _tag: "agg-min"; readonly field: string } - | { readonly _tag: "agg-max"; readonly field: string } - -export type AggregateIrItem = - | AggregateIrExpression - | { readonly _tag: "agg-field"; readonly path: string } - -export type ComputedProjectionMathIrExpression = - | { - readonly _tag: "field" - readonly field: string - } - | { - readonly _tag: "mul" - readonly left: ComputedProjectionMathIrExpression - readonly right: ComputedProjectionMathIrExpression - } - -export type ComputedProjectionIrExpression = - | { - readonly _tag: "relation-count" - readonly path: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-any" - readonly path: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-every" - readonly path: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-distinct-count" - readonly path: string - readonly field: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum" - readonly path: string - readonly field: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum-expr" - readonly path: string - readonly expression: ComputedProjectionMathIrExpression - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum-expr-by" - readonly path: string - readonly expression: ComputedProjectionMathIrExpression - readonly unit: string - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-sum-expr-normalized" - readonly path: string - readonly expression: ComputedProjectionMathIrExpression - readonly unit: string - readonly toBase: string - readonly factors: Readonly> - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-collect" - readonly path: string - readonly field: string - readonly distinct: boolean - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-collect-fields" - readonly path: string - readonly fields: readonly string[] - readonly distinct: boolean - readonly filter: readonly FilterResult[] - } - | { - readonly _tag: "relation-length" - readonly path: string - } - -type SelectItem = - | keyof TFieldValues - | { key: string; subKeys: string[] } - | { - key: string - computed: ComputedProjectionIrExpression - } - -const isArraySelect = ( - item: SelectItem -): item is { key: string; subKeys: string[] } => typeof item === "object" && item !== null && "subKeys" in item - -const mergeArraySelect = ( - select: SelectItem[], - arraySelect: { key: string; subKeys: string[] } -) => { - const subKeys = [...new Set(arraySelect.subKeys)] - const existing = select.find( - (item): item is { key: string; subKeys: string[] } => isArraySelect(item) && item.key === arraySelect.key - ) - - if (existing) { - const existingIndex = select.indexOf(existing) - select[existingIndex] = { - key: arraySelect.key, - subKeys: [...new Set([...existing.subKeys, ...subKeys])] - } - } else { - select.push({ key: arraySelect.key, subKeys }) - } - - const scalarIndex = select.findIndex((item) => item === arraySelect.key) - if (scalarIndex >= 0) { - select.splice(scalarIndex, 1) - } -} - -type Result = { - filter: FilterResult[] - schema: S.Codec | undefined - limit: number | undefined - skip: number | undefined - order: { key: FieldPath; direction: "ASC" | "DESC" }[] - ttype: "one" | "many" | "count" | undefined - mode: "collect" | "project" | "transform" | "aggregate" | undefined - computed: Record | undefined - aggregateMap: Record | undefined -} - -const interpret = < - TFieldValues extends FieldValues, - TFieldValuesRefined extends TFieldValues = TFieldValues, - A = TFieldValues, - R = never ->(_: QAll) => { - const a = _ as Q - - const data: Result = { - filter: [], - schema: undefined, - limit: undefined, - skip: undefined, - order: [], - ttype: undefined, - mode: undefined, - computed: undefined, - aggregateMap: undefined - } - - const upd = ( - v: Result - ) => { - data.filter.push(...v.filter) - data.order.push(...v.order) - if (v.limit !== undefined) data.limit = v.limit - if (v.skip !== undefined) data.skip = v.skip - if (v.ttype !== undefined) data.ttype = v.ttype - if (v.schema !== undefined) data.schema = v.schema - if (v.mode !== undefined) data.mode = v.mode - if (v.computed !== undefined) data.computed = v.computed - if (v.aggregateMap !== undefined) data.aggregateMap = v.aggregateMap - } - - const applyPath = (path: string) => (_: FilterResult): FilterResult => - _.t === "where" || _.t === "and" || _.t === "or" - ? { ..._, path: `${path}.-1.${_.path}` } - : { ..._, result: _.result.map(applyPath(path)) } - - switch (a._tag) { - case "value": - break - case "where": { - const { current, operation, relation, subPath } = a - upd(interpret(current)) - if (typeof operation === "function") { - data.filter.push( - { - t: "where-scope", - result: interpret(operation(make())).filter.map(subPath ? applyPath(subPath) : identity), - relation - } - ) - } else { - data.filter.push( - { - t: "where", - path: operation[0], - op: operation.length === 2 ? "eq" : operation[1], - value: operation.length === 2 ? operation[1] : operation[2] - } - ) - } - break - } - case "and": { - const { current, operation, relation } = a - upd(interpret(current)) - if (typeof operation === "function") { - data.filter.push( - { t: "and-scope", result: interpret(operation(make())).filter, relation } - ) - } else { - data.filter.push( - { - t: "and", - path: operation[0], - op: operation.length === 2 ? "eq" : operation[1], - value: operation.length === 2 ? operation[1] : operation[2] - } - ) - } - break - } - case "or": { - const { current, operation, relation } = a - upd(interpret(current)) - if (typeof operation === "function") { - data.filter.push( - { t: "or-scope", result: interpret(operation(make())).filter, relation } - ) - } else { - data.filter.push( - { - t: "or", - path: operation[0], - op: operation.length === 2 ? "eq" : operation[1], - value: operation.length === 2 ? operation[1] : operation[2] - } - ) - } - break - } - case "one": { - upd(interpret(a.current)) - data.limit = 1 - data.ttype = "one" - break - } - case "count": { - upd(interpret(a.current)) - data.ttype = "count" - data.schema = S.Struct({ id: S.String }) as any - break - } - case "order": { - upd(interpret(a.current)) - data.order.push({ key: a.field, direction: a.direction }) - break - } - case "page": { - upd(interpret(a.current)) - data.limit = a.take - data.skip = a.skip - break - } - case "project": { - upd(interpret(a.current)) - if (a.mode === "aggregate" && a.aggregateMap) { - data.schema = a.schema - data.mode = "aggregate" - data.aggregateMap = Object.fromEntries( - Object.entries(a.aggregateMap).map(([key, expression]) => { - switch (expression._tag) { - case "agg-field": - return [key, { _tag: "agg-field" as const, path: expression.path }] - case "agg-count": - return [key, { _tag: "agg-count" as const }] - case "agg-count-when": { - const filter = interpret(expression.operation(make())).filter - return [key, { _tag: "agg-count-when" as const, filter }] - } - case "agg-sum": - return [key, { _tag: "agg-sum" as const, field: expression.field }] - case "agg-min": - return [key, { _tag: "agg-min" as const, field: expression.field }] - case "agg-max": - return [key, { _tag: "agg-max" as const, field: expression.field }] - } - }) - ) - break - } - if (a.computed && a.mode === "transform") { - throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'") - } - data.schema = a.schema - data.mode = a.computed - ? a.mode === "collect" ? "collect" : "project" - : a.mode - data.computed = a.computed - ? Object.fromEntries( - Object.entries(a.computed).map(([key, expression]) => { - const e = expression - const op = "operation" in e ? e.operation : undefined - const filter = op ? interpret(op(make())).filter.map(applyPath(e.path)) : [] - switch (e._tag) { - case "relation-count": - case "relation-any": - case "relation-every": - return [key, { _tag: e._tag, path: e.path, filter }] - case "relation-distinct-count": - case "relation-sum": - return [ - key, - { _tag: e._tag, path: e.path, field: e.field, filter } - ] - case "relation-sum-expr": - return [ - key, - { _tag: e._tag, path: e.path, expression: e.expression, filter } - ] - case "relation-sum-expr-by": - return [ - key, - { - _tag: e._tag, - path: e.path, - expression: e.expression, - unit: e.unit, - filter - } - ] - case "relation-sum-expr-normalized": - return [ - key, - { - _tag: e._tag, - path: e.path, - expression: e.expression, - unit: e.unit, - toBase: e.toBase, - factors: e.factors, - filter - } - ] - case "relation-collect": - return [ - key, - { - _tag: e._tag, - path: e.path, - field: e.field, - distinct: e.distinct, - filter - } - ] - case "relation-collect-fields": - return [ - key, - { - _tag: e._tag, - path: e.path, - fields: e.fields, - distinct: e.distinct, - filter - } - ] - case "relation-length": - return [key, { _tag: e._tag, path: e.path }] - } - }) - ) - : undefined - break - } - } - - return data -} - -const walkTransformation = (t: S.AST.AST): S.AST.AST => { - if (S.AST.isDeclaration(t) && t.typeParameters.length > 0) { - return walkTransformation(t.typeParameters[0]!) - } - return t -} - -const objectAsts = (ast: S.AST.AST): readonly S.AST.Objects[] => { - const t = walkTransformation(ast) - if (S.AST.isObjects(t)) { - return [t] - } - if (S.AST.isUnion(t)) { - return t.types.flatMap(objectAsts) - } - return [] -} - -const objectKeys = (ast: S.AST.AST): readonly string[] => [ - ...new Set(objectAsts(ast).flatMap((object) => object.propertySignatures.map((_) => _.name as string))) -] - -export const toFilter = < - TFieldValues extends FieldValues, - A, - R, - TFieldValuesRefined extends TFieldValues = TFieldValues ->( - q: QAll, - baseSchema?: S.Schema -) => { - // TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format - const a = interpret(q) - - // Aggregate mode: build select entirely from aggregateMap (no schema-driven field list) - if (a.mode === "aggregate" && a.aggregateMap) { - const aggSelect = Object.entries(a.aggregateMap).map(([key, item]) => { - if (item._tag === "agg-field") { - return { key, path: item.path } - } - return { key, aggregate: item } - }) - return dropUndefinedT({ - t: null as unknown as TFieldValues, - limit: a.limit, - skip: a.skip, - select: Option.getOrUndefined(Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(aggSelect)) as any, - schema: a.schema, - computed: undefined, - order: Option.getOrUndefined(Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(a.order)), - ttype: a.ttype, - mode: "aggregate" as const, - filter: a.filter.length ? a.filter : undefined - }) - } - - const schema = a.schema - let select: SelectItem[] = [] - // TODO: support more complex (nested) schemas? - if (schema) { - const t = walkTransformation(SchemaAST.toEncoded(schema.ast)) - const objects = [...objectAsts(t)] - if (Array.isArrayNonEmpty(objects)) { - select = [...objectKeys(t)] - for (const prop of objects.flatMap((_) => _.propertySignatures)) { - if (S.AST.isArrays(prop.type)) { - // make sure we only select when there are actually type literals in the tuple... - // otherwise we might be dealing with strings etc. - // TODO; be more strict, can't support arrays with unions that have non TypeLiteral members etc.. - const arraySelect = { - key: prop.name as string, - subKeys: Array.flatMap( - prop.type.rest, - (x) => { - return objectKeys(x) - } - ) - } - if (arraySelect.subKeys.length > 0) { - mergeArraySelect(select, arraySelect) - } - } - } - } - } - const computed = a.computed - const getSelectKey = (_: (typeof select)[number]) => { - if (typeof _ === "string") { - return _ - } - if (typeof _ === "object" && _ !== null && "key" in _) { - return _.key - } - return String(_) - } - const schemaKeys = select.map(getSelectKey) - const nonEncodedSchemaKeys = (() => { - if (!baseSchema) { - return [] as string[] - } - const encoded = walkTransformation(SchemaAST.toEncoded(baseSchema.ast)) - const encodedKeys = objectKeys(encoded) - return schemaKeys.filter((key) => !encodedKeys.includes(key)) - })() - const missingComputedKeys = nonEncodedSchemaKeys.filter((key) => !(computed && key in computed)) - - if (Array.isArrayNonEmpty(missingComputedKeys)) { - throw new Error(`Missing computed projections for schema keys: ${missingComputedKeys.join(", ")}`) - } - - if (computed) { - const computedKeys = Object.keys(computed) - const extraComputedKeys = computedKeys.filter((key) => !schemaKeys.includes(key)) - if (Array.isArrayNonEmpty(extraComputedKeys)) { - throw new Error(`Computed projection keys must exist in projection schema: ${extraComputedKeys.join(", ")}`) - } - select = select.filter((_) => { - const key = getSelectKey(_) - return !(key in computed) - }) - select.push(...Object.entries(computed).map(([key, expression]) => ({ key, computed: expression }))) - } - return dropUndefinedT({ - t: null as unknown as TFieldValues, - limit: a.limit, - skip: a.skip, - select: Option.getOrUndefined(Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(select)), - schema, - computed, - order: Option.getOrUndefined(Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(a.order)), - ttype: a.ttype, - mode: a.mode ?? "transform", - filter: a.filter.length - ? a.filter - : undefined - }) -} diff --git a/packages/effect-app/src/NonEmptySet.ts b/packages/effect-app/src/NonEmptySet.ts deleted file mode 100644 index 1cd39e22a..000000000 --- a/packages/effect-app/src/NonEmptySet.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { NonEmptyReadonlyArray } from "effect/Array" -import type * as Equivalence from "effect/Equivalence" -import * as Option from "effect/Option" -import type * as Order from "effect/Order" -import { flow, pipe } from "./Function.ts" -import { filter_, filterMap, filterMap_, fromArray as fromArrayOriginal, insert as insertOriginal, insert_ as insert_Original, map, map_, reduce, reduce_, remove, remove_, type Set, toArray as toArrayOriginal } from "./Set.ts" - -export interface NonEmptyBrand { - readonly NonEmpty: unique symbol -} - -export type NonEmptySet = Set & NonEmptyBrand - -function make_(ord: Order.Order, eq_?: Equivalence.Equivalence) { - const eq = eq_ - ?? ((x, y) => ord(x, y) === 0) - - const fromArray_ = fromArrayOriginal(eq) - const fromArray = flow(fromArray_, fromSet) - const fromNonEmptyArray = (arr: NonEmptyReadonlyArray) => fromArray_(arr) as NonEmptySet - const concat_ = (set: NonEmptySet, it: Iterable) => fromArray([...set, ...it]) - const insert__ = insertOriginal(eq) - const insert: (a: A) => (set: NonEmptySet) => NonEmptySet = insert__ as any - const insert_: (set: NonEmptySet, a: A) => NonEmptySet = insert_Original as any - - function replace_(set: NonEmptySet, a: A) { - return (pipe(filter_(set, (x) => !eq(x, a)), insert__(a)) as NonEmptySet) - } - - const toArray__ = toArrayOriginal(ord) - - function toArray(s: NonEmptySet) { - return toArray__(s) as NonEmptyReadonlyArray - } - - const remove__ = remove(eq) - const filterMap__ = filterMap(eq) - - return { - insert, - insert_, - remove: (a: A) => flow(remove__(a), fromSet), - remove_: flow(remove_(eq), fromSet), - reduce: reduce(ord), - reduce_: reduce_(ord), - replace: (a: A) => (set: NonEmptySet) => replace_(set, a), - replace_, - toArray, - fromArray, - fromNonEmptyArray, - from: (it: Iterable) => fromArray([...it]), - of: (a: A) => new Set([a]) as unknown as NonEmptySet, - concat_, - concat: (it: Iterable) => (set: NonEmptySet) => concat_(set, it), - - // A and B the same, useful when editing elements. - map: map(eq) as unknown as ( - f: (x: A) => A - ) => (set: NonEmptySet) => NonEmptySet, - map_: map_(eq) as unknown as ( - set: NonEmptySet, - f: (x: A) => A - ) => NonEmptySet, - filterMap: (f: (a: A) => Option.Option) => flow(filterMap__((a) => f(a)), fromSet), - filterMap_: flow(filterMap_(eq), fromSet) - } - // TODO: extend -} - -class Wrapper { - wrapped(ord: Order.Order, eq?: Equivalence.Equivalence) { - return make_(ord, eq) - } -} - -export interface NonEmptySetSchemaExtensions extends ReturnType["wrapped"]> {} - -export const make: ( - ord: Order.Order, - eq?: Equivalence.Equivalence -) => NonEmptySetSchemaExtensions = make_ - -export function fromSet(set: Set) { - if (set.size > 0) { - return Option.some(set as NonEmptySet) - } else { - return Option.none() - } -} - -// TODO -export * from "./Set.ts" diff --git a/packages/effect-app/src/Option.ts b/packages/effect-app/src/Option.ts deleted file mode 100644 index 3daac9dfe..000000000 --- a/packages/effect-app/src/Option.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -// eslint-disable-next-line import/no-unassigned-import -import "./builtin.ts" -import { type Some } from "effect/Option" -import * as Option from "effect/Option" - -export * from "effect/Option" - -export function omitableToNullable(om: Option.Option | undefined) { - return om ?? Option.fromNullishOr(om) -} - -export const fromBool = (b: boolean) => (b ? Option.some(true) : Option.none()) - -/** - * Access property, unwrapping Options along the path - */ -export function p>>( - k: K -): (v: Option.Option) => Option.Option<_A> -export function p(k: K): (v: Option.Option) => Option.Option -export function p(k: any) { - return (v: Option.Option) => Option.flatMap(v, (a) => convert((a as any)[k])) -} -function convert(a: any) { - return Option.isSome(a) || Option.isNone(a) ? a : Option.fromNullishOr(a) -} -export type _A = A extends Some ? Y : never -type KeysMatching = { - [K in keyof T]: T[K] extends V ? K : never -}[keyof T] - -export const PartialExceptionTypeId = Symbol() -export type PartialExceptionTypeId = typeof PartialExceptionTypeId - -export class PartialException { - readonly _typeId: PartialExceptionTypeId = PartialExceptionTypeId -} - -function raisePartial(): X { - throw new PartialException() -} - -/** - * Simulates a partial function - */ -export function partial( - f: (miss: () => X) => (...args: ARGS) => A -): (...args: ARGS) => Option.Option { - return (...args) => { - try { - return Option.some(f(raisePartial)(...args)) - } catch (e) { - if (e instanceof PartialException) { - return Option.none() - } - throw e - } - } -} diff --git a/packages/effect-app/src/Pure.ts b/packages/effect-app/src/Pure.ts deleted file mode 100644 index 41315bc14..000000000 --- a/packages/effect-app/src/Pure.ts +++ /dev/null @@ -1,450 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as Chunk from "effect/Chunk" -import * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import * as Result from "effect/Result" -import * as Tuple from "effect/Tuple" -import * as Context from "./Context.ts" - -const S1 = Symbol() -const S2 = Symbol() -const W = Symbol() - -export interface PureState { - readonly [S1]: (_: S) => void - readonly [S2]: () => S2 - - state: S2 -} - -export interface PureLog { - readonly [W]: () => W - log: Chunk.Chunk -} - -export interface PureEnv extends PureState, PureLog {} - -export interface PureEnvTest extends PureState, PureLog {} - -class PureEnvBase implements PureEnv { - readonly [W]!: () => W - readonly [S1]!: (_: S) => void - readonly [S2]!: () => S2 - readonly state: S2 - readonly log: Chunk.Chunk - - constructor(s: S2) { - this.state = s - this.log = Chunk.empty() - } -} - -export function makePureEnv(s: S2): PureEnv { - return new PureEnvBase(s) -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function unifyPureEnv>( - self: X -): PureEnv< - [X] extends [{ [W]: () => infer W }] ? W : never, - [X] extends [{ [S1]: (_: infer S) => void }] ? S : never, - [X] extends [{ [S2]: () => infer S2 }] ? S2 : never -> { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return self -} - -export type Pure = Effect.Effect | R> - -// type dsl = ProgramDSL - -export function GMUA_( - get: Pure, - modify: (i: GA) => Pure, - update: (i: GA) => Pure -): Pure { - return Effect.flatMap(get, modify).pipe(Effect.flatMap(([s, a]) => Effect.map(update(s), () => a))) -} - -export function GMUA(modify: (i: GA) => Pure) { - return ( - get: Pure, - update: (i: GA) => Pure - ) => GMUA_(get, modify, update) -} - -export function GMU_( - get: Pure, - modify: (i: GA) => Pure, - update: (i: GA) => Pure -): Pure { - return Effect.flatMap(get, modify).pipe(Effect.flatMap(update)) -} - -export function GMU(modify: (i: GA) => Pure) { - return ( - get: Pure, - update: (i: GA) => Pure - ) => GMU_(get, modify, update) -} - -const tagg = Context.Service<{ env: PureEnv }>("PureEnv") -function castTag() { - return tagg as any as Context.Service, PureEnvEnv> -} - -export const ServiceTag = Symbol() -export type ServiceTag = typeof ServiceTag - -export abstract class PhantomTypeParameter { - protected abstract readonly [ServiceTag]: { - readonly [NameP in Identifier]: (_: InstantiatedType) => InstantiatedType - } -} - -export type ServiceShape> = Omit< - T, - keyof Context.ServiceClass.Shape -> - -export abstract class ServiceTagged extends PhantomTypeParameter {} - -export function makeService>(_: Omit) { - return _ as T -} - -export const PureEnvEnv = Symbol() -export interface PureEnvEnv extends ServiceTagged { - env: PureEnv -} - -export function get(): Pure { - return (castTag()).useSync((_) => _.env.state) -} - -export function set(s: S): Pure { - return (castTag()).useSync((_) => { - _.env.state = s - }) -} - -export type PureLogT = Pure - -export function log(w: W): PureLogT { - return (castTag()).useSync((_) => { - _.env.log = Chunk.append(_.env.log, w) - }) -} - -export function logMany(w: Iterable): PureLogT { - return (castTag()).useSync((_) => { - _.env.log = Chunk.appendAll(_.env.log, Chunk.fromIterable(w)) - }) -} - -export function runAll( - self: Effect.Effect>, - s: S4 -): Effect.Effect< - readonly [Chunk.Chunk, Result.Result], - never, - Exclude }> -> { - const a = Effect - .flatMap(self, (x) => - (castTag()) - .useSync( - ({ env: _ }) => ({ log: _.log, state: _.state }) - ) - .pipe( - Effect.map( - ({ log, state }) => Tuple.make(log, Result.succeed(Tuple.make(state, x))) - ) - )) - .pipe(Effect.catch((err: any) => tagg.useSync((env) => Tuple.make(env.env.log, Result.fail(err))))) - return Effect.provide(a, Layer.succeed(tagg, { env: makePureEnv(s) as any }) as any) as any -} - -export function runResult( - self: Effect.Effect>, - s: S4 -) { - return Effect.map(runAll(self, s), ([log, r]) => Tuple.make(log, Result.map(r, ([s]) => s))) -} - -export function runTerm( - self: Effect.Effect>, - s: S4 -) { - return Effect.flatMap( - runAll(self, s), - ([evts, r]) => Effect.map(Effect.fromResult(r), ([s3, a]) => Tuple.make(s3, Chunk.toArray(evts), a)) - ) -} - -export function runTermDiscard( - self: Effect.Effect>, - s: S4 -) { - return Effect.map(runTerm(self, s), ([s3, w3]) => Tuple.make(s3, w3)) -} - -export function runA( - self: Effect.Effect>, - s: S4 -) { - return Effect.map(runAll(self, s), ([log, r]) => Tuple.make(log, Result.map(r, ([, a]) => a))) -} - -export function modify( - mod: (s: S2) => readonly [S3, A] -): Effect.Effect }> { - return (castTag()).useSync((_) => { - const [s, a] = mod(_.env.state) - _.env.state = s as any - return a - }) as any -} - -export function modifyM( - mod: (s: S2) => Effect.Effect> -): Effect.Effect> { - // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) - return Effect.flatMap( - (castTag()).useSync((_) => _), - (_: any) => - Effect.map(mod(_.env.state), ([s, a]: any) => { - _.env.state = s - return a - }) - ) as any -} - -export function updateWith(upd: (s: S2) => S3) { - return modify((_: S2) => { - const r = upd(_) - return Tuple.make(r, r) - }) -} - -export function updateWithEffect( - upd: (s: S2, log: (evt: W) => PureLogT) => Effect.Effect> -): Effect.Effect> { - return modifyM((_: S2) => Effect.map(upd(_, log), (_) => Tuple.make(_, _))) -} - -export type FixEnv = - | Exclude> - | PureEnvEnv - -// export function getMA(self: (s: S) => A): Pure { -// return Effect.accessM((_: PureState) => Ref.get(_.state).map(self)) -// } - -// export function getM(self: (s: S) => Pure): Pure { -// return Effect.accessM((_: PureState) => Ref.get(_.state).flatMap(self)) -// } - -// export function getTM__(self: (s: S) => Pure) { -// return (tag: Tag>) => Effect.accessServiceM(tag)(_ => Ref.get(_.state).flatMap(self)) -// } - -// export function getTM_(tag: Tag>, self: (s: S) => Pure) { -// return Effect.accessServiceM(tag)(_ => Ref.get(_.state).flatMap(self)) -// } - -// export function getTM(tag: Tag>) { -// const access = Effect.accessServiceM(tag) -// return (self: (s: S) => Pure) => access(_ => Ref.get(_.state).flatMap(self)) -// } - -// export function makeProgramDSL(): ProgramDSL { -// return makeDSL_() -// } - -// function makeDSL_() { -// const tag = tagg as unknown as Tag> -// const get = Effect.serviceWithEffect(tag, _ => _.env.state.get) -// function getM(self: (s: S2) => Effect.Effect) { return get.flatMap(self) } -// function get_(self: (s: S2) => A) { return get.map(self)} -// function set(s: S2) { -// return Effect.serviceWithEffect(tag, _ => _.env.state.set(s)) -// } -// function log(w: W2): Effect.Effect<{ -// env: PureEnv; -// }, never, void> { -// return Effect.serviceWithEffect(tag, _ => _.env.log.update(l => l.append(w as any))) as any -// } - -// const baseDSL = { -// get, -// log, -// set -// } - -// function modify(mod: (s: S2) => readonly [S3, A]): Effect.Effect<{ -// env: PureEnv; -// }, never, A> { -// return Effect.serviceWithEffect(tag, _ => -// _.env.state.get.map(_ => mod(_)).flatMap(([s, a]) => _.env.state.set(s as any ).map(() => a)) -// ) as any -// } - -// function modifyM(mod: (s: S2) => Effect.Effect): Effect.Effect<{ -// env: PureEnv; -// } | R, E, A> { -// // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) -// return Effect.serviceWithEffect(tag, _ => -// _.env.state.get.flatMap(_ => mod(_ as unknown as S2)).flatMap(([s, a]) => _.env.state.set(s as any).map(() => a)) -// ) as any -// } - -// function update(upd: (s: S2) => S3) { -// return modify(_ => Tuple.make(upd(_), void 0 as void)) -// } - -// function updateM(upd: (s: S2) => Effect.Effect) { -// return modifyM(_ => upd(_).map(_ => Tuple.make(_, void 0 as void))) -// } - -// const accessLog = Effect.serviceWithEffect(tag, _ => _.env.log.get) - -// function runAll( -// self: Effect.Effect, E, A>, -// s: S4 -// ): Effect.Effect}>, never, readonly [Chunk, Either.Either]> { -// return self.flatMap(x => -// Effect.serviceWithEffect(tag, ({ env: _ }) => -// Effect.all({ log: _.log.get, state: _.state.get }) -// // Ref.get(_.log).flatMap(log => Ref.get(_.state).map(state => ({ log, state }))) -// ).map( -// ( -// { log, state } -// ) => Tuple.make(log, Either.right(Tuple.make(state, x)) as Either.Either) -// ) -// ).catchAll( -// err => -// accessLog.map(log => -// Tuple.make(log, Either.left(err) as Either.Either) -// ) -// ).provideService(tag, { env: makePureEnv(s) as any }) as any -// } - -// function runResult( -// self: Effect.Effect, E, A>, -// s: S4 -// ) { -// return runAll(self, s).map(([log, r]) => Tuple.make(log, r.map(([s]) => s))) -// } - -// function runA( -// self: Effect.Effect, E, A>, -// s: S4 -// ) { -// return runAll(self, s).map(([log, r]) => Tuple.make(log, r.map(([, a]) => a))) -// } - -// return { -// ...baseDSL, - -// getM, -// get_, -// accessLog, -// runAll, -// runA, -// runResult, - -// modify, -// modifyM, -// update, -// updateM -// } -// } - -// type dsl_ = ReturnType> - -// export interface ProgramDSL extends dsl_ { } - -// export function dslmodifyM(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => Effect.Effect) { -// return dsl.modifyM(_ => mod(_, dsl)) -// } - -// export function dslmodify(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => readonly [S, A]) { -// return dsl.modify(_ => mod(_, dsl)) -// } - -// export function dslupdateM(dsl: ProgramDSL, upd: (s: S2, dsl: ProgramDSL) => Effect.Effect) { -// return dsl.updateM(_ => upd(_, dsl)) -// } - -// export function dslupdate(dsl: ProgramDSL, upd: (s: S, dsl: ProgramDSL) => S) { -// return dsl.update(_ => upd(_, dsl)) -// } - -// export interface ZPure {} - -// export class ZPureImpl implements ZPure { -// zipRight( -// f: ZPure -// ): ZPure { -// throw new Error("not implemented") -// } - -// flatMap( -// f: (a: A) => ZPure -// ): ZPure { -// throw new Error("not implemented") - -// } -// } - -// declare type Eff1 = Effect.Effect, never, void> - -// function flatMap(self: Effect.Effect, E, A>, (s: S) => Effect.Effect) { -// } - -// export interface PureEnv2 extends PureState2, PureLog {} - -// const S1 = Symbol() -// const S2 = Symbol() -// const W = Symbol() -// export interface PureState2 { -// readonly [S1]: () => S -// readonly [S2]: () => S2 - -// readonly state: Ref -// } - -// const abc = Do($ => { -// const s = $(Pure.get<{ a: 1 }>()) -// console.log(s) -// // const a = $(Pure.log("hello")) -// $(Pure.log("hallo" as const)) -// $(Pure.log("hello" as const)) -// $(Pure.log<"hallo" | "hello" | 5>(5)) -// }) - -// declare const a: Effect.Effect< -// | { -// env: PureEnv -// } -// | { env: PureEnv } -// | { env: PureEnv }, -// never, -// never -// > -// type R = _R -// type Env = T extends { env: infer rr } ? rr : never - -// type RRRR = Env - -// const abcc = unifyPureEnv( -// undefined as any as RRRR -// ) -// const test = Pure.runAll( -// abc, -// { a: 1 } -// ) diff --git a/packages/effect-app/src/QueueMaker.ts b/packages/effect-app/src/QueueMaker.ts deleted file mode 100644 index bfcf6213e..000000000 --- a/packages/effect-app/src/QueueMaker.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RequestContext } from "./RequestContext.ts" - -/** - * Per-message context envelope (tenant/store id, request metadata) carried - * alongside message-schema payloads. Alias of {@link RequestContext}. - * - * All that remains of the former queue machinery (SB/SQLite/mem `QueueMaker` - * implementations and the `QueueBase` interface) after the move to cluster - * entities — the message schemas that embed it still reference this envelope. - */ -export const QueueMeta = RequestContext diff --git a/packages/effect-app/src/RequestContext.ts b/packages/effect-app/src/RequestContext.ts deleted file mode 100644 index b8c6a7b2b..000000000 --- a/packages/effect-app/src/RequestContext.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as Context from "./Context.ts" -import { UserProfileId } from "./ids.ts" -import * as S from "./Schema.ts" -import { NonEmptyString255 } from "./Schema.ts" - -export const Locale = S.Literals(["en", "de"]) -export type Locale = typeof Locale.Type - -export class LocaleRef extends Context.Reference("Locale", { defaultValue: (): Locale => "en" }) {} - -export class RequestContext extends S.Opaque()(S.Struct({ - span: S.Struct({ - traceId: S.String, - spanId: S.String, - sampled: S.Boolean - }), - name: NonEmptyString255, - locale: Locale, - sourceId: S.optional(NonEmptyString255), // TODO? - namespace: NonEmptyString255, - /** @deprecated */ - userProfile: S.optional(S.Struct({ sub: UserProfileId })) // -})) { - static toMonitoring(this: void, self: RequestContext) { - return { - operationName: self.name, - locale: self.locale - } - } -} - -export const spanAttributes = (ctx: Pick & Partial) => ({ - "code.function.name": ctx.name, - "app.locale": ctx.locale, - "app.tenant.id": ctx.namespace, - ...ctx.sourceId ? { "client.id": ctx.sourceId } : {}, - ...(ctx.userProfile?.sub - ? { - "user.id": ctx - .userProfile - .sub, - "user.roles": "roles" in ctx - .userProfile - ? ctx.userProfile.roles - : undefined - } - : {}) -}) diff --git a/packages/effect-app/src/RequestScopedDependencies.ts b/packages/effect-app/src/RequestScopedDependencies.ts deleted file mode 100644 index e045f7a56..000000000 --- a/packages/effect-app/src/RequestScopedDependencies.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as Data from "effect/Data" -import type * as Scope from "effect/Scope" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" - -export type Root = "root" - -export class RequestScopedDependencyNotStartedError extends Data.TaggedError( - "RequestScopedDependencyNotStartedError" -)<{ readonly key: string }> {} - -export interface RequestScopedDependency extends Context.Reference { - readonly current: Effect.Effect - readonly layer: Layer.Layer -} - -export const access = ( - dependency: Context.Reference, - key: string -): Effect.Effect => - dependency.pipe( - Effect.filterOrFail((_): _ is Service => _ !== "root", () => new RequestScopedDependencyNotStartedError({ key })) - ) - -export const make = ( - key: string, - service: Effect.Effect -): RequestScopedDependency> => { - const dependency = Context.Reference(key, { defaultValue: () => "root" }) - return Object.assign(dependency, { - current: access(dependency, key), - layer: Layer.effect(dependency, service) - }) -} - -export const layer = < - Deps extends readonly [RequestScopedDependency, ...Array>] ->( - ...dependencies: Deps -): Layer.Layer< - never, - { [K in keyof Deps]: Deps[K] extends RequestScopedDependency ? E : never }[number], - { [K in keyof Deps]: Deps[K] extends RequestScopedDependency ? R : never }[number] -> => dependencies.map((_) => _.layer).reduce((acc, l) => Layer.merge(acc, l)) as any diff --git a/packages/effect-app/src/Schema.ts b/packages/effect-app/src/Schema.ts deleted file mode 100644 index 71afd0056..000000000 --- a/packages/effect-app/src/Schema.ts +++ /dev/null @@ -1,499 +0,0 @@ -import * as S from "effect/Schema" -import * as EffectSchemaParser from "effect/SchemaParser" -import { type Simplify } from "effect/Struct" -import type * as Tracer from "effect/Tracer" -import type { RequiredKeys } from "effect/Types" -import type { Arbitrary as FastCheckArbitrary } from "fast-check" -import type { NonEmptyReadonlyArray } from "./Array.ts" -import { Email as EmailT, type Email as EmailType } from "./Schema/email.ts" -import { concurrencyUnbounded, withDefaultMake, withDefaultParseOptions } from "./Schema/ext.ts" -import type { FC } from "./Schema/FastCheck.ts" -import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.ts" -import { type AST } from "./Schema/schema.ts" -import * as SchemaAST from "./SchemaAST.ts" -import { copy, defineOwn, extendM, type StructuralCopyOrigin } from "./utils.ts" - -// --------------------------------------------------------------------------- -// Default helpers — re-exported from effect/Schema -// -// The five helpers below are surfaced explicitly so the (important) policy -// around them lives next to the export. See also the file-level note in -// `./Schema/ext.ts` and the documented wrappers in -// `./Schema/ext.ts`, `./Schema/numbers.ts`, `./Schema/moreStrings.ts`, -// and `./ids.ts`. -// -// **Construction-only**: `withConstructorDefault` fills the field when it -// is omitted from `.make(...)` input. It is NOT applied during decode, so -// it CANNOT be used to just-in-time migrate database fields. A stored -// record missing the field will still fail to decode. -// -// **`withDecodingDefault*` is discouraged**: a missing field in persisted -// data is just as likely to be data corruption as it is an old-shape -// document; silently substituting a default hides the problem and can -// poison downstream aggregates. Prefer an explicit, preferably versioned -// migration of database data over shoving missing fields under the rug. -// --------------------------------------------------------------------------- - -/** - * Attach a default value used **only** when constructing a value (e.g. via - * `.make(...)` or struct constructors) and the field is omitted from input. - * - * **Not applied during decode.** Decoding a payload that is missing the - * field will still raise a parse error. Do **not** rely on this to migrate - * database fields just-in-time — see the section header above. - * - * @see {@link withDecodingDefault} / {@link withDecodingDefaultType} — - * decode-time variants (discouraged for persisted data; use explicit, - * versioned migrations instead). - */ -export { withConstructorDefault } from "effect/Schema" - -/** - * Attach a default value used during decode when the field's `Encoded` value - * is missing **or** `undefined`. The default is specified as an `Encoded` - * value and threaded through the schema's decode step. - * - * **Discouraged for persisted data.** A missing field in a stored record is - * just as likely to be data corruption as it is an old-shape document; - * silently substituting a default hides the problem. Prefer an explicit, - * preferably versioned migration of database data — see the section header - * above. - * - * @see {@link withDecodingDefaultKey} — key-absent-only variant - * @see {@link withDecodingDefaultType} — `Type`-side variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefault } from "effect/Schema" - -/** - * Attach a default value used during decode when the field **key is absent** - * (note: not when present and `undefined`). The default is an `Encoded` - * value. - * - * **Discouraged for persisted data** — same reasoning as - * {@link withDecodingDefault}. Use explicit, preferably versioned migrations - * over decode-time fallbacks. - * - * @see {@link withDecodingDefault} — value-absent-or-undefined variant - * @see {@link withDecodingDefaultTypeKey} — `Type`-side variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefaultKey } from "effect/Schema" - -/** - * Attach a default value used during decode when the field is missing **or** - * `undefined`. The default is specified as a `Type` value (i.e. on the - * decoded side). - * - * **Discouraged for persisted data** — same reasoning as - * {@link withDecodingDefault}. Use explicit, preferably versioned migrations - * over decode-time fallbacks. - * - * @see {@link withDecodingDefault} — `Encoded`-side variant - * @see {@link withDecodingDefaultTypeKey} — key-absent-only variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefaultType } from "effect/Schema" - -/** - * Attach a default value used during decode when the field **key is absent** - * (note: not when present and `undefined`). The default is a `Type` value. - * - * **Discouraged for persisted data** — same reasoning as - * {@link withDecodingDefault}. Use explicit, preferably versioned migrations - * over decode-time fallbacks. - * - * @see {@link withDecodingDefaultKey} — `Encoded`-side variant - * @see {@link withDecodingDefaultType} — value-absent-or-undefined variant - * @see {@link withConstructorDefault} — for `.make(...)`-time defaults - */ -export { withDecodingDefaultTypeKey } from "effect/Schema" - -export * from "effect/Schema" - -export * from "./Schema/Class.ts" -export { Class, ErrorClass, Opaque, OpaqueErrorFacadeClass, OpaqueFacade, OpaqueShape, OpaqueType, TaggedClass, TaggedErrorClass } from "./Schema/Class.ts" - -export { fromBrand, nominal } from "./Schema/brand.ts" -export { Array, Boolean, Date, DateFromString, Finite, Literals, NullOr, Number, ReadonlyMap, ReadonlySet } from "./Schema/ext.ts" -export { Int, NonNegativeInt } from "./Schema/numbers.ts" - -export * from "./Schema/email.ts" -export * from "./Schema/ext.ts" -export * from "./Schema/moreStrings.ts" -export * from "./Schema/numbers.ts" -export * from "./Schema/phoneNumber.ts" -export * from "./Schema/schema.ts" -export * from "./Schema/SpecialJsonSchema.ts" -export * from "./Schema/SpecialOpenApi.ts" -export * from "./Schema/strings.ts" -export { NonEmptyString } from "./Schema/strings.ts" - -export * as SchemaIssue from "effect/SchemaIssue" - -/** Fast-check generator factory previously exported as `Schema.Arbitrary`. */ -export type Arbitrary = (fc: FC) => FastCheckArbitrary - -export const decodeEffectConcurrently: typeof S.decodeEffect = withDefaultParseOptions(S.decodeEffect) -export const decodeUnknownEffectConcurrently: typeof S.decodeUnknownEffect = withDefaultParseOptions( - S.decodeUnknownEffect -) -export * as SchemaParser from "./Schema/SchemaParser.ts" - -/** - * Schema for a TypeScript `void` return value — the canonical `Void` used - * across the app, including RPC success schemas. Accepts **any present value** - * at runtime and discards it to `undefined`, while keeping the decoded/encoded - * type as `void`. Use `S.Undefined` when the input must be exactly `undefined`. - */ -export interface Void extends S.Void {} -export const Void: Void = S.Void - -// --------------------------------------------------------------------------- -// Struct / NonEmptyArray / Record -// --------------------------------------------------------------------------- - -export function Struct( - fields: Fields -): Struct { - const result = S.Struct(fields).annotate(concurrencyUnbounded) - const allowVoidMake = (schema: any): any => { - // Normalize omitted input to an empty object so optional/default-only structs can be constructed with make(). - // Do not read schema.make first: Effect 4 caches it as a non-writable own property. - const origMake = EffectSchemaParser.make(schema) - const origMakeOption = EffectSchemaParser.makeOption(schema) - const origMakeEffect = EffectSchemaParser.makeEffect(schema) - const wrap = (orig: (input: any, options?: any) => any) => - function(this: any, input: any, options?: any) { - return orig.call(this, input === undefined ? {} : input, options) - } - defineOwn(schema, "make", wrap(origMake)) - defineOwn(schema, "makeOption", wrap(origMakeOption)) - defineOwn(schema, "makeEffect", wrap(origMakeEffect)) - return schema - } - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment - const origMapFields: any = result.mapFields - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment - const origAnnotate: any = result.annotate - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/no-unsafe-assignment - const origAnnotateKey: any = result.annotateKey - - const preserveCopyAndMethods = (schema: any): any => { - schema.copy = copy - schema.mapFields = function(this: any, f: any, options?: any) { - return (result as any).mapFields.call(this, f, options) - } - schema.annotate = function(this: any, annotations?: any) { - return (result as any).annotate.call(this, annotations) - } - schema.annotateKey = function(this: any, annotations?: any) { - return (result as any).annotateKey.call(this, annotations) - } - return allowVoidMake(schema) - } - ;(result as any).mapFields = function(this: any, f: any, options?: any) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const mapped = origMapFields.call(this, f, options).annotate(concurrencyUnbounded) - return preserveCopyAndMethods(mapped) - } - ;(result as any).annotate = function(this: any, annotations?: any) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const annotated = origAnnotate.call(this, annotations) - return preserveCopyAndMethods(annotated) - } - ;(result as any).annotateKey = function(this: any, annotations?: any) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - const annotated = origAnnotateKey.call(this, annotations) - return preserveCopyAndMethods(annotated) - } - ;(result as any).copy = copy - allowVoidMake(result) - return result as Struct -} - -export interface Struct extends - S.Bottom< - Struct.Type, - Struct.Encoded, - Struct.DecodingServices, - Struct.EncodingServices, - AST.Objects, - // Rebuild is what's returned from annotate etc - Struct, - Struct.MakeIn, - Struct.Iso - > -{ - /** - * The field definitions of this struct. Spread them into a new struct to - * reuse fields across schemas. - * - * **Example** (Reusing fields across structs) - * - * ```ts - * import * as Schema from "effect/Schema" - * - * const Timestamped = Schema.Struct({ - * createdAt: Schema.Date, - * updatedAt: Schema.Date - * }) - * - * const User = Schema.Struct({ - * ...Timestamped.fields, - * name: Schema.String, - * email: Schema.String - * }) - * ``` - */ - readonly fields: Fields - /** - * Returns a new struct with the fields modified by the provided function. - * - * **Options** - * - * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints - * that were attached to the original union. Defaults to `false`. - * - * **Warning**: This is an unsafe operation. Since `mapFields` - * transformations change the schema type, the original refinement functions - * may no longer be valid or safe to apply to the transformed schema. Only - * use this option if you have verified that your refinements remain correct - * after the transformation. - */ - mapFields( - f: (fields: Fields) => To, - options?: { - readonly unsafePreserveChecks?: boolean | undefined - } | undefined - ): Struct>> - - // added copy - readonly copy: StructuralCopyOrigin> -} - -export declare namespace Struct { - export type Fields = S.Struct.Fields - export type Type = S.Struct.Type - export type Encoded = S.Struct.Encoded - export type DecodingServices = S.Struct.DecodingServices - export type EncodingServices = S.Struct.EncodingServices - // changed; all optional allows void - export type MakeIn = RequiredKeys> extends never - ? void | S.Struct.MakeIn - : S.Struct.MakeIn - export type Iso = S.Struct.Iso -} - -export type StructNestedEncodedError = { - readonly _tag: "StructNestedEncodedError" - readonly message: "Expected a Struct schema or a schema with from.Encoded" - readonly schema: T -} - -export type StructNestedEncoded = T extends { fields: infer Fields extends S.Struct.Fields } ? Struct.Encoded - : T extends { readonly from: { readonly Encoded: infer Encoded } } ? Encoded - : StructNestedEncodedError - -export function NonEmptyArray(value: Value): S.NonEmptyArray { - return S.NonEmptyArray(value).annotate(concurrencyUnbounded) -} - -export function TaggedStruct( - value: Tag, - fields: Fields -): TaggedStruct { - return Struct({ _tag: S.tag(value), ...fields }) as any -} -export interface TaggedStruct - extends Struct<{ readonly _tag: S.tag } & Fields> -{} -export declare namespace TaggedStruct { - export type Fields = S.Struct.Fields - export type Type = S.Struct.Type< - { readonly _tag: S.tag } & F - > - export type Encoded = S.Struct.Encoded< - { readonly _tag: S.tag } & F - > - export type DecodingServices = - S.Struct.DecodingServices< - { readonly _tag: S.tag } & F - > - export type EncodingServices = - S.Struct.EncodingServices< - { readonly _tag: S.tag } & F - > - export type MakeIn = S.Struct.MakeIn< - { readonly _tag: S.tag } & F - > - export type Iso = S.Struct.Iso< - { readonly _tag: S.tag } & F - > -} - -export function Record( - key: Key, - value: Value -): S.$Record { - return S.Record(key, value).annotate(concurrencyUnbounded) -} -export declare namespace Record { - export type Key = S.Record.Key - export type Type = S.Record.Type - export type Encoded = S.Record.Encoded -} - -export const SpanId = Symbol() -export type SpanId = typeof SpanId - -export interface WithOptionalSpan { - [SpanId]?: Tracer.Span -} - -export const Email = EmailT.pipe(withDefaultMake) - -export type Email = EmailType - -export const PhoneNumber = PhoneNumberT.pipe(withDefaultMake) - -export type PhoneNumber = PhoneNumberType - -// Copied from SchemaAST.collectSentinels (marked @internal in effect). -// Returns all { key, literal } pairs that can discriminate a union member. -const getTagFromAST = (schema: S.Top): string => { - const sentinels = collectSentinelsFromAST(schema.ast) - const sentinel = sentinels.find((s) => s.key === "_tag") - if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal - throw new Error("No _tag literal found on schema member") -} - -function collectSentinelsFromAST( - ast: SchemaAST.AST -): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> { - switch (ast._tag) { - case "Declaration": { - const s = ast.annotations?.["~sentinels"] - return Array.isArray(s) ? s : [] - } - case "Objects": - return ast.propertySignatures.flatMap( - (ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => { - const type = ps.type - if (!SchemaAST.isOptional(type)) { - if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }] - if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }] - } - return [] - } - ) - case "Suspend": - return collectSentinelsFromAST(ast.thunk()) - default: - return [] - } -} - -export const tags = < - Members extends NonEmptyReadonlyArray<(S.Top & { readonly Type: { readonly _tag: string } })> ->( - self: Members -) => - S.Literals( - self.map(getTagFromAST) as { - [Index in keyof Members]: Members[Index]["Type"]["_tag"] - } - ) as S.Literals< - { - [Index in keyof Members]: Members[Index]["Type"]["_tag"] - } - > - -type TaggedUnionMembers = NonEmptyReadonlyArray< - S.Top & { readonly Type: { readonly _tag: string } } -> - -type TaggedUnionTags = S.Literals< - { - [Index in keyof Members]: Members[Index]["Type"]["_tag"] - } -> - -type TaggedPropertyKeys = { - [K in keyof A & string]: A[K] extends Members[number]["Type"] ? K : never -}[keyof A & string] - -type PropertyGuardsFor< - Members extends TaggedUnionMembers, - K extends string, - A -> = - & { - readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: ( - target: A - ) => target is A & { readonly [P in K]: M["Type"] } - } - & { - readonly isAnyOf: >( - tags: Tags - ) => ( - target: A - ) => target is A & { readonly [P in K]: Extract } - } - -type PropertyGuards< - Members extends TaggedUnionMembers, - K extends string -> = - & { - readonly [M in Members[number] as `is${M["Type"]["_tag"]}`]: < - T extends { readonly [P in K]: Members[number]["Type"] } - >(target: T) => target is T & { readonly [P in K]: M["Type"] } - } - & { - readonly isAnyOf: >( - tags: Tags - ) => ( - target: T - ) => target is T & { readonly [P in K]: Extract } - } - -type TaggedUnionWithTags = S.toTaggedUnion<"_tag", Members> & { - readonly tags: TaggedUnionTags - readonly generateGuards: (property: K) => PropertyGuards - readonly generateGuardsFor: () => >( - property: K - ) => PropertyGuardsFor -} - -const extendTaggedUnionWithTags = ( - schema: S.Union -): TaggedUnionWithTags => - extendM(schema.pipe(S.toTaggedUnion("_tag")), (tagged) => { - const makeGuards = (property: string) => { - const result: any = {} - const guards: Record boolean> = tagged.guards - for (const tag of Object.keys(guards)) { - const guard = guards[tag]! - result[`is${tag}`] = (target: any) => guard(target[property]) - } - result.isAnyOf = (memberTags: Array) => { - const check = tagged.isAnyOf(memberTags) - return (target: any) => check(target[property]) - } - return result - } - return { - tags: tags(schema.members), - generateGuards: makeGuards, - generateGuardsFor: () => makeGuards - } - }) - -export const ExtendTaggedUnion = ( - schema: S.Union -): TaggedUnionWithTags => extendTaggedUnionWithTags(schema) - -export const TaggedUnion = < - Members extends TaggedUnionMembers ->(members: Members): TaggedUnionWithTags => extendTaggedUnionWithTags(S.Union(members)) diff --git a/packages/effect-app/src/Schema/Class.ts b/packages/effect-app/src/Schema/Class.ts deleted file mode 100644 index a13163e22..000000000 --- a/packages/effect-app/src/Schema/Class.ts +++ /dev/null @@ -1,683 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type * as Cause from "effect/Cause" -import * as Effect from "effect/Effect" -import * as S from "effect/Schema" -import * as SchemaGetter from "effect/SchemaGetter" -import * as SchemaIssue from "effect/SchemaIssue" -import * as SchemaTransformation from "effect/SchemaTransformation" -import type { Struct as EffectAppStruct } from "../Schema.ts" -import * as SchemaAST from "../SchemaAST.ts" -import { copyOrigin } from "../utils.ts" -import { concurrencyUnbounded } from "./ext.ts" -import * as SchemaParser from "./SchemaParser.ts" - -type ClassAnnotations = S.Annotations.Declaration - -export interface EnhancedClass< - Self, - SchemaS extends S.Constraint & { readonly fields: S.Struct.Fields }, - Inherited -> extends S.Class { - /** - * See `copyOrigin` docs in `utils.ts` for return-type design details. - */ - readonly copy: ReturnType Self>> -} -type MissingSelfGeneric = - `Missing \`Self\` generic - use \`class Self extends ${Usage}()(${Params}{ ... })\`` - -type HasFields = { - readonly fields: Fields -} | { - readonly from: HasFields -} - -type ClassOptions = { - readonly strict?: boolean -} - -export declare const ExtendedSchemaNoEncoded: unique symbol - -export type ExtendedSchemaNoEncoded = typeof ExtendedSchemaNoEncoded - -/** - * Override schema `Encoded` while keeping `Constraint` (intersection, not `Omit`). - * Free type-params for `Encoded` keep the conditional unresolved — callers that - * need Encoded overrides use the intersection form of the class factory return type. - */ -type WithEncoded = SchemaS & { readonly Encoded: Encoded } - -type ExtendedSchema = [ - Encoded -] extends [ExtendedSchemaNoEncoded] ? SchemaS - : WithEncoded - -/** Struct fields schema, optionally with a caller-supplied Encoded override. */ -type ClassFieldsSchema = ExtendedSchema, Encoded> - -export type Class = EnhancedClass< - Self, - S, - Inherited -> - -/** - * Build a modified Declaration that accepts struct-matching values during - * encoding, given the original Declaration and the class's fields. - */ -function makeRelaxedDeclaration( - ast: SchemaAST.Declaration, - fields: S.Struct.Fields, - cls: any -): SchemaAST.Declaration { - const parseOptions = ast.annotations?.["parseOptions"] as SchemaAST.ParseOptions | undefined - const structSchema = S.Struct(fields) - const annotatedStruct = parseOptions ? S.toType(structSchema).annotate({ parseOptions }) : S.toType(structSchema) - const decodeStruct = SchemaParser.decodeUnknownEffect(annotatedStruct) - - return new SchemaAST.Declaration( - ast.typeParameters, - () => (input: unknown, self: SchemaAST.Declaration, options: SchemaAST.ParseOptions) => { - if (input instanceof cls) { - return Effect.succeed(input) - } - if (input !== null && typeof input === "object") { - return decodeStruct(input, options) - } - return Effect.fail(new SchemaIssue.InvalidType(self, input)) - }, - ast.annotations, - ast.checks, - ast.encoding, - ast.context - ) -} - -// --------------------------------------------------------------------------- -// Class — like Schema.Class but with relaxed encoding -// --------------------------------------------------------------------------- - -/** - * Like `Schema.Class`, but the resulting class accepts plain objects matching - * the struct schema during encoding — not only `instanceof` or type-id - * checks. - * - * @example - * ```ts - * import * as Schema from "effect/Schema" - * import { Class } from "./Class.ts" - * - * class A extends Class("A")({ a: Schema.String }) {} - * - * // Construction works as normal: - * new A({ a: "hello" }) - * - * // Encoding accepts plain objects: - * Schema.encodeUnknownSync(A)({ a: "hello" }) // { a: "hello" } - * ``` - */ -export const Class: ( - identifier: string -) => ( - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"Class"> - : EnhancedClass< - Self, - ClassFieldsSchema, - Brand - > = (identifier) => (fields, annotations, options) => { - const relaxed = options?.strict === false - // Build the original Schema.Class - const Base = (S.Class as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations }) - // Get the original ast getter from the base class - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - - // Cache per-class to avoid recomputing - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - // Call the original getter with `this` bound to the actual user class, - // so getClassSchema(this) creates a schema that uses `new this(...)`. - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -// --------------------------------------------------------------------------- -// TaggedClass — like Schema.TaggedClass but with relaxed encoding -// --------------------------------------------------------------------------- - -/** - * Like `Schema.TaggedClass`, but the resulting class accepts plain objects - * matching the struct schema during encoding. - * - * @example - * ```ts - * import * as Schema from "effect/Schema" - * import { TaggedClass } from "./Class.ts" - * - * class Circle extends TaggedClass()("Circle", { - * radius: Schema.Number - * }) {} - * - * Schema.encodeUnknownSync(Circle)({ _tag: "Circle", radius: 5 }) - * ``` - */ -export const TaggedClass: ( - identifier?: string -) => ( - tag: Tag, - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"TaggedClass"> - : EnhancedClass< - Self, - ClassFieldsSchema<{ readonly _tag: S.tag } & Fields, Encoded>, - Brand - > = (identifier) => (tag, fields, annotations, options) => { - const relaxed = options?.strict === false - const Base = (S.TaggedClass as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations }) - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -// --------------------------------------------------------------------------- -// ErrorClass — like Schema.ErrorClass but with relaxed encoding -// --------------------------------------------------------------------------- - -export const ErrorClass: ( - identifier: string -) => ( - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"ErrorClass"> - : EnhancedClass< - Self, - ClassFieldsSchema, - Cause.YieldableError & Brand - > = (identifier) => (fields, annotations, options) => { - const relaxed = options?.strict === false - const Base = (S.Error as any)(identifier)(fields, { ...concurrencyUnbounded, ...annotations }) - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -// --------------------------------------------------------------------------- -// TaggedErrorClass — like Schema.TaggedErrorClass but with relaxed encoding -// --------------------------------------------------------------------------- - -export const TaggedErrorClass: ( - identifier?: string -) => ( - tag: Tag, - fieldsOr: Fields | HasFields, - annotations?: ClassAnnotations, - options?: ClassOptions -) => [Self] extends [never] ? MissingSelfGeneric<"TaggedErrorClass"> - : EnhancedClass< - Self, - ClassFieldsSchema<{ readonly _tag: S.tag } & Fields, Encoded>, - Cause.YieldableError & Brand - > = (identifier) => (tag, fields, annotations, options) => { - const relaxed = options?.strict === false - const Base = (S.TaggedError as any)(identifier)(tag, fields, { ...concurrencyUnbounded, ...annotations }) - const originalAstDescriptor = Object.getOwnPropertyDescriptor(Base, "ast")! - const astCache = new WeakMap() - const copyCache = new WeakMap>() - - return class extends Base { - static get copy() { - let cached = copyCache.get(this) - if (cached === undefined) { - cached = copyOrigin(this) - copyCache.set(this, cached) - } - return cached - } - static get ast(): SchemaAST.Declaration { - let cached = astCache.get(this) - if (cached !== undefined) return cached - const originalAst = originalAstDescriptor.get!.call(this) as SchemaAST.Declaration - cached = relaxed ? makeRelaxedDeclaration(originalAst, Base.fields, this) : originalAst - astCache.set(this, cached) - return cached - } - static mapFields(f: any, options?: any) { - return Base.mapFields(f, options).annotate(concurrencyUnbounded) - } - } as any - } - -export interface Opaque extends S.Opaque { - readonly "Encoded": Encoded extends ExtendedSchemaNoEncoded ? SchemaS["Encoded"] : Encoded -} - -export const Opaque: () => ( - schema: S -) => Opaque & Omit = S.Opaque as any - -/** - * Like {@link Opaque}, but the class **instance type is exactly `Self`** (the supplied - * decoded `Type`) instead of the structurally-computed `struct["Type"] & Brand`. - * - * Stock `Opaque` types the instance as `struct["Type"] & Brand` and only overrides the - * schema's `Type`/`Encoded` *members* with `Self`/`Encoded`. So `make`/`copy`/consumers - * still resolve the struct's mapped `Type`. With a codegen-supplied pre-expanded literal - * `Type` interface, `OpaqueType` lets all of those resolve a single **named** interface - * (resolved once per checker) instead of re-deriving the mapped `Type` — cutting - * instantiation on `Type`-touching consumers. - * - * Use with `class X extends OpaqueType()(struct) {}` where `X.Type` - * and `X.Encoded` are generated literal interfaces (see `@effect-app/eslint-codegen-model`, - * `static` + `type` mode). - * - * KNOWN GAP — **no branding**: the instance type is a plain structural `Self`, so opaque - * types of identical shape are mutually assignable. (Stock `Opaque` is also structural by - * default — `Brand` defaults to `{}` — so this only differs if you passed a non-default - * `Brand`.) Re-introducing a nominal brand on top of a supplied `Self` (e.g. branding the - * generated `Type` interface) is not yet implemented. - * - * NOTE: only `Type` (via `Self`) and `Encoded` are supplied statically here; `make`'s input - * (`~type.make.in`) and other derived members are still computed from the struct. See - * a future `OpaqueShape`-style helper if those also need to be supplied. - */ -export interface OpaqueType extends S.Opaque { - readonly "Encoded": Encoded extends ExtendedSchemaNoEncoded ? SchemaS["Encoded"] : Encoded - new(_: never): Self -} - -export const OpaqueType: () => ( - schema: S -) => OpaqueType & Omit = S.Opaque as any - -type OpaqueFacadeConstructorArgs = {} extends MakeIn ? [props?: MakeIn, options?: S.MakeOptions] - : [props: MakeIn, options?: S.MakeOptions] - -// Only the codec channels are required. `copy`/`fields`/`mapFields` are NOT -// required here: transformed schemas (`.pipe(S.encodeKeys/annotate/filter/...)`) -// don't expose them at the static level, and requiring them would reject those -// models from facading. When present they still flow through via -// `OpaqueFacadeStatics`; when absent the facade simply doesn't offer them. -type OpaqueFacadeInput = S.Top & { - readonly DecodingServices: DecodingServices - readonly EncodingServices: EncodingServices -} - -type OpaqueClassFacadeInput = - & OpaqueFacadeInput< - DecodingServices, - EncodingServices - > - & (abstract new(...args: Array) => unknown) - -type PrototypeFunction = Function & { prototype: object } - -// Class schemas expose a constructor descriptor under this key (was previously -// identified via a `~effect/Schema/Class` annotation id). -const ConstructorAnnotationKey = "~constructor" - -const getOwnOrInheritedPropertyDescriptor = (value: object, property: PropertyKey): PropertyDescriptor | undefined => { - let current: object | null = value - while (current !== null) { - const descriptor = Object.getOwnPropertyDescriptor(current, property) - if (descriptor !== undefined) return descriptor - current = Object.getPrototypeOf(current) - } -} - -const isClassSchemaConstructor = (value: unknown): value is PrototypeFunction => { - if (typeof value !== "function") return false - // Transformed schemas (e.g. `Class.pipe(S.encodeKeys(...))`) are also functions with a - // Declaration AST that still carries `~constructor`, but they are not constructable classes. - // Real Schema.Class constructors expose a static `identifier`. - if (typeof (value as { readonly identifier?: unknown }).identifier !== "string") return false - const ast = (value as { readonly ast?: unknown }).ast - return SchemaAST.isAST(ast) - && SchemaAST.isDeclaration(ast) - && ast.annotations?.[ConstructorAnnotationKey] !== undefined -} - -const getFacadeClassSchema = (schema: OpaqueFacadeInput): PrototypeFunction | undefined => { - if (isClassSchemaConstructor(schema)) return schema - const target = "to" in schema ? (schema as { readonly to?: unknown }).to : undefined - return isClassSchemaConstructor(target) ? target : undefined -} - -const makePublicClassTransformation = (Public: PrototypeFunction) => - new SchemaTransformation.Transformation( - SchemaGetter.transform((input) => Reflect.construct(Public, [input], Public)), - SchemaGetter.passthrough() - ) - -const makeFacadeClassAst = ( - schema: OpaqueFacadeInput, - originalAst: SchemaAST.AST, - Public: PrototypeFunction -): SchemaAST.AST => { - if (SchemaAST.isDeclaration(schema.ast)) { - const schemaEncoding = schema.ast.encoding ?? [] - if (schemaEncoding.length > 0) { - return new SchemaAST.Declaration( - schema.ast.typeParameters, - schema.ast.run, - schema.ast.annotations, - schema.ast.checks, - [new SchemaAST.Link(schemaEncoding[0]!.to, makePublicClassTransformation(Public)), ...schemaEncoding.slice(1)], - schema.ast.context, - schema.ast.encodingChecks - ) - } - } - - return schema.rebuild(originalAst).ast -} - -// Carry the schema's own statics, dropping the generic `S.Top` machinery and -// `prototype`. EXCEPT `to`: models compose each other via `X.to.fields` / -// `X.to.copy` at definition time, so it must survive on the facade. -type OpaqueFacadeStatics = Omit | "prototype"> - -/** - * Like {@link OpaqueType}, but ALSO supplies a static **make-input** shape, so - * `make`/`copy` resolve a named `MakeIn` interface instead of re-deriving the struct's - * mapped `~type.make.in`. Use with codegen-supplied `X.Type` / `X.Encoded` / `X.Make`: - * - * ```ts - * class X extends OpaqueShape()(struct) {} - * ``` - * - * `decode`/`encode` already use the supplied `Type`/`Encoded` (they read the schema's - * `Type`/`Encoded` members, which are `Self`/`Encoded` here) — no separate override needed. - * - * NOTE — measured gain is modest (~5%): the struct passed to the wrapper is still - * constructed in full (definition cost dominates); the static shapes only cheapen - * consumer-side reads of `Type`/`Encoded`/`MakeIn`. Same `no-branding` gap as - * {@link OpaqueType}. - */ -export interface OpaqueShape - extends S.Opaque -{ - readonly "Encoded": Encoded - readonly "~type.make.in": MakeIn - new(_: never): Self -} - -export const OpaqueShape: () => ( - schema: S -) => OpaqueShape & Omit = S.Opaque as any - -/** - * Shallow public view for generated model facades. - * - * The runtime value can still be the full private schema class, but emitted - * declarations expose only named `Type` / `Encoded` / `Make` interfaces and a - * small static surface. This keeps downstream project references from pulling - * the private struct field map back through `typeof Model`. - */ -export interface OpaqueFacade< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} -> extends - S.Bottom< - Self, - Encoded, - DecodingServices, - EncodingServices, - SchemaAST.AST, - S.Codec, - MakeIn, - Self, - readonly [], - MakeIn - > -{ - new(_: never): Brand - readonly copy: ReturnType Self>> - // NOTE: no `identifier` here — `S.Opaque` (and requests) build on `S.Bottom`, not `S.Class`, - // so they have no `identifier`. It lives only on the class facades below. - // NOTE: `fields` / `mapFields` are intentionally NOT redeclared here. They are - // carried (precise) from the underlying schema via `OpaqueFacadeStatics`. A wide - // `mapFields(f: (fields: S.Struct.Fields) => To)` override would win overload - // resolution and erase field precision in `Q.project(X.mapFields(...))`. -} - -export interface OpaqueClassFacade< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} -> extends - S.Bottom< - Self, - Encoded, - DecodingServices, - EncodingServices, - SchemaAST.AST, - S.Codec, - MakeIn, - Self, - readonly [], - MakeIn - > -{ - new(...args: OpaqueFacadeConstructorArgs): Brand - readonly copy: ReturnType Self>> - readonly identifier: string -} - -export function OpaqueFacade< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} ->() { - function facade>( - schema: SchemaS - ): - & OpaqueClassFacade - & OpaqueFacadeStatics - function facade>( - schema: SchemaS - ): - & OpaqueFacade - & OpaqueFacadeStatics - function facade(schema: OpaqueFacadeInput) { - const Base = getFacadeClassSchema(schema) - if (Base !== undefined) { - const astCache = new WeakMap() - const originalAstDescriptor = getOwnOrInheritedPropertyDescriptor(Base, "ast") - - return class extends (Base as any) { - static get ast(): SchemaAST.AST { - let cached = astCache.get(this) - if (cached !== undefined) return cached - - const originalAst = originalAstDescriptor?.get?.call(this) as SchemaAST.AST - cached = makeFacadeClassAst(schema, originalAst, this) - astCache.set(this, cached) - return cached - } - } - } - - class Facade {} - return Object.setPrototypeOf(Facade, schema) - } - - return facade -} - -/** - * Like class-schema {@link OpaqueFacade}, but for error models (`TaggedErrorClass` / - * `ErrorClass`). The decoded instance type carries `Cause.YieldableError`, so - * `yield* new MyError(...)`, `Effect.fail(myError)`, and `instanceof` all keep - * working through the facade — the runtime `_X` is the real error class (the - * facade `X extends ...(_X)` inherits its prototype), and the type reflects it. - * Nothing is lost vs the underlying error class. - */ -export interface OpaqueErrorFacadeClass< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} -> extends - S.Bottom< - Self, - Encoded, - DecodingServices, - EncodingServices, - SchemaAST.AST, - S.Codec, - MakeIn, - Self, - readonly [], - MakeIn - > -{ - // YieldableError (not Self) on the constructed instance — like class-schema OpaqueFacade's - // `new(): Brand`, the data type comes from the declaration-merged `interface X`, - // so `Self` must NOT appear here (would recurse). Merging `X & YieldableError` - // makes `yield* new X()` / `Effect.fail` / `instanceof` work without losing data. - new(...args: OpaqueFacadeConstructorArgs): Cause.YieldableError & Brand - readonly copy: ReturnType Self>> - readonly identifier: string -} - -export function OpaqueErrorFacadeClass< - Self, - Encoded, - MakeIn, - DecodingServices = never, - EncodingServices = DecodingServices, - Brand = {} ->() { - return >( - schema: SchemaS - ): - & OpaqueErrorFacadeClass - & OpaqueFacadeStatics => - { - type FacadeSchema = - & OpaqueErrorFacadeClass - & OpaqueFacadeStatics - - if (isClassSchemaConstructor(schema)) { - return schema as SchemaS & FacadeSchema - } - - throw new TypeError("OpaqueErrorFacadeClass requires a class schema") - } -} - -/** - * Workflow-compatible struct facade. - * - * A top-level `const X = S.Struct(...)` / `S.TaggedStruct(...)` schema model whose `.d.ts` - * is produced by the schema-aware declaration emitter is retyped from the giant inline - * `S.Struct<{ ...fields... }>` to this facade. - * - * Unlike {@link OpaqueFacade}, `StructFacade` **extends effect-app's `Struct`**, so the - * value is still a real (effect-app) struct schema: it satisfies `Workflow.AnyStructSchema` and - * the `Struct` reconstruction the workflow machinery performs, and `Union` / - * `.fields.x` keep working. It MUST be effect-app's `Struct` (not effect core's) — scanner - * models are effect-app `S.Struct`, so a facade built on `effect/Schema`'s `Struct` is not - * assignable where an effect-app struct is expected. At the same time the type-level members - * (`Type` / `Encoded` / make-input / services) are pinned to the named interfaces the emitter - * generates in `namespace X`, so consumers reference those by name instead of re-deriving them - * from `Fields` on every use. `Fields` is the model's own generated `X.Fields` interface — a - * single materialization of the (expensive) field shape that every reference site points at. - */ -export type StructFacade< - Self, - Encoded, - MakeIn, - DecodingServices, - EncodingServices, - Fields extends S.Struct.Fields -> = - & Omit, "Type" | "Encoded" | "~type.make.in" | "DecodingServices" | "EncodingServices"> - & { - readonly "Type": Self - readonly "Encoded": Encoded - readonly "~type.make.in": MakeIn - readonly "DecodingServices": DecodingServices - readonly "EncodingServices": EncodingServices - } diff --git a/packages/effect-app/src/Schema/FastCheck.ts b/packages/effect-app/src/Schema/FastCheck.ts deleted file mode 100644 index 0c01c89aa..000000000 --- a/packages/effect-app/src/Schema/FastCheck.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type * as fc from "fast-check" - -export type { Arbitrary } from "fast-check" -type FC = typeof fc - -export type { FC } - -export const ALPHABET = "abcdefghijklmnopqrstuvwxyz" -export const ALPHABET_UC = ALPHABET.toUpperCase() - -// uppercase A-Z -const UC_char = (fc: Pick) => fc.integer({ min: 0x41, max: 0x5a }).map(String.fromCharCode) -export const UC_alphabet = (min: number, max: number) => (fc: FC) => - fc.array(UC_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join("")) -// lowercase A-Z -const LC_char = (fc: Pick) => fc.integer({ min: 0x61, max: 0x7a }).map(String.fromCharCode) -export const LC_alphabet = (min: number, max: number) => (fc: Pick) => - fc.array(LC_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join("")) - -// numbers 0-9 -const N_char = (fc: Pick) => fc.integer({ min: 0x30, max: 0x39 }).map(String.fromCharCode) -export const Numbers = (min: number, max: number) => (fc: Pick) => - fc.array(N_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join("")) diff --git a/packages/effect-app/src/Schema/Methods.ts b/packages/effect-app/src/Schema/Methods.ts deleted file mode 100644 index 2da6a7a93..000000000 --- a/packages/effect-app/src/Schema/Methods.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type ReadMethods = "GET" -export type WriteMethods = "POST" | "PUT" | "PATCH" | "DELETE" - -export type Rest = ReadMethods | WriteMethods - -export type System = "OPTIONS" | "HEAD" | "TRACE" - -export type All = Rest | System diff --git a/packages/effect-app/src/Schema/SchemaParser.ts b/packages/effect-app/src/Schema/SchemaParser.ts deleted file mode 100644 index 5b0fe3265..000000000 --- a/packages/effect-app/src/Schema/SchemaParser.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as SchemaParser from "effect/SchemaParser" -import { withDefaultParseOptions } from "./ext.ts" - -export * from "effect/SchemaParser" - -export const decodeEffectConcurrently: typeof SchemaParser.decodeEffect = withDefaultParseOptions( - SchemaParser.decodeEffect -) - -export const decodeUnknownEffectConcurrently: typeof SchemaParser.decodeUnknownEffect = withDefaultParseOptions( - SchemaParser.decodeUnknownEffect -) diff --git a/packages/effect-app/src/Schema/SpecialJsonSchema.ts b/packages/effect-app/src/Schema/SpecialJsonSchema.ts deleted file mode 100644 index e99d1cd88..000000000 --- a/packages/effect-app/src/Schema/SpecialJsonSchema.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * SpecialJsonSchema — A variant of Schema.toJsonSchemaDocument that - * post-processes the output (e.g. flattens simple allOf). - */ -import type * as JsonSchema from "effect/JsonSchema" -import * as Schema from "effect/Schema" - -/** - * Converts a schema to a JSON Schema Document (draft-2020-12), with - * post-processing that flattens simple allOf entries. - */ -export function specialJsonSchemaDocument( - schema: Schema.Top, - options?: Schema.ToJsonSchemaOptions -): JsonSchema.Document<"draft-2020-12"> { - const jd = Schema.toJsonSchemaDocument(schema, options) - const processedDefs: JsonSchema.Definitions = {} - for (const [key, def] of Object.entries(jd.definitions)) { - processedDefs[key] = postProcessJsonSchema(def) - } - return { - dialect: "draft-2020-12", - schema: postProcessJsonSchema(jd.schema), - definitions: processedDefs - } -} - -/** - * Flattens `allOf` entries into the parent when the parent already has a - * `type` and every `allOf` entry is a plain constraint object (no `$ref`, - * no `type`). Merged properties from `allOf` entries win on conflict. - */ -export function flattenSimpleAllOf(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") return obj - - if (globalThis.Array.isArray(obj)) { - return obj.map(flattenSimpleAllOf) - } - - const record = obj as Record - const result: Record = {} - for (const [key, value] of Object.entries(record)) { - result[key] = flattenSimpleAllOf(value) - } - - if (result["type"] && globalThis.Array.isArray(result["allOf"])) { - const allOf = result["allOf"] as Array> - const canFlatten = allOf.every((entry) => - typeof entry === "object" && entry !== null && !("$ref" in entry) && !("type" in entry) - ) - if (canFlatten) { - const { allOf: _, ...rest } = result - const merged: Record = { ...rest } - for (const entry of allOf) { - Object.assign(merged, entry) - } - return merged - } - } - - return result -} - -/** - * Recursively removes `additionalProperties: false` from JSON Schema objects. - * Only removes when the value is exactly `false` -- other values are left intact. - */ -export function removeAdditionalPropertiesFalse(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") return obj - - if (globalThis.Array.isArray(obj)) { - return obj.map(removeAdditionalPropertiesFalse) - } - - const record = obj as Record - const result: Record = {} - for (const [key, value] of Object.entries(record)) { - if (key === "additionalProperties" && value === false) continue - result[key] = removeAdditionalPropertiesFalse(value) - } - - return result -} - -/** - * Flattens nested `anyOf` entries: if an anyOf entry is itself just `{ anyOf: [...] }` - * with no other keys, its children are inlined. If only one item remains, the anyOf - * wrapper is removed entirely. - */ -export function flattenNestedAnyOf(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") return obj - if (globalThis.Array.isArray(obj)) return obj.map(flattenNestedAnyOf) - - const record = obj as Record - const result: Record = {} - for (const [key, value] of Object.entries(record)) { - result[key] = flattenNestedAnyOf(value) - } - - if (globalThis.Array.isArray(result["anyOf"])) { - const anyOf = result["anyOf"] as Array - const flattened: Array = [] - for (const entry of anyOf) { - if ( - typeof entry === "object" - && entry !== null - && !globalThis.Array.isArray(entry) - && "anyOf" in entry - && Object.keys(entry).length === 1 - && globalThis.Array.isArray((entry as Record)["anyOf"]) - ) { - flattened.push(...(entry as Record)["anyOf"] as Array) - } else { - flattened.push(entry) - } - } - if (flattened.length === 1) { - const { anyOf: _, ...rest } = result - const single = flattened[0] - if (typeof single === "object" && single !== null && !globalThis.Array.isArray(single)) { - return { ...rest, ...single } - } - return single - } - result["anyOf"] = flattened - } - - return result -} - -/** - * Applies JSON Schema post-processing: flattens simple allOf, - * flattens nested anyOf, then strips additionalProperties: false. - */ -export function postProcessJsonSchema(obj: JsonSchema.JsonSchema): JsonSchema.JsonSchema { - return removeAdditionalPropertiesFalse(flattenNestedAnyOf(flattenSimpleAllOf(obj))) as JsonSchema.JsonSchema -} diff --git a/packages/effect-app/src/Schema/SpecialOpenApi.ts b/packages/effect-app/src/Schema/SpecialOpenApi.ts deleted file mode 100644 index 44c88e372..000000000 --- a/packages/effect-app/src/Schema/SpecialOpenApi.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec - * and applies JSON Schema post-processing (null removal, allOf flattening). - * - * When `OpenApi.fromApi` generates the spec, different AST nodes sharing the - * same identifier can produce duplicate entries (e.g. "X" and "X1") in - * `components.schemas`. This module provides a transform function that - * collapses those duplicates, rewrites all `$ref` pointers accordingly, - * and post-processes schemas for better codegen compatibility. - * - * Usage with the OpenApi `Transform` annotation: - * - * ```ts - * import { OpenApi } from "effect/http-api" - * import { deduplicateOpenApiSchemas } from "./SpecialOpenApi.ts" - * - * const api = HttpApi.make("myApi") - * .pipe(HttpApi.annotateContext(OpenApi.annotations({ transform: deduplicateOpenApiSchemas }))) - * ``` - */ - -import { postProcessJsonSchema } from "./SpecialJsonSchema.ts" - -/** - * Deduplicates `components.schemas` entries in an OpenAPI spec. - * - * Entries sharing the same base identifier (e.g. "X" and "X1") whose JSON - * representations are identical are collapsed into a single canonical entry, - * and all `$ref` pointers throughout the spec are rewritten to point to - * the canonical key. - * - * Designed to be used as the `transform` option in `OpenApi.annotations`. - */ -export function deduplicateOpenApiSchemas( - spec: Record -): Record { - const components = spec["components"] as Record | undefined - if (!components) return spec - const schemas = components["schemas"] as Record | undefined - if (!schemas) return spec - - const keys = Object.keys(schemas) - if (keys.length === 0) return spec - - // Group keys by base identifier (strip trailing digits) - const groups = new Map>() - for (const key of keys) { - const base = getBaseIdentifier(key) - const fingerprint = JSON.stringify(schemas[key]) - const group = groups.get(base) - if (group === undefined) { - groups.set(base, [{ key, fingerprint }]) - } else { - group.push({ key, fingerprint }) - } - } - - // Build remapping from duplicate keys to canonical keys - const remapping = new Map() - for (const [, group] of groups) { - if (group.length <= 1) continue - const seen = new Map() // fingerprint -> canonical key - for (const entry of group) { - const canonical = seen.get(entry.fingerprint) - if (canonical !== undefined) { - remapping.set(entry.key, canonical) - } else { - seen.set(entry.fingerprint, entry.key) - } - } - } - - if (remapping.size === 0) return postProcessJsonSchema(spec) - - // Build new schemas object without duplicates - const newSchemas: Record = {} - for (const key of keys) { - if (!remapping.has(key)) { - newSchemas[key] = schemas[key] - } - } - - // Deep clone the spec, replace schemas, and rewrite all $ref pointers - const newSpec = structuredClone(spec) - newSpec["components"]["schemas"] = newSchemas - rewriteRefs(newSpec, remapping) - - return postProcessJsonSchema(newSpec) -} - -/** - * Extracts the base identifier from a schema key by stripping trailing - * digits appended by the gen() function. - * E.g. "X1" -> "X", "X" -> "X", "MyType2" -> "MyType" - */ -function getBaseIdentifier(key: string): string { - const match = key.match(/^(.+?)(\d+)$/) - return match ? match[1]! : key -} - -/** - * Recursively rewrites `$ref` values in a JSON object tree. - * Mutates the object in-place (caller should pass a deep clone). - */ -function rewriteRefs(obj: any, remapping: Map): void { - if (obj === null || typeof obj !== "object") return - - if (Array.isArray(obj)) { - for (const item of obj) { - rewriteRefs(item, remapping) - } - return - } - - if (typeof obj.$ref === "string") { - // OpenAPI refs look like "#/components/schemas/X1" - const prefix = "#/components/schemas/" - if (obj.$ref.startsWith(prefix)) { - const refKey = obj.$ref.slice(prefix.length) - const canonical = remapping.get(refKey) - if (canonical !== undefined) { - obj.$ref = prefix + canonical - } - } - } - - for (const value of Object.values(obj)) { - rewriteRefs(value, remapping) - } -} diff --git a/packages/effect-app/src/Schema/brand.ts b/packages/effect-app/src/Schema/brand.ts deleted file mode 100644 index bd4275112..000000000 --- a/packages/effect-app/src/Schema/brand.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -import * as B from "effect/Brand" -import type * as Option from "effect/Option" -import type * as Result from "effect/Result" -import * as S from "effect/Schema" - -export interface Constructor> { - /** - * Constructs a branded type from a value of type `A`, throwing an error if - * the provided `A` is not valid. - */ - (args: Unbranded): A - /** - * Constructs a branded type from a value of type `A`, returning `Some` - * if the provided `A` is valid, `None` otherwise. - */ - option(args: Unbranded): Option.Option - /** - * Constructs a branded type from a value of type `A`, returning `Result.succeed` - * if the provided `A` is valid, `Result.fail` otherwise. - */ - result(args: Unbranded): Result.Result - /** - * Attempts to refine the provided value of type `A`, returning `true` if - * the provided `A` is valid, `false` otherwise. - */ - is(a: Unbranded): a is Unbranded & A -} - -type BrandAnnotations = S.Annotations.Filter - -export interface BrandedSchema> extends - S.Bottom< - C, - S["Encoded"], - S["DecodingServices"], - S["EncodingServices"], - S["ast"], - BrandedSchema, - S["~type.make.in"], - S["Iso"], - S["~type.parameters"], - C, - S["~type.mutability"], - S["~type.optionality"], - S["~type.constructor.default"], - S["~encoded.mutability"], - S["~encoded.optionality"] - > -{} - -export const fromBrand = >( - _constructor: Constructor, - options?: BrandAnnotations -) => -(self: Self): BrandedSchema => { - const branded = self.pipe(S.brand("Brand")) - return (options ? branded.pipe(S.annotate(options)) : branded) as BrandedSchema -} - -export type Brands

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

- : never - -export type Unbranded

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

> = { -// [K in keyof P[B.BrandTypeId]]: K extends string | symbol ? { -// readonly [k in K]: k -// } -// : never -// }[keyof P[B.BrandTypeId]] -// export type UnionToIntersection3 = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I -// : never - -/** Union of core `Schema.Date` (Date objects) and string-encoded `DateFromString`, with default helpers. */ -export const inputDate = extendM( - S.Union([S.Date, DateFromString]), - (s) => ({ - /** - * Construction-only default `new Date()`. Applied only when the field is - * omitted from `.make(...)` input. NOT applied during decode — cannot be - * used to JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => new globalThis.Date()))), - /** - * Decode-time default `new Date()`. **Discouraged for persisted data:** a - * missing field may be data corruption, not an old-shape document; - * silently substituting `new Date()` hides the problem. Prefer an - * explicit, preferably versioned migration over a decode-time fallback. - * See file-level note. - */ - withDecodingDefaultType: s.pipe(S.withDecodingDefaultType(Effect.sync(() => new globalThis.Date()))) - }) -) - -export interface UnionBrand {} - -// TODO: v4 migration — makeOpt used internal PropertySignature types that are removed in v4 -// Simplified to use v4's S.optional / S.optionalKey directly -export function makeOptional( - t: NER -): { - [K in keyof NER]: NER[K] extends S.Top ? ReturnType> : any -} { - return Struct.keys(t).reduce((prev, cur) => { - prev[cur] = S.optional(t[cur] as any) - return prev - }, {} as any) -} - -// `Omit` erases the polymorphic `this` of "this-returning" methods (`annotate`, `annotateKey`, -// `check`, `rebuild`): extracting them through a mapped type binds their `this["Rebuild"]` back to -// the original `T`, not to this intersection. So overriding just `Rebuild` above is not enough, those -// four methods have to be redeclared here with a concrete return type instead of `this["Rebuild"]`. -export type DropConstructorDefault = - & Omit< - T, - "~type.constructor.default" | "Rebuild" | "annotate" | "annotateKey" | "check" | "rebuild" - > - & { - readonly "~type.constructor.default": "no-default" - readonly "Rebuild": DropConstructorDefault - annotate(annotations: S.Annotations.Bottom): DropConstructorDefault - annotateKey(annotations: S.Annotations.Key): DropConstructorDefault - check( - ...checks: readonly [SchemaAST.Check, ...Array>] - ): DropConstructorDefault - rebuild(ast: T["ast"]): DropConstructorDefault - } - -// `replaceContext` is `@internal` in `effect/SchemaAST` (stripped from its public types, though the -// export itself still ships in the compiled JS). Effect's own `withConstructorDefault` goes through the -// same function internally, so this mirrors an existing code path rather than reaching for something -// novel, but it's still an internal API that could move or disappear without a signal. -const SchemaASTInternal = SchemaAST as unknown as { - readonly replaceContext: (ast: A, context: SchemaAST.Context | undefined) => A -} - -export interface DropConstructorDefaultLambda extends Struct.Lambda { - (self: T): DropConstructorDefault - readonly "~lambda.out": DropConstructorDefault -} - -/** - * A `Struct.Lambda` that removes a field's `withConstructorDefault`, if any. - * Compose with `Struct.pick`/`Struct.omit` via `Struct.map` to derive a - * partial-update schema from a "create" schema without inheriting defaults - * that would resurrect omitted fields in `.make(...)` output: - * - * ```ts - * import { flow } from "effect/Function" - * import * as Struct from "effect/Struct" - * import * as S from "effect-app/Schema" - * - * const Person = S.Struct({ - * id: S.String, - * name: S.String.pipe(S.optionalKey, S.withConstructorDefault(() => "")) - * }) - * - * const UpdatePerson = Person.mapFields( - * flow(Struct.omit(["id"]), Struct.map(S.dropConstructorDefault)) - * ) - * ``` - */ -export const dropConstructorDefault: DropConstructorDefaultLambda = Struct.lambda( - (schema) => { - const context = schema.ast.context - if (!context?.constructorDefault) return schema - return schema.rebuild( - SchemaASTInternal.replaceContext( - schema.ast, - new SchemaAST.Context(context.isOptional, context.isMutable, undefined, context.annotations) - ) - ) - } -) - -export function makeExactOptional( - t: NER -): { - [K in keyof NER]: NER[K] extends S.Top ? DropConstructorDefault>> - : any -} { - return Struct.keys(t).reduce((prev, cur) => { - prev[cur] = dropConstructorDefault(S.optionalKey(t[cur] as any)) - return prev - }, {} as any) -} - -/** A version of transform which is only a one way mapping of From->To */ -export const transformTo = ( - from: From, - to: To, - decode: ( - fromA: From["Type"], - options: SchemaAST.ParseOptions - ) => To["Encoded"] -) => - from.pipe( - S.decodeTo( - to, - SchemaTransformation.transformEffect({ - decode: (input: any, options: any) => Effect.sync(() => decode(input, options)), - encode: (i: any) => - Effect.fail( - new SchemaIssue.Forbidden( - { message: "One way schema transformation, encoding is not allowed" }, - i - ) - ) - }) - ) - ) - -/** A version of transformEffect which is only a one way mapping of From->To */ -export const transformToOrFail = ( - from: From, - to: To, - decode: ( - fromA: From["Type"], - options: SchemaAST.ParseOptions - ) => Effect.Effect -) => - from.pipe( - S.decodeTo( - to, - SchemaTransformation.transformEffect({ - decode, - encode: (i: any) => - Effect.fail( - new SchemaIssue.Forbidden( - { message: "One way schema transformation, encoding is not allowed" }, - i - ) - ) - }) - ) - ) - -export const provide: { - (context: Context.Context): (self: Self) => ProvidedCodec - (self: Self, context: Context.Context): ProvidedCodec -} = Function.dual(2, (self: Self, context: Context.Context): ProvidedCodec => { - const prov = Effect.provide(context) - return self.pipe( - S.middlewareDecoding((effect) => prov(effect)), - S.middlewareEncoding((effect) => prov(effect)) - ) -}) -export const contextFromServices = Effect.fnUntraced(function*< - Self extends S.Top, - Tags extends ReadonlyArray> ->(self: Self, ...services: Tags) { - const context: Context.Context> = Context.pick(...services)( - yield* Effect.context>() - ) - return provide(self, context) -}) as < - Self extends S.Top, - Tags extends ReadonlyArray> ->( - self: Self, - ...services: Tags -) => Effect.Effect< - ProvidedCodec>, - never, - Context.Service.Identifier -> diff --git a/packages/effect-app/src/Schema/moreStrings.ts b/packages/effect-app/src/Schema/moreStrings.ts deleted file mode 100644 index ece539d1e..000000000 --- a/packages/effect-app/src/Schema/moreStrings.ts +++ /dev/null @@ -1,390 +0,0 @@ -/** - * Branded string ID schemas with `.withConstructorDefault` extensions. - * - * Each `.withConstructorDefault` here is **only** applied when the field is - * omitted during construction (`.make(...)`). It is **not** applied during - * decode and therefore cannot be used to JIT-migrate database fields. - * - * For persisted data, prefer an explicit, preferably versioned migration - * over decode-time fallbacks. See `./ext.ts` for the full policy note. - */ -import type { Refinement } from "effect-app/Function" -import { extendM } from "effect-app/utils" -import type * as B from "effect/Brand" -import * as Effect from "effect/Effect" -import { pipe } from "effect/Function" -import * as S from "effect/Schema" -import * as Getter from "effect/SchemaGetter" -import * as SchemaIssue from "effect/SchemaIssue" -import type { Simplify } from "effect/Types" -import { customRandom, nanoid, urlAlphabet } from "nanoid" -import validator from "validator" -import type * as SchemaAST from "../SchemaAST.ts" -import { type BrandedSchema, fromBrand, nominal } from "./brand.ts" -import { withDefaultMake } from "./ext.ts" -import { type B as SchemaB } from "./schema.ts" -import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.ts" - -const nonEmptyString = S.NonEmptyString - -/** - * A string that is at least 1 character long and a maximum of 50. - */ -export interface NonEmptyString50Brand extends Simplify & NonEmptyString64Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 50. - */ -export type NonEmptyString50 = string & NonEmptyString50Brand - -/** - * A string that is at least 1 character long and a maximum of 50. - */ -export interface NonEmptyString50Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString50 -} -export const NonEmptyString50: NonEmptyString50Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(50)), - fromBrand(nominal(), { - identifier: "NonEmptyString50", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 1 character long and a maximum of 64. - */ -export interface NonEmptyString64Brand extends Simplify & NonEmptyString80Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 64. - */ -export type NonEmptyString64 = string & NonEmptyString64Brand - -/** - * A string that is at least 1 character long and a maximum of 64. - */ -export interface NonEmptyString64Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString64 -} -export const NonEmptyString64: NonEmptyString64Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(64)), - fromBrand(nominal(), { - identifier: "NonEmptyString64", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 1 character long and a maximum of 80. - */ -export interface NonEmptyString80Brand extends Simplify & NonEmptyString100Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 80. - */ -export type NonEmptyString80 = string & NonEmptyString80Brand - -/** - * A string that is at least 1 character long and a maximum of 80. - */ - -export interface NonEmptyString80Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString80 -} -export const NonEmptyString80: NonEmptyString80Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(80)), - fromBrand(nominal(), { - identifier: "NonEmptyString80", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 1 character long and a maximum of 100. - */ -export interface NonEmptyString100Brand extends Simplify & NonEmptyString255Brand> {} - -/** - * A string that is at least 1 character long and a maximum of 100. - */ -export type NonEmptyString100 = string & NonEmptyString100Brand - -/** - * A string that is at least 1 character long and a maximum of 100. - */ -export interface NonEmptyString100Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString100 -} -export const NonEmptyString100: NonEmptyString100Schema = nonEmptyString.pipe( - S.check(S.isMaxLength(100)), - fromBrand(nominal(), { - identifier: "NonEmptyString100", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 3 character long and a maximum of 255. - */ -export interface Min3String255Brand extends Simplify & NonEmptyString255Brand> {} - -/** - * A string that is at least 3 character long and a maximum of 255. - */ -export type Min3String255 = string & Min3String255Brand - -/** - * A string that is at least 3 character long and a maximum of 255. - */ -export interface Min3String255Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): Min3String255 -} -export const Min3String255: Min3String255Schema = pipe( - S.String, - S.check(S.isMinLength(3), S.isMaxLength(255)), - fromBrand(nominal(), { - identifier: "Min3String255", - jsonSchema: {} - }), - withDefaultMake -) - -/** - * A string that is at least 6 characters long and a maximum of 50. - */ -export interface StringIdBrand extends Simplify & NonEmptyString50Brand> {} - -/** - * A string that is at least 6 characters long and a maximum of 50. - */ -export type StringId = string & StringIdBrand - -const minLength = 6 -const maxLength = 50 -const nanoidSize = 21 -const nanoidByteLength = 10 * nanoidSize - -const StringIdString = pipe( - S.String, - S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)), - fromBrand(nominal(), { - identifier: "StringId", - jsonSchema: {} - }) -) - -const nanoidFromBytes = (bytes: Uint8Array) => - customRandom(urlAlphabet, nanoidSize, (size) => bytes.subarray(0, size))() as StringId - -// JSON stays a branded string (toCodec). Generation uses toCodecArbitrary — the native -// replacement for fast-check `toArbitrary` — same as the old StringIdArb: -// uint8Array(210).map(bytes => customRandom(urlAlphabet, 21, …)). -const StringIdSchemaBase = S.declareConstructor()( - [], - () => (input, ast, options) => - S.is(StringIdString)(input) - ? Effect.succeed(input) - : Effect.fail(new SchemaIssue.InvalidType(ast, input, options)), - { - identifier: "StringId", - expected: "StringId", - toCodec: () => - S.link()(StringIdString, { - decode: Getter.passthrough(), - encode: Getter.passthrough() - }), - toCodecArbitrary: () => - S.link()( - S.Uint8Array.check(S.isMinLength(nanoidByteLength), S.isMaxLength(nanoidByteLength)), - { - decode: Getter.transform(nanoidFromBytes), - encode: Getter.forbiddenEncoding - } - ) - } -) -const makeStringId = (s?: string): StringId => - s !== undefined ? S.decodeSync(StringIdString)(s) : nanoid() as unknown as StringId -/** - * A string that is at least 6 characters long and a maximum of 50. - * - * `.withConstructorDefault` => fresh `nanoid()` (construction-only; not - * applied during decode — see file-level note). - */ -export interface StringIdSchema extends S.declareConstructor { - (i: string, options?: SchemaAST.ParseOptions): StringId - /** Generate fresh `nanoid()`-shaped `StringId`. */ - make(): StringId - /** Construct a `StringId` from a known string (validated via decodeSync). */ - make(input: string, options?: S.MakeOptions): StringId - readonly withConstructorDefault: S.withConstructorDefault< - S.declareConstructor - > -} -export const StringId: StringIdSchema = extendM( - StringIdSchemaBase, - (s) => ({ - make: makeStringId, - /** - * Construction-only default: fresh `nanoid()`-shaped `StringId`. Applied - * only when the field is omitted from `.make(...)` input. NOT applied - * during decode — cannot be used to JIT-migrate database fields. See - * file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(makeStringId))) - }) -) - .pipe(withDefaultMake) - -/** - * Build a `StringId` schema whose values are required to start with a fixed - * `prefix` (joined with `separator`, default `-`). - * - * The returned schema exposes `.withConstructorDefault` that mints a fresh - * prefixed id. Construction-only — not applied during decode; see file-level - * note. - */ -export function prefixedStringId() { - return ( - prefix: Prefix, - name: string, - separator?: Separator - ) => { - type FullPrefix = `${Prefix}${Separator}` - const pref = `${prefix}${separator ?? "-"}` as FullPrefix - const PrefixedString = StringIdString.pipe( - S.refine((x): x is Type => x.startsWith(pref), { identifier: name }) - ) - // Pre-rc.114: StringIdArb().map(x => pref + x.substring(0, 50 - pref.length)) - const s = S.declareConstructor()( - [], - () => (input, ast, options) => - S.is(PrefixedString)(input) - ? Effect.succeed(input) - : Effect.fail(new SchemaIssue.InvalidType(ast, input, options)), - { - identifier: name, - expected: name, - toCodec: () => - S.link()(PrefixedString, { - decode: Getter.passthrough(), - encode: Getter.passthrough() - }), - toCodecArbitrary: () => - S.link()( - S.Uint8Array.check(S.isMinLength(nanoidByteLength), S.isMaxLength(nanoidByteLength)), - { - decode: Getter.transform((bytes) => - (pref + nanoidFromBytes(bytes).substring(0, maxLength - pref.length)) as Type - ), - encode: Getter.forbiddenEncoding - } - ) - } - ) - const schema = s.pipe(withDefaultMake) - const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Type - - return extendM( - schema, - (ex): PrefixedStringUtils => ({ - make, - /** - * Automatically adds the prefix. - */ - unsafeFrom: (str: string) => ex(pref + str), - /** - * Must provide a literal string starting with prefix. - */ - prefixSafe: (str: `${Prefix}${Separator}${REST}`) => ex(str), - prefix, - /** - * Construction-only default: fresh prefixed id. Applied only when - * the field is omitted from `.make(...)` input. NOT applied during - * decode — cannot be used to JIT-migrate database fields. See - * file-level note. - */ - withConstructorDefault: schema.pipe( - S.withConstructorDefault & S.WithoutConstructorDefault>( - Effect.sync(make) - ) - ) - }) - ) - } -} - -/** - * Build a branded `StringId` schema for the given branded `Id` type. - * - * Exposes `.withConstructorDefault` that mints a fresh `nanoid()`-shaped id. - * Construction-only — not applied during decode; see file-level note. - */ -export const brandedStringId = >(): BrandedStringIdSchema => - withDefaultMake( - Object.assign(Object.create(StringId), StringId) as BrandedStringIdSchema - ) - -export interface PrefixedStringUtils< - Type extends StringId, - Prefix extends string, - Separator extends string -> { - readonly make: () => Type - readonly unsafeFrom: (str: string) => Type - prefixSafe: (str: `${Prefix}${Separator}${REST}`) => Type - readonly prefix: Prefix - /** - * Construction-only default: fresh prefixed id. Applied only when the - * field is omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See file-level note. - */ - readonly withConstructorDefault: S.withConstructorDefault & S.WithoutConstructorDefault> -} - -export interface BrandedStringIdSchema> extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): Id - /** Generate fresh `nanoid()`-shaped id (inherited from `StringId`). */ - make(): Id - /** Construct an `Id` from a known string (validated via decodeSync). */ - make(input: string, options?: S.MakeOptions): Id - readonly withConstructorDefault: S.withConstructorDefault> -} - -export interface UrlBrand extends Simplify & NonEmptyStringBrand> {} - -export type Url = string & UrlBrand - -const isUrl: Refinement = (s: string): s is Url => { - return validator.default.isURL(s, { require_tld: false }) -} - -export interface UrlSchema extends S.refine { - (i: string, options?: SchemaAST.ParseOptions): Url -} -export const Url: UrlSchema = S - .String - .pipe( - S.annotate({ - title: "Url", - format: "uri" - }), - S.refine(isUrl, { - identifier: "Url", - jsonSchema: { format: "uri" }, - // Native Arbitrary otherwise emits strings that `isURL({ require_tld: false })` - // accepts (`"R"`, `"prototype"`). Restore the old fast-check `fc.webUrl()` shape. - arbitraryConstraint: { - patterns: [{ - source: "^https://[a-z]{3,12}\\.(?:com|net|org|de)(?:/[a-z0-9-]{0,16})?$", - flags: "" - }] - } - }), - withDefaultMake - ) diff --git a/packages/effect-app/src/Schema/numbers.ts b/packages/effect-app/src/Schema/numbers.ts deleted file mode 100644 index 020be91dd..000000000 --- a/packages/effect-app/src/Schema/numbers.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Numeric brand schemas with `.withConstructorDefault` extensions. - * - * Each `.withConstructorDefault` here is **only** applied when the field is - * omitted during construction (`.make(...)`). It is **not** applied during - * decode and therefore cannot be used to JIT-migrate database fields. - * - * For persisted data, prefer an explicit, preferably versioned migration - * over decode-time fallbacks. See `./ext.ts` for the full policy note. - */ -import { extendM } from "effect-app/utils" -import * as Effect from "effect/Effect" -import * as Order from "effect/Order" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import type * as SchemaAST from "../SchemaAST.ts" -import { type BrandedSchema, fromBrand, nominal } from "./brand.ts" -import { withDefaultMake } from "./ext.ts" -import { type B } from "./schema.ts" - -export interface PositiveIntBrand - extends Simplify & NonNegativeIntBrand & PositiveNumberBrand> -{} -export type PositiveInt = number & PositiveIntBrand -/** Positive integer. `.withConstructorDefault` => `1` (construction-only). */ -export interface PositiveIntSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): PositiveInt - readonly withConstructorDefault: S.withConstructorDefault> -} -export const PositiveInt: PositiveIntSchema = extendM( - S.Int.pipe( - S.check(S.isGreaterThan(0)), - fromBrand(nominal(), { identifier: "PositiveInt", jsonSchema: {} }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `1`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) - }) -) - -export interface NonNegativeIntBrand extends Simplify & IntBrand & NonNegativeNumberBrand> {} -export type NonNegativeInt = number & NonNegativeIntBrand -/** Non-negative integer. `.withConstructorDefault` => `0` (construction-only). */ -export interface NonNegativeIntSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): NonNegativeInt - readonly withConstructorDefault: S.withConstructorDefault> -} -export const NonNegativeInt: NonNegativeIntSchema = extendM( - S.Int.pipe( - S.check(S.isGreaterThanOrEqualTo(0)), - fromBrand(nominal(), { - identifier: "NonNegativeInt", - jsonSchema: {} - }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) - }) -) - -export interface IntBrand extends Simplify> {} -export type Int = number & IntBrand -/** Integer. `.withConstructorDefault` => `0` (construction-only). */ -export interface IntSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): Int - readonly withConstructorDefault: S.withConstructorDefault> -} -export const Int: IntSchema = extendM( - S.Int.pipe(fromBrand(nominal(), { identifier: "Int", jsonSchema: {} }), withDefaultMake), - (s) => ({ - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) - }) -) - -export interface PositiveNumberBrand extends Simplify & NonNegativeNumberBrand> {} -export type PositiveNumber = number & PositiveNumberBrand -/** Positive finite number. `.withConstructorDefault` => `1` (construction-only). */ -// Generation-only: native Arbitrary otherwise samples up to Number.MAX_VALUE, so derived totals -// (amount x weight, sums) overflow to Infinity. Validation and JSON Schema are unaffected. -const realisticMaximum = 1_000_000 - -export interface PositiveNumberSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): PositiveNumber - readonly withConstructorDefault: S.withConstructorDefault> -} -export const PositiveNumber: PositiveNumberSchema = extendM( - S.Finite.pipe( - S.check(S.isGreaterThan(0, { - // the full constraint: annotations replace the check default, which carries `order` + `minimum` - arbitraryConstraint: { order: Order.Number, minimum: 0, exclusiveMinimum: true, maximum: realisticMaximum } - })), - fromBrand(nominal(), { - identifier: "PositiveNumber", - jsonSchema: {} - }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `1`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(1)))) - }) -) - -export interface NonNegativeNumberBrand extends Simplify> {} -export type NonNegativeNumber = number & NonNegativeNumberBrand -/** Non-negative finite number. `.withConstructorDefault` => `0` (construction-only). */ -export interface NonNegativeNumberSchema extends BrandedSchema { - (i: number, options?: SchemaAST.ParseOptions): NonNegativeNumber - readonly withConstructorDefault: S.withConstructorDefault> -} -export const NonNegativeNumber: NonNegativeNumberSchema = extendM( - S - .Finite - .pipe( - S.check(S.isGreaterThanOrEqualTo(0, { - arbitraryConstraint: { order: Order.Number, minimum: 0, maximum: realisticMaximum } - })), - fromBrand(nominal(), { - identifier: "NonNegativeNumber", - jsonSchema: {} - }), - withDefaultMake - ), - (s) => ({ - /** - * Construction-only default `0`. Applied only when the field is omitted - * from `.make(...)` input. NOT applied during decode — cannot be used to - * JIT-migrate database fields. See file-level note. - */ - withConstructorDefault: s.pipe(S.withConstructorDefault(Effect.sync(() => s(0)))) - }) -) - -/** @deprecated Not an actual decimal */ -export const NonNegativeDecimal = NonNegativeNumber -/** @deprecated Not an actual decimal */ -export type NonNegativeDecimal = NonNegativeNumber - -/** @deprecated Not an actual decimal */ -export const PositiveDecimal = PositiveNumber -/** @deprecated Not an actual decimal */ -export type PositiveDecimal = PositiveNumber diff --git a/packages/effect-app/src/Schema/phoneNumber.ts b/packages/effect-app/src/Schema/phoneNumber.ts deleted file mode 100644 index 93533734f..000000000 --- a/packages/effect-app/src/Schema/phoneNumber.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Refinement } from "effect-app/Function" -import { isValidPhone } from "effect-app/validation" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import { withDefaultMake } from "./ext.ts" -import type { B } from "./schema.ts" -import type { NonEmptyStringBrand } from "./strings.ts" - -export interface PhoneNumberBrand extends Simplify & NonEmptyStringBrand> {} -export type PhoneNumber = string & PhoneNumberBrand - -export const PhoneNumber = S - .String - .pipe( - S.annotate({ - title: "PhoneNumber", - description: "a phone number with at least 7 digits", - format: "phone" - }), - S.refine(isValidPhone as Refinement, { - identifier: "PhoneNumber", - description: "a phone number with at least 7 digits", - jsonSchema: { format: "phone" }, - // Native Arbitrary cannot invert `isValidPhone`; steer sampling to digit-only numbers (>= 7 digits). - arbitraryConstraint: { - patterns: [{ source: "^\\+?[1-9][0-9]{6,11}$", flags: "" }] - } - }), - withDefaultMake - ) diff --git a/packages/effect-app/src/Schema/schema.ts b/packages/effect-app/src/Schema/schema.ts deleted file mode 100644 index 5b405c8be..000000000 --- a/packages/effect-app/src/Schema/schema.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as B from "effect/Brand" -import * as P from "effect/SchemaParser" -import * as AST from "../SchemaAST.ts" - -export { AST, B, P } diff --git a/packages/effect-app/src/Schema/strings.ts b/packages/effect-app/src/Schema/strings.ts deleted file mode 100644 index 71d857dd9..000000000 --- a/packages/effect-app/src/Schema/strings.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type * as B from "effect/Brand" -import * as S from "effect/Schema" -import type { Simplify } from "effect/Types" -import type * as SchemaAST from "../SchemaAST.ts" -import { type BrandedSchema, fromBrand, nominal } from "./brand.ts" -import { withDefaultMake } from "./ext.ts" - -export type NonEmptyStringBrand = B.Brand<"NonEmptyString"> -export type NonEmptyString = string & NonEmptyStringBrand -export interface NonEmptyStringSchema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString -} -export const NonEmptyString: NonEmptyStringSchema = S - .NonEmptyString - .pipe( - fromBrand(nominal(), { - identifier: "NonEmptyString", - jsonSchema: {} - }), - withDefaultMake - ) - -export interface NonEmptyString64kBrand extends Simplify & NonEmptyStringBrand> {} -export type NonEmptyString64k = string & NonEmptyString64kBrand -export interface NonEmptyString64kSchema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString64k -} -export const NonEmptyString64k: NonEmptyString64kSchema = S - .NonEmptyString - .pipe( - S.check(S.isMaxLength(64 * 1024)), - fromBrand(nominal(), { - identifier: "NonEmptyString64k", - jsonSchema: {} - }), - withDefaultMake - ) - -export interface NonEmptyString2kBrand extends Simplify & NonEmptyString64kBrand> {} -export type NonEmptyString2k = string & NonEmptyString2kBrand -export interface NonEmptyString2kSchema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString2k -} -export const NonEmptyString2k: NonEmptyString2kSchema = S - .NonEmptyString - .pipe( - S.check(S.isMaxLength(2 * 1024)), - fromBrand(nominal(), { - identifier: "NonEmptyString2k", - jsonSchema: {} - }), - withDefaultMake - ) - -export interface NonEmptyString255Brand extends Simplify & NonEmptyString2kBrand> {} -export type NonEmptyString255 = string & NonEmptyString255Brand -export interface NonEmptyString255Schema extends BrandedSchema { - (i: string, options?: SchemaAST.ParseOptions): NonEmptyString255 -} -export const NonEmptyString255: NonEmptyString255Schema = S - .NonEmptyString - .pipe( - S.check(S.isMaxLength(255)), - fromBrand(nominal(), { - identifier: "NonEmptyString255", - jsonSchema: {} - }), - withDefaultMake - ) diff --git a/packages/effect-app/src/SchemaAST.ts b/packages/effect-app/src/SchemaAST.ts deleted file mode 100644 index 968094487..000000000 --- a/packages/effect-app/src/SchemaAST.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "effect/SchemaAST" diff --git a/packages/effect-app/src/Set.ts b/packages/effect-app/src/Set.ts deleted file mode 100644 index 23af735d2..000000000 --- a/packages/effect-app/src/Set.ts +++ /dev/null @@ -1,666 +0,0 @@ -// ets_tracing: off - -import * as Array from "effect/Array" -import type * as Equivalence from "effect/Equivalence" -import * as Option from "effect/Option" -import type * as Order from "effect/Order" -import { not } from "effect/Predicate" -import type * as Result from "effect/Result" -import * as Tuple from "effect/Tuple" -import { identity, pipe, type Predicate, type Refinement } from "./Function.ts" - -export function find_( - as: ReadonlySet, - refinement: Refinement -): B | undefined -export function find_(set: ReadonlySet, predicate: Predicate): A | undefined -export function find_(set: ReadonlySet, predicate: Predicate) { - return [...set].find(predicate) -} - -export function findFirst_( - set: ReadonlySet, - refinement: Refinement -): Option.Option -export function findFirst_(set: ReadonlySet, predicate: Predicate): Option.Option -export function findFirst_(set: ReadonlySet, predicate: Predicate): Option.Option { - return Option.fromNullishOr([...set].find(predicate)) -} - -export function findFirstMap_( - set: ReadonlySet, - f: (a: A) => Option.Option -): Option.Option { - return Array.findFirst([...set], f) -} - -export type MutableSet = globalThis.Set -export type Set = ReadonlySet - -export const empty: Set = new Set() - -/** - * The set of elements which are in both the first and second set - */ -export function intersection_(E: Equivalence.Equivalence): (l: Set, r: Set) => Set { - const elemE = elem_(E) - return (x, y) => { - if (x === empty || y === empty) { - return empty - } - const r = new Set() - x.forEach((e) => { - if (elemE(y, e)) { - r.add(e) - } - }) - return r - } -} - -/** - * The set of elements which are in both the first and second set - */ -export function intersection(E: Equivalence.Equivalence): (r: Set) => (l: Set) => Set { - const i = intersection_(E) - return (x) => (y) => i(x, y) -} - -/** - * Convert a mutable set to a readonly one - */ -export function fromMutable(s: MutableSet): Set { - return new Set(s) -} - -/** - * Convert a set to a mutable one - */ -export function toMutable(s: Set): MutableSet { - return new Set(s) -} - -// /** -// * get Show for set given Show for values -// */ -// export function getShow(S: Show): Show> { -// return { -// show: (s) => { -// let elements = "" -// s.forEach((a) => { -// elements += S.show(a) + ", " -// }) -// if (elements !== "") { -// elements = elements.substring(0, elements.length - 2) -// } -// return `new Set([${elements}])` -// } -// } -// } - -/** - * Convert a set to an Array - */ -export function toArray(O: Order.Order): (set: Set) => ReadonlyArray { - return (x) => { - const r: Array = [] - x.forEach((e) => r.push(e)) - return r.sort(O) - } -} - -/** - * Convert a set to an Array - */ -export function toArray_(x: Set, O: Order.Order): ReadonlyArray { - return toArray(O)(x) -} - -// /** -// * Get Equivalence for Setgiven Equivalence for element -// */ -// export function getEquivalence.Equivalence(E: Equivalence.Equivalence): Equivalence.Equivalence> { -// const subsetE = isSubset_(E) -// return makeEquivalence((x, y) => subsetE(x, y) && subsetE(y, x)) -// } - -interface Next { - readonly done?: boolean - readonly value: A -} - -/** - * true if one or more elements match predicate - */ -export function some(predicate: Predicate): (set: Set) => boolean { - return (set) => { - const values = set.values() - let e: Next - let found = false - while (!found && !(e = values.next() as any).done) { - found = predicate(e.value) - } - return found - } -} - -/** - * true if one or more elements match predicate - */ -export function some_(set: Set, predicate: Predicate): boolean { - return some(predicate)(set) -} - -/** - * Projects a Set through a function - */ -export function map(E: Equivalence.Equivalence): (f: (x: A) => B) => (set: Set) => Set { - const m = map_(E) - return (f) => (set) => m(set, f) -} - -/** - * Projects a Set through a function - */ -export function map_(E: Equivalence.Equivalence): (set: Set, f: (x: A) => B) => Set { - const elemE = elem_(E) - return (set, f) => { - const r = new Set() - set.forEach((e) => { - const v = f(e) - if (!elemE(r, v)) { - r.add(v) - } - }) - return r - } -} - -/** - * true if all elements match predicate - */ -export function every(predicate: Predicate): (set: Set) => boolean { - return (set) => every_(set, predicate) -} - -/** - * true if all elements match predicate - */ -export function every_(set: Set, predicate: Predicate): boolean { - return not(some(not(predicate)))(set) -} - -/** - * Map + Flatten - */ -export function chain( - E: Equivalence.Equivalence -): (f: (x: A) => Set) => (set: Set) => Set { - const c = chain_(E) - return (f) => (set) => c(set, f) -} - -/** - * Map + Flatten - */ -export function chain_( - E: Equivalence.Equivalence -): (set: Set, f: (x: A) => Set) => Set { - const elemE = elem_(E) - return (set, f) => { - const r = new Set() - set.forEach((e) => { - f(e).forEach((e) => { - if (!elemE(r, e)) { - r.add(e) - } - }) - }) - return r - } -} - -/** - * `true` if and only if every element in the first set is an element of the second set - */ -export function isSubset(E: Equivalence.Equivalence): (x: Set, y: Set) => boolean { - const i = isSubset_(E) - return (x, y) => i(y, x) -} - -/** - * `true` if and only if every element in the first set is an element of the second set - */ -export function isSubset_(E: Equivalence.Equivalence): (x: Set, y: Set) => boolean { - const elemE = elem_(E) - return (x, y) => every((a: A) => elemE(y, a))(x) -} - -/** - * Filter set values using predicate - */ -export function filter( - refinement: Refinement -): (set: Set) => Set -export function filter(predicate: Predicate): (set: Set) => Set -export function filter(predicate: Predicate): (set: Set) => Set { - return (set) => filter_(set, predicate) -} - -/** - * Filter set values using predicate - */ -export function filter_( - set: Set, - refinement: Refinement -): Set -export function filter_(set: Set, predicate: Predicate): Set -export function filter_(set: Set, predicate: Predicate): Set { - const values = set.values() - let e: Next - const r = new Set() - while (!(e = values.next() as any).done) { - const value = e.value - if (predicate(value)) { - r.add(value) - } - } - return r -} - -/** - * Partition set values using predicate - */ -export function partition( - refinement: Refinement -): (set: Set) => readonly [Set, Set] -export function partition( - predicate: Predicate -): (set: Set) => readonly [Set, Set] -export function partition( - predicate: Predicate -): (set: Set) => readonly [Set, Set] { - return (set) => partition_(set, predicate) -} - -/** - * Partition set values using predicate - */ -export function partition_( - set: Set, - refinement: Refinement -): readonly [Set, Set] -export function partition_( - set: Set, - predicate: Predicate -): readonly [Set, Set] -export function partition_( - set: Set, - predicate: Predicate -): readonly [Set, Set] { - const values = set.values() - let e: Next - const right = new Set() - const left = new Set() - while (!(e = values.next() as any).done) { - const value = e.value - if (predicate(value)) { - right.add(value) - } else { - left.add(value) - } - } - return Tuple.make(left, right) -} - -/** - * Test if a value is a member of a set - */ -export function elem_(E: Equivalence.Equivalence): (set: Set, a: A) => boolean { - return (set, a) => { - const values = set.values() - let e: Next - let found = false - while (!found && !(e = values.next() as any).done) { - found = E(a, e.value) - } - return found - } -} - -/** - * Test if a value is a member of a set - */ -export function elem(E: Equivalence.Equivalence): (a: A) => (set: Set) => boolean { - const e = elem_(E) - return (a) => (set) => e(set, a) -} - -/** - * Partition elements according to f - */ -export function partitionMap( - EB: Equivalence.Equivalence, - EC: Equivalence.Equivalence -): (f: (a: A) => Result.Result) => (set: Set) => readonly [Set, Set] { - const pm = partitionMap_(EB, EC) - return (f: (a: A) => Result.Result) => (set: Set) => pm(set, f) -} - -/** - * Partition elements according to f - */ -export function partitionMap_( - EB: Equivalence.Equivalence, - EC: Equivalence.Equivalence -): (set: Set, f: (a: A) => Result.Result) => readonly [Set, Set] { - return (set: Set, f: (a: A) => Result.Result) => { - const values = set.values() - let e: Next - const left = new Set() - const right = new Set() - const hasB = elem_(EB) - const hasC = elem_(EC) - while (!(e = values.next() as any).done) { - const v = f(e.value) - switch (v._tag) { - case "Failure": - if (!hasB(left, v.failure)) { - left.add(v.failure) - } - break - case "Success": - if (!hasC(right, v.success)) { - right.add(v.success) - } - break - } - } - return Tuple.make(left, right) - } -} - -/** - * Form the set difference (`x` - `y`) - */ -export function difference_(E: Equivalence.Equivalence): (l: Set, r: Set) => Set { - const elemE = elem_(E) - return (x, y) => filter((a: A) => !elemE(y, a))(x) -} - -/** - * Form the set difference (`x` - `y`) - */ -export function difference(E: Equivalence.Equivalence): (x: Set, y: Set) => Set { - const diff = difference_(E) - return (x, y) => diff(x, y) -} - -/** - * Reduce over the set values - */ -export function reduce( - O: Order.Order -): (b: B, f: (b: B, a: A) => B) => (fa: Set) => B { - const red = reduce_(O) - return (b, f) => (fa) => red(fa, b, f) -} - -/** - * Reduce over the set values - */ -export function reduce_( - O: Order.Order -): (fa: Set, b: B, f: (b: B, a: A) => B) => B { - const toArrayO = toArray(O) - return (fa, b, f) => toArrayO(fa).reduce(f, b) -} - -// /** -// * Fold + Map -// */ -// export function foldMap( -// O: Order.Order, -// M: Identity -// ): (f: (a: A) => M) => (fa: Set) => M { -// const fm = foldMap_(O, M) -// return (f) => (fa) => fm(fa, f) -// } - -// /** -// * Fold + Map -// */ -// export function foldMap_( -// O: Order.Order, -// M: Identity -// ): (fa: Set, f: (a: A) => M) => M { -// const toArrayO = toArray(O) -// return (fa, f) => toArrayO(fa).reduce((b, a) => M.combine(b, f(a)), M.identity) -// } - -/** - * Create a set with one element - */ -export function singleton(a: A): Set { - return new Set([a]) -} - -/** - * Insert a value into a set - */ -export function insert(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { - const i = insert_(E) - return (a) => (set) => i(set, a) -} - -/** - * Insert a value into a set - */ -export function insert_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { - const elemE = elem_(E) - return (set, a) => { - if (!elemE(set, a)) { - const r = new Set(set) - r.add(a) - return r - } else { - return set - } - } -} - -/** - * Delete a value from a set - */ -export function remove(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { - const rem = remove_(E) - return (a) => (set) => rem(set, a) -} - -/** - * Delete a value from a set - */ -export function remove_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { - return (set, a) => filter((ax: A) => !E(a, ax))(set) -} - -/** - * If element is present remove it, if not add it - */ -export function toggle(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { - const t = toggle_(E) - return (a) => (set) => t(set, a) -} - -/** - * If element is present remove it, if not add it - */ -export function toggle_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { - const elemE = elem_(E) - const removeE = remove(E) - const insertE = insert(E) - return (set, a) => (elemE(set, a) ? removeE : insertE)(a)(set) -} - -/** - * Create a set from an array - */ -export function fromArray(E: Equivalence.Equivalence): (as: ReadonlyArray) => Set { - return (as) => { - const len = as.length - const r = new Set() - const has = elem_(E) - for (let i = 0; i < len; i++) { - const a = as[i]! - if (!has(r, a)) { - r.add(a) - } - } - return r - } -} - -/** - * Set compaction, remove none - */ -export function compact(E: Equivalence.Equivalence): (fa: Set>) => Set { - return filterMap(E)(identity) -} - -/** - * Separate elements - */ -export function separate( - EE: Equivalence.Equivalence, - EA: Equivalence.Equivalence -): (fa: Set>) => readonly [Set, Set] { - return (fa) => { - const elemEE = elem_(EE) - const elemEA = elem_(EA) - const left: MutableSet = new Set() - const right: MutableSet = new Set() - fa.forEach((e) => { - switch (e._tag) { - case "Failure": - if (!elemEE(left, e.failure)) { - left.add(e.failure) - } - break - case "Success": - if (!elemEA(right, e.success)) { - right.add(e.success) - } - break - } - }) - return Tuple.make(left, right) - } -} - -/** - * Filter + Map - */ -export function filterMap( - E: Equivalence.Equivalence -): (f: (a: A) => Option.Option) => (fa: Set) => Set { - const fm = filterMap_(E) - return (f) => (fa) => fm(fa, f) -} - -/** - * Filter + Map - */ -export function filterMap_( - E: Equivalence.Equivalence -): (fa: Set, f: (a: A) => Option.Option) => Set { - const elemE = elem_(E) - return (fa, f) => { - const r: MutableSet = new Set() - fa.forEach((a) => { - const ob = f(a) - if (ob._tag === "Some" && !elemE(r, ob.value)) { - r.add(ob.value) - } - }) - return r - } -} - -/** - * Form the union of two sets - */ -export function union_(E: Equivalence.Equivalence): (set: Set, y: Set) => Set { - const elemE = elem_(E) - return (x, y) => { - if (x === empty) { - return y - } - if (y === empty) { - return x - } - const r = new Set(x) - y.forEach((e) => { - if (!elemE(r, e)) { - r.add(e) - } - }) - return r - } -} - -/** - * Form the union of two sets - */ -export function union(E: Equivalence.Equivalence): (set: Set, y: Set) => Set { - const u = union_(E) - return (x, y) => u(x, y) -} - -function make_(ord: Order.Order, eq_?: Equivalence.Equivalence) { - const eq = eq_ ?? ((x, y) => ord(x, y) === 0) - - const fromArray_ = fromArray(eq) - const concat_ = (set: Set, it: Iterable) => fromArray_([...set, ...it]) - const insert__ = insert(eq) - - function replace_(set: Set, a: A) { - return pipe(filter_(set, (x) => !eq(x, a)), insert__(a)) - } - - return { - insert: insert__, - insert_: insert_(eq), - remove: remove(eq), - remove_: remove_(eq), - reduce: reduce(ord), - reduce_: reduce_(ord), - replace: (a: A) => (set: Set) => replace_(set, a), - replace_, - toArray: toArray(ord), - fromArray, - from: (it: Iterable) => fromArray_([...it]), - empty: () => new Set(), - concat_, - concat: (it: Iterable) => (set: Set) => concat_(set, it), - - // A and B the same, useful when editing elements. - map: map(eq), - map_: map_(eq), - filterMap: filterMap(eq), - filterMap_: filterMap_(eq) - } - // TODO: extend -} - -class Wrapper { - wrapped(ord: Order.Order, eq: Equivalence.Equivalence) { - return make_(ord, eq) - } -} - -export interface SetSchemaExtensions extends ReturnType["wrapped"]> {} - -export const make: ( - ord: Order.Order, - eq?: Equivalence.Equivalence -) => SetSchemaExtensions = make_ diff --git a/packages/effect-app/src/Store.ts b/packages/effect-app/src/Store.ts deleted file mode 100644 index f2030df9d..000000000 --- a/packages/effect-app/src/Store.ts +++ /dev/null @@ -1,325 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type * as Redacted from "effect/Redacted" -import * as Semaphore from "effect/Semaphore" -import type { NonEmptyReadonlyArray } from "./Array.ts" -import type { DatabaseError, OptimisticConcurrencyException } from "./client/errors.ts" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import type { FilterResult } from "./Model/filter/filterApi.ts" -import type { FieldValues } from "./Model/filter/types.ts" -import type { FieldPath } from "./Model/filter/types/path/index.ts" -import type { AggregateIrExpression, ComputedProjectionIrExpression, RawQuery } from "./Model/query.ts" -import type * as Option from "./Option.ts" -import * as RequestScopedDependencies from "./RequestScopedDependencies.ts" -import { type Json as SchemaJson, NonEmptyString255, type SchemaError, type Top as SchemaTop } from "./Schema.ts" - -/** - * Adapter-neutral unique-key definition for stores that support unique indexes, - * such as the Cosmos adapter. This shape is intentionally kept structurally - * compatible with adapter-specific `UniqueKey` types. Each path identifies a - * field participating in the unique key, and adapters forward these paths - * directly to the underlying storage engine. - */ -export interface UniqueKey { - readonly paths: string[] -} - -/** - * A raw stored JSON document: a readonly record of JSON values. - * - * This is the shape a document has *in the database*: dates are ISO strings, - * `ReadonlyMap`/`ReadonlySet` are arrays, and absent or legacy keys are - * whatever was written. It is deliberately not the `Encoded` shape, which - * holds native `Date`/`Map`/`Set` values. - */ -export type JsonRecord = { readonly [key: string]: SchemaJson } - -export interface StoreConfig { - partitionValue: (e?: E) => string - /** - * Primarily used for testing, creating namespaces in the database to separate data e.g to run multiple tests in isolation within the same database. - * Memory/Disk use separate store instances per namespace. CosmosDB uses namespace-prefixed partition keys. SQL uses a `_namespace` column. - */ - allowNamespace?: (namespace: string) => boolean - /** - * just in time migrations, supported by the database driver, supporting queries, for simple default values - */ - defaultValues?: Partial - - /** - * just in time Migration: for complex migrations that aren't just simple - * default values - use {@link defaultValues} for those. - * - * Runs at the store boundary on the **raw JSON document**: after - * {@link defaultValues} have been merged in (they still only fill *absent* - * keys) and *before* the JSON→Encoded decode - * (`Schema.toCodecJson(Schema.toEncoded(schema))`). - * - * It therefore sees exactly what is stored - a `Date` field as an ISO - * string, a `ReadonlyMap` as an array of pairs, an explicit `null` as - * `null` - and it must return JSON again, never native Encoded values - * (no `Date`/`Map`/`Set` instances). That is what lets it repair legacy - * shapes, including explicit `null`s, before any schema decode happens. - * - * `_etag` is infrastructure metadata and is not part of the document passed - * to `jitM`. Not applied on the write/encode path. - */ - jitM?: (json: JsonRecord) => JsonRecord - - /** - * How many items can be processed in one batch at a time. - * Defaults to 100 for CosmosDB. - */ - maxBulkSize?: number - - /** - * Unique indexes, mainly for CosmosDB - */ - uniqueKeys?: UniqueKey[] - /** - * Domain schema whose Encoded shape is stored. JSON adapters use - * `Schema.toCodecJson(Schema.toEncoded(schema))` for documents, query - * values, and defaults. Date/Map/Set (and app types such as DateOnly) - * stay native on Encoded and lower only on this JSON boundary. - */ - schema?: SchemaTop -} - -export type SupportedValues = string | boolean | number | null -export type SupportedValues2 = string | boolean | number - -// default is eq -export type Where = - | { key: string; t?: "eq" | "not-eq"; value: SupportedValues } - | { key: string; t: "gt" | "lt" | "gte" | "lte"; value: SupportedValues2 } - | { - key: string - t: "contains" | "starts-with" | "ends-with" | "not-contains" | "not-starts-with" | "not-ends-with" - value: string - } - | { key: string; t: "includes" | "not-includes"; value: string } - | { - key: string - t: "in" | "not-in" - value: readonly (SupportedValues)[] - } - -export type Filter = readonly FilterResult[] - -export interface O { - key: FieldPath - direction: "ASC" | "DESC" -} - -export interface FilterArgs { - t: Encoded - filter?: Filter | undefined - select?: - | NonEmptyReadonlyArray< - U | { key: string; subKeys: readonly string[] } | { - key: string - computed: ComputedProjectionIrExpression - } | { - key: string - path: string - } | { - key: string - aggregate: AggregateIrExpression - } - > - | undefined - order?: NonEmptyReadonlyArray>> - limit?: number | undefined - skip?: number | undefined -} - -/** decodes stored documents unless `select` projects fields, hence `SchemaError` */ -export type FilterFunc = ( - args: FilterArgs -) => Effect.Effect<(U extends undefined ? Encoded : Pick)[], DatabaseError | SchemaError> - -export interface Store< - IdKey extends keyof Encoded, - Encoded extends FieldValues, - PM extends PersistenceModelType = PersistenceModelType -> { - /** decodes every stored document, so a document `jitM` does not repair fails with `SchemaError` */ - all: Effect.Effect - filter: FilterFunc - /** decodes the stored document, so a document `jitM` does not repair fails with `SchemaError` */ - find: (id: Encoded[IdKey]) => Effect.Effect, DatabaseError | SchemaError> - /** adapters that decode the written document on the way back can fail with `SchemaError` */ - set: (e: PM) => Effect.Effect - /** adapters that decode the written documents on the way back can fail with `SchemaError` */ - batchSet: ( - items: NonEmptyReadonlyArray - ) => Effect.Effect, OptimisticConcurrencyException | DatabaseError | SchemaError> - /** adapters that decode the written documents on the way back can fail with `SchemaError` */ - bulkSet: ( - items: NonEmptyReadonlyArray - ) => Effect.Effect, OptimisticConcurrencyException | DatabaseError | SchemaError> - batchRemove: (ids: NonEmptyReadonlyArray, partitionKey?: string) => Effect.Effect - /** - * Runs the raw query over stored JSON documents, not Encoded. - * `Repository.queryRaw` applies `toCodecJson` to the projector output. - */ - queryRaw: (query: RawQuery) => Effect.Effect - /** - * Explicitly seed a namespace. Primary is seeded eagerly on initialization. - * Non-primary namespaces must be seeded explicitly before use. - */ - seedNamespace: (namespace: string) => Effect.Effect -} - -export class StoreMaker extends Context.Opaque( - name: string, - idKey: IdKey, - seed?: Effect.Effect, E, R>, - config?: StoreConfig - ) => Effect.Effect, E, R> -}>()("effect-app/StoreMaker") { -} - -export const makeContextMap = () => { - const etags = new Map() - const getEtag = (id: string) => etags.get(id) - const setEtag = (id: string, eTag: string | undefined) => { - if (eTag === undefined) { - etags.delete(id) - } else { - etags.set(id, eTag) - } - } - - // const parsedCache = new Map< - // Parser, - // Map> - // >() - - // const parserCache = new Map< - // Parser, - // (i: any) => These.These - // >() - - // const setAndReturn = ( - // p: Parser, - // np: (i: I) => These.These - // ) => { - // parserCache.set(p, np) - // return np - // } - - // const parserEnv: ParserEnv = { - // // TODO: lax: true would turn off refinement checks, may help on large payloads - // // but of course removes confirming of validation rules (which may be okay for a database owned by the app, as we write safely) - // lax: false, - // cache: { - // getOrSetParser: (p) => parserCache.get(p) ?? setAndReturn(p, (i) => parserEnv.cache!.getOrSet(i, p)), - // getOrSetParsers: (parsers) => { - // return Object.entries(parsers).reduce((prev, [k, v]) => { - // prev[k] = parserEnv.cache!.getOrSetParser(v) - // return prev - // }, {} as any) - // }, - // getOrSet: (i, parse): any => { - // const c = parsedCache.get(parse) - // if (c) { - // const f = c.get(i) - // if (f) { - // // console.log("$$$ cache hit", i) - // return f - // } else { - // const nf = parse(i, parserEnv) - // c.set(i, nf) - // return nf - // } - // } else { - // const nf = parse(i, parserEnv) - // parsedCache.set(parse, new Map([[i, nf]])) - // return nf - // } - // } - // } - // } - - const store = new Map() - const sem = Semaphore.makeUnsafe(1) - - return { - createdAt: new Date(), - get: getEtag, - set: setEtag, - getOrCreateStore: (key: symbol, make: () => T): T => { - let value = store.get(key) as T | undefined - if (value === undefined) { - value = make() - store.set(key, value) - } - return value - }, - getOrCreateStoreEffect: (key: symbol, make: Effect.Effect): Effect.Effect => - sem.withPermits(1)(Effect.uninterruptible(Effect.gen(function*() { - const value = store.get(key) as T | undefined - if (value !== undefined) return value - const v = yield* make - store.set(key, v) - return v - }))), - clear: () => { - etags.clear() - store.clear() - } - } -} - -const makeMap = Effect.acquireRelease( - Effect.sync(() => makeContextMap()), - (m) => Effect.sync(() => m.clear()) -) - -export class ContextMap extends Context.Opaque()("effect-app/ContextMap", { make: makeMap }) { -} - -export const ContextMapContainer = RequestScopedDependencies.make( - "ContextMapContainer", - ContextMap.make.pipe(Effect.map(ContextMap.of)) -) - -export type ContextMapNotStartedError = RequestScopedDependencies.RequestScopedDependencyNotStartedError - -export const getContextMap = ContextMapContainer.current - -/** - * Runs `make` at most once per ContextMap (i.e. per request) and caches the - * resulting value in the ContextMap under a fresh symbol. Subsequent calls of - * the returned Effect within the same ContextMap return the cached value. - * - * Uses the ContextMap's shared semaphore for safe single initialization. - */ -export const cachedPerRequest = ( - make: Effect.Effect -): Effect.Effect => { - const cacheKey = Symbol() - return getContextMap.pipe( - Effect.flatMap((ctxMap) => ctxMap.getOrCreateStoreEffect(cacheKey, make)) - ) -} - -const defaultNs: NonEmptyString255 = NonEmptyString255("primary") -export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {} - -export type PersistenceModelType = Encoded & { - _etag?: string | undefined -} - -export interface StorageConfig { - url: Redacted.Redacted - prefix: string - dbName: string - /** - * Cosmos only: autoscale max RU/s for containers the store creates, in databases - * without shared throughput. Unset keeps the Cosmos default (manual 400 RU/s). - */ - autoscaleMaxThroughput?: number | undefined -} diff --git a/packages/effect-app/src/TypeTest.ts b/packages/effect-app/src/TypeTest.ts deleted file mode 100644 index d7b7018db..000000000 --- a/packages/effect-app/src/TypeTest.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Nice way to underline types that are only there for type testing, not for production use -// sadly with unique symbols we get weird issues in app projects. -// api/src/X/PackList.Controllers.ts:21:1 - error TS4082: Default export of the module has or is using private name 'TypeTestId' -// export const TypeTestId: unique symbol = Symbol.for("@effect/infra/type-test") -// export type TypeTestId = typeof TypeTestId -export const TypeTestId = "effect-app/type-test" as const -export type TypeTestId = typeof TypeTestId diff --git a/packages/effect-app/src/Types.ts b/packages/effect-app/src/Types.ts deleted file mode 100644 index 99bc8370d..000000000 --- a/packages/effect-app/src/Types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export * from "effect/Types" - -export type DeepMutable = T extends ReadonlyMap ? Map, DeepMutable> - : T extends ReadonlySet ? Set> - : [keyof T] extends [never] ? T - // keep brands alive - : T extends string ? T - : T extends number ? T - : { -readonly [K in keyof T]: DeepMutable } - -export type Mutable = { - -readonly [P in keyof T]: T[P] -} - -export type DistributiveOmit = T extends any ? Omit - : never diff --git a/packages/effect-app/src/Widen.type.ts b/packages/effect-app/src/Widen.type.ts deleted file mode 100644 index 6ad23d875..000000000 --- a/packages/effect-app/src/Widen.type.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { BuiltInObject } from "effect-app/utils" - -type AllKeys = T extends any ? keyof T : never - -type OptionalKeys = T extends any ? { [K in keyof T]-?: {} extends Pick ? K : never }[keyof T] - : never - -type Idx = T extends any ? K extends keyof T ? T[K] - : D - : never - -type PartialKeys = - & Omit - & Partial> extends infer O ? { [P in keyof O]: O[P] } - : never - -export type Primitive = boolean | string | number | bigint | symbol | undefined | null -export type Widen = [T] extends [BuiltInObject] | [Primitive] ? T - : [T] extends [Array] ? { [K in keyof T]: Widen } - : [T] extends [ReadonlyArray] ? { [K in keyof T]: Widen } - : [T] extends [object] ? PartialKeys< - { [K in AllKeys]: Widen> }, - Exclude, keyof T> | OptionalKeys - > - : T diff --git a/packages/effect-app/src/_ext/Array.ts b/packages/effect-app/src/_ext/Array.ts deleted file mode 100644 index 27f2ad0fa..000000000 --- a/packages/effect-app/src/_ext/Array.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as Chunk from "effect-app/Chunk" -import * as Effect from "effect-app/Effect" -import * as Equal from "effect/Equal" -import { NotFoundError } from "../client/errors.ts" - -function getFirstBy( - a: Iterable, - idKey: keyof A, - id: A[typeof idKey], - type: Type -) { - return Effect - .fromOption( - Chunk.fromIterable(a).pipe(Chunk.findFirst((_) => Equal.equals(_[idKey], id))) - ) - .pipe(Effect.mapError(() => new NotFoundError({ type, id }))) -} - -export function makeGetFirstBy() { - return (idKey: Id, type: Type) => - ( - a: Iterable, - id: A[Id] - ) => getFirstBy(a, idKey, id, type) -} - -export const makeGetFirstById = () => (type: Type) => - makeGetFirstBy()("id", type) - -export function getFirstById( - a: Iterable, - id: A["id"], - type: Type -) { - return getFirstBy(a, "id", id, type) -} diff --git a/packages/effect-app/src/_ext/ord.ext.ts b/packages/effect-app/src/_ext/ord.ext.ts deleted file mode 100644 index b89c2c949..000000000 --- a/packages/effect-app/src/_ext/ord.ext.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as Chunk from "effect/Chunk" -import type * as Equivalence from "effect/Equivalence" - -export function toArray( - gen: Generator -) { - return Array.from(gen) -} - -/** - * Remove duplicates from an array, keeping the first occurrence of an element. - */ -export function uniq(E: Equivalence.Equivalence) { - return (self: Chunk.Chunk): Chunk.Chunk => { - let out = Chunk.fromIterable([]) - for (let i = 0; i < self.length; i++) { - const a = Chunk.getUnsafe(self, i) - if (!elem(E, a)(out)) { - out = Chunk.append(out, a) - } - } - return self.length === out.length ? self : out - } -} - -/** - * Test if a value is a member of an array. Takes a `Equivalence.Equivalence` as a single - * argument which returns the function to use to search for a value of type `A` - * in an array of type `Chunk.Chunk`. - */ -export function elem(E: Equivalence.Equivalence, value: A) { - return (self: Chunk.Chunk): boolean => { - for (let i = 0; i < self.length; i++) { - if (E(Chunk.getUnsafe(self, i), value)) { - return true - } - } - return false - } -} diff --git a/packages/effect-app/src/builtin-json.d.ts b/packages/effect-app/src/builtin-json.d.ts deleted file mode 100644 index 1e643f362..000000000 --- a/packages/effect-app/src/builtin-json.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -// JSON / Body return-type overrides (`any` -> `unknown`), isolated from -// `builtin.ts` so consumers that source-link this package (e.g. an embedded -// Effect-source setup) can toggle them off. As a `declare global`, the override -// is program-wide: in linked mode it leaks into source-linked Effect and breaks -// Effect's own `JSON.parse(...)` / `response.json()` call sites (TS2322 -// `unknown` vs `Json`). `builtin.ts` triple-slash-references this file, so -// normal builds keep the override. -declare global { - interface JSON { - /** - * Converts a JavaScript Object Notation (JSON) string into an object. - * @param text A valid JSON string. - * @param reviver A function that transforms the results. This function is called for each member of the object. - * If a member contains nested objects, the nested objects are transformed before the parent object is. - */ - parse(text: string, reviver?: (this: any, key: string, value: any) => any): unknown - } - - interface Body { - json(): Promise - } -} - -export {} diff --git a/packages/effect-app/src/builtin.ts b/packages/effect-app/src/builtin.ts deleted file mode 100644 index 084a6ce79..000000000 --- a/packages/effect-app/src/builtin.ts +++ /dev/null @@ -1,53 +0,0 @@ -// oxlint-disable-next-line typescript/triple-slash-reference -/// -import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect/Array" - -declare global { - interface String { - /** - * Split a string into substrings using the specified separator and return them as an array. - * @param splitter An object that can split a string. - * @param limit A value used to limit the number of elements returned in the array. - */ - split(splitter: { [Symbol.split](string: string, limit?: number): string[] }, limit?: number): [string, ...string[]] - - /** - * Split a string into substrings using the specified separator and return them as an array. - * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. - * @param limit A value used to limit the number of elements returned in the array. - */ - split(separator: string | RegExp, limit?: number): [string, ...string[]] - } - - // JSON.parse / Body.json overrides live in ./builtin-json.d.ts (referenced - // above) so a source-linked consumer can disable them in linked mode. - - interface Array { - // Conditional return instead of a `this`-typed overload: a `this` overload - // is selected-then-rejected on union-of-array receivers (TS2684), poisoning - // foreign code like `(readonly A[] | readonly B[]).map(...)`. A single - // conditional-return signature refines NonEmpty without a `this` param. - map( - callbackfn: (value: T, index: number, array: T[]) => U, - thisArg?: any - ): this extends NonEmptyArray ? NonEmptyArray : U[] - } - interface ReadonlyArray { - // Subsequent property declarations must have the same type. Property 'length' must be of type 'number', but here has type 'NonNegativeInt'. - // readonly length: NonNegativeInt - - map( - callbackfn: (value: T, index: number, array: readonly T[]) => U, - thisArg?: any - ): this extends NonEmptyReadonlyArray ? NonEmptyReadonlyArray : U[] - } -} - -declare module "effect/Option" { - export interface None { - get value(): A | undefined - } -} - -// TODO: v4 migration — Either module augmentation removed (Either → Result) -// Previously added .right to Left and .left to Right for convenience access diff --git a/packages/effect-app/src/client.ts b/packages/effect-app/src/client.ts deleted file mode 100644 index 578b82dd7..000000000 --- a/packages/effect-app/src/client.ts +++ /dev/null @@ -1,8 +0,0 @@ -// codegen:start {preset: barrel, include: ./client/*.ts} -export * from "./client/apiClientFactory.ts" -export * from "./client/clientFor.ts" -export * from "./client/errors.ts" -export * from "./client/InvalidationKeys.ts" -export * from "./client/makeClient.ts" -// codegen:end -export * as DataDependencies from "./DataDependencies.ts" diff --git a/packages/effect-app/src/client/InvalidationKeys.ts b/packages/effect-app/src/client/InvalidationKeys.ts deleted file mode 100644 index 6e0086335..000000000 --- a/packages/effect-app/src/client/InvalidationKeys.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as Equal from "effect/Equal" -import * as Ref from "effect/Ref" -import * as Context from "../Context.ts" -import * as Effect from "../Effect.ts" -import type { InvalidationKey } from "../rpc/Invalidation.ts" - -export type { InvalidationKey } -/** Shape of the per-mutation service that accumulates server-provided invalidation keys. */ -export interface InvalidationKeysService { - readonly add: (key: InvalidationKey) => Effect.Effect - readonly get: Effect.Effect> -} - -/** - * Context.Reference that accumulates invalidation keys received from the server via the - * `x-invalidate` HTTP response header. - * - * The default is a no-op: when not explicitly provided (e.g. outside a mutation wrapper) - * all calls are ignored. The mutation wrapper in `@effect-app/vue` provides a real - * implementation backed by a `Ref`. - */ -export const InvalidationKeysFromServer = Context.Reference( - "effect-app/client/InvalidationKeysFromServer", - { - defaultValue: () => ({ - add: (_key: InvalidationKey) => Effect.void, - get: Effect.succeed([] as ReadonlyArray) - }) - } -) -export type InvalidationKeysFromServer = typeof InvalidationKeysFromServer - -/** - * Creates a fresh `InvalidationKeysService` implementation backed by a `Ref`. - * - * @param ref - The `Ref` that stores the accumulated keys. - * @param onAdded - V3: Optional Effect run after a distinct key is added. Use to trigger - * mid-stream query invalidation without waiting for the stream to complete. Repeated keys - * stay accumulated for the final refresh but do not retrigger the callback. - */ -export const makeInvalidationKeysService = ( - ref: Ref.Ref>, - onAdded?: (key: InvalidationKey) => Effect.Effect -): InvalidationKeysService => ({ - add: (key) => - Ref - .modify(ref, (keys) => { - if (keys.some((existing) => Equal.equals(existing, key))) return [false, keys] - return [true, [...keys, key]] - }) - .pipe( - Effect.flatMap((added) => added && onAdded ? onAdded(key) : Effect.void) - ), - get: Ref.get(ref) -}) diff --git a/packages/effect-app/src/client/apiClientFactory.ts b/packages/effect-app/src/client/apiClientFactory.ts deleted file mode 100644 index b95783108..000000000 --- a/packages/effect-app/src/client/apiClientFactory.ts +++ /dev/null @@ -1,465 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { constant, flow } from "effect/Function" -import * as Layer from "effect/Layer" -import * as ManagedRuntime from "effect/ManagedRuntime" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as Record from "effect/Record" -import { Rpc, RpcClient, RpcGroup, RpcSerialization } from "effect/rpc" -import * as Schema from "effect/Schema" -import * as Stream from "effect/Stream" -import * as Struct from "effect/Struct" -import * as Config from "../Config.ts" -import * as Context from "../Context.ts" -import * as DataDependencies from "../DataDependencies.ts" -import * as Effect from "../Effect.ts" -import { HttpClient, HttpClientRequest } from "../http.ts" -import { Invalidation } from "../rpc.ts" -import type * as S from "../Schema.ts" -import type { Client, ClientForOptions, ExtractModuleName, RequestsAny } from "./clientFor.ts" -import { InvalidationKeysFromServer } from "./InvalidationKeys.ts" - -export interface ApiConfig { - url: string - headers: Option.Option> -} - -export const DefaultApiConfig = Config.all({ - url: Config.String("apiUrl").pipe(Config.withDefault("/api")), - headers: Config.Record(Schema.String, Schema.String, "headers").pipe(Config.option) -}) - -export type Req = S.Top & { - readonly make: (...args: any[]) => any - _tag: string - fields: S.Struct.Fields - success: S.Top - error: S.Top - /** Optional final-value schema for stream requests. When set, the execute effect resolves with the last stream value decoded to this type. */ - final?: S.Top - config?: Record - readonly id: string - readonly moduleName: string - readonly type: "command" | "query" - readonly stream: boolean - readonly middleware?: unknown -} - -class RequestName extends Context.Reference("RequestName", { - defaultValue: () => ({ requestName: "Unspecified", moduleName: "Error" }) -}) {} - -export const HttpClientLayer = (config: ApiConfig) => - Layer.effect( - HttpClient.HttpClient, - Effect - .gen(function*() { - const baseClient = yield* HttpClient.HttpClient - const client = baseClient.pipe( - HttpClient.mapRequest(HttpClientRequest.prependUrl(config.url + "/rpc")), - HttpClient.mapRequest( - HttpClientRequest.setHeaders(config.headers.pipe(Option.getOrElse(() => ({})))) - ), - HttpClient.mapRequestEffect((req) => - Effect.map(RequestName, (ctx) => - flow( - HttpClientRequest.appendUrlParam("action", ctx.requestName), - HttpClientRequest.appendUrl("/" + ctx.moduleName) - )(req)) - ) - ) - return client - }) - ) - -export const HttpClientFromConfigLayer = Layer.unwrap( - Effect.gen(function*() { - const config = yield* DefaultApiConfig - return HttpClientLayer(config) - }) -) - -export const RpcSerializationLayer = (config: ApiConfig) => - Layer.mergeAll( - RpcSerialization.layerNdjson, - HttpClientLayer(config) - ) - -type RpcHandlers = { - [K in keyof M]: Rpc.Rpc -} - -const getFiltered = (resource: M) => { - type Filtered = { - [K in keyof M as M[K] extends Req ? K : never]: M[K] extends Req ? M[K] : never - } - // TODO: Record.filter - const filtered = Struct.keys(resource).reduce((acc, cur) => { - if ( - Predicate.isObjectKeyword(resource[cur]) - && (resource[cur].success) - ) { - acc[cur as keyof Filtered] = resource[cur] as any - } - return acc - }, {} as Record) - - return filtered as unknown as Filtered -} - -export const getMeta = ( - resource: M -): { moduleName: ExtractModuleName; middleware?: unknown } => { - const first = Record.values(getFiltered(resource))[0] - if (first && "moduleName" in first) return { moduleName: first.moduleName, middleware: (first as any).middleware } - throw new Error("No moduleName on requests!") -} - -export const makeRpcGroupFromRequestsAndModuleName = ( - resource: M, - moduleName: ModuleName, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - middleware?: any -) => { - const filtered = getFiltered(resource) - type newM = typeof filtered - const baseRpcs = RpcGroup - .make( - ...Record.values(filtered).map((_) => { - const r = _ as any - const isStream = r.stream - const isCommand = r.type === "command" - return (isCommand - ? isStream - ? Invalidation.makeStreamRpc(r._tag, { - payload: r, - success: r.success, - error: r.error, - stream: true as const - }) - : Invalidation.makeCommandRpc(r._tag, { payload: r, success: r.success, error: r.error }) - : Rpc.make(r._tag, { - payload: r, - success: isStream ? r.success : Invalidation.QueryResponseWithMetaData(r.success), - error: r.error, - stream: isStream - })) as any - }) - ) - .prefix(`${moduleName}.`) - // Attach the middleware tag (schema-only on the client — no Live invoked) - // so its declared `error` joins the rpc failure union via - // `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. Required for stream - // rpcs whose top-level `errorSchema` is forced to `Never` by effect-rpc; - // without it, middleware-thrown errors fail client decode. - const rpcs = (middleware ? baseRpcs.middleware(middleware) : baseRpcs) as unknown as RpcGroup.RpcGroup< - Rpc.Prefixed[keyof newM], `${ModuleName}.`> - > - return rpcs -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const makeRpcTag = (resource: M, middleware?: any) => { - const meta = getMeta(resource) - const rpcs = makeRpcGroupFromRequestsAndModuleName(resource, meta.moduleName, middleware) - - // Use Object.assign instead of class extension to avoid TS2509 with complex generic return types. - // The first type arg is `any` because this is a dynamically created tag — its identity is the string key. - const TheClient = Context.Opaque< - any, - RpcClient.RpcClient> - >()(`RpcClient.${meta.moduleName}`) - // Use Layer.effect directly (not TheClient.toLayer) so TypeScript properly excludes Scope - const layer = Layer.effect( - TheClient, - RpcClient.make(rpcs) - ) - return Object.assign(TheClient, { layer }) -} - -const makeApiClientFactory = Effect - .gen(function*() { - const ctx = yield* Effect.context() - const makeClientFor = Effect.fnUntraced(function*( - resource: M, - requestLevelLayers = Layer.empty, - options?: ClientForOptions - ) { - const meta = getMeta(resource) - - const TheClient = makeRpcTag(resource, meta.middleware) - - // TODO: somehow we need a protocol per REQUEST kind of it seems ... - // otherwise it locks up on the client, navigation remains empty... - const clientLayer = TheClient.layer.pipe( - // add ApiClientFactory for nested schemas - // eslint-disable-next-line @typescript-eslint/no-use-before-define - Layer.provide(Layer.succeed(ApiClientFactory, makeClientForCached as any)), - Layer.provide( - RpcClient - .layerProtocolHttp({ url: "" }) // why not here set meta.moduleName as root? - .pipe( - Layer.provideMerge(Layer.succeedContext(ctx)) - ) - ) - ) - const mr = ManagedRuntime.make(clientLayer) - - const filtered = getFiltered(resource) - - // Unwrap `CommandResponseWithMetaData` (success) and `CommandFailureWithMetaData` - // (handler-thrown failure): forward accumulated invalidation keys to - // `InvalidationKeysFromServer` and yield the raw payload / re-fail with the raw - // error. Middleware-thrown failures arrive raw on the Cause already (no wrap to - // strip) — the `else` branch passes them through. - const forwardDependencies = ( - dependencies: DataDependencies.DataDependencySet | undefined, - options?: { readonly reads?: boolean } - ) => - dependencies === undefined - ? Effect.void - : Effect.gen(function*() { - const dependencyRecorder = yield* DataDependencies.getDataDependencyRecorder - if (options?.reads) { - yield* Effect.forEach(dependencies.reads, dependencyRecorder.read, { discard: true }) - } - yield* Effect.forEach(dependencies.writes, dependencyRecorder.write, { discard: true }) - }) - - const unwrapCommand = (eff: Effect.Effect): Effect.Effect => - eff.pipe( - Effect.flatMap((result: any) => - Effect.gen(function*() { - const keys: ReadonlyArray = result?.metadata?.invalidateQueries ?? [] - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(keys, (key) => invalidationKeys.add(key), { discard: true }) - yield* forwardDependencies(result?.metadata?.dataDependencies) - return result.payload - }) - ), - Effect.catch((result: any) => - result?._tag === "CommandFailureWithMetaData" - ? Effect.gen(function*() { - const keys: ReadonlyArray = result.metadata?.invalidateQueries ?? [] - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(keys, (key) => invalidationKeys.add(key), { discard: true }) - yield* forwardDependencies(result.metadata?.dataDependencies) - return yield* Effect.fail(result.error) - }) - : Effect.fail(result) - ) - ) - - const unwrapQuery = (eff: Effect.Effect): Effect.Effect => - eff.pipe( - Effect.flatMap((result: any) => - Effect.gen(function*() { - yield* forwardDependencies(result?.metadata?.dataDependencies, { reads: true }) - return result.payload - }) - ) - ) - - return { - mr, - client: Struct - .keys(filtered) - .reduce((prev, cur) => { - const h = filtered[cur]! - - const Request = h - - const id = `${meta.moduleName}.${cur}` - .replaceAll(".js", "") - - const requestMeta = { - Request, - id, - options, - disableQueryInvalidation: (h as { config?: { disableQueryInvalidation?: boolean } }) - .config - ?.disableQueryInvalidation === true - } - - const requestNameLayer = Layer.succeed(RequestName, { - requestName: cur, - moduleName: meta.moduleName - }) - - const layers = requestLevelLayers.pipe(Layer.provideMerge(requestNameLayer)) - - const fields = Struct.omit(Request.fields, ["_tag"] as const) - const requestAttr = `${meta.moduleName}.${h._tag}` - const isCommand = h.type === "command" - const isStream = h.stream - - const buildEffect = (input: any) => - mr.contextEffect.pipe( - Effect.flatMap((svcs) => { - const rpcEffect = TheClient - .use((client) => (client as any)[requestAttr]!(Request.make(input)) as Effect.Effect) - .pipe( - // local: true → isolate to this RPC call. `layers` is pure today - // (RequestName + caller-supplied requestLevelLayers), but the - // flag prevents a stateful caller-supplied layer from leaking - // across RPC calls via the ambient fiber MemoMap. - Effect.provide(layers, { local: true }), - Effect.provide(svcs) - ) - return isCommand ? unwrapCommand(rpcEffect) : isStream ? rpcEffect : unwrapQuery(rpcEffect) - }), - // Forwarding the server's dependency metadata needs a recorder in scope. A parent - // query/command (vue) provides one; a top-level call gets a fresh per-request one. - DataDependencies.ensureDataDependencyRecorder - ) - - const buildStream = (input: any) => { - const stream = Stream.unwrap( - mr.contextEffect.pipe( - Effect.flatMap((svcs) => - TheClient - .useSync((client) => { - const rpcStream = (client as any)[requestAttr]!( - Request.make(input) - ) as Stream.Stream - return rpcStream.pipe( - // Collect server invalidation keys from the "done" chunk, then discard it. - Stream.tap((item: any) => - item._tag === "done" || item._tag === "metadata" - ? Effect.gen(function*() { - const metadata = item.metadata as Invalidation.CommandMetaData - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(metadata.invalidateQueries, invalidationKeys.add, { - discard: true - }) - const dependencyRecorder = yield* DataDependencies.getDataDependencyRecorder - yield* Effect.forEach( - metadata.dataDependencies.reads, - dependencyRecorder.read, - { discard: true } - ) - yield* Effect.forEach( - metadata.dataDependencies.writes, - dependencyRecorder.write, - { discard: true } - ) - }) - : Effect.void - ), - Stream.filter((item: any) => item._tag === "value"), - Stream.map((item: any) => item.value), - // V2: unwrap StreamFailureChunk — forward keys from failures too, - // then re-fail with the original error so callers see the unmodified - // error type. - Stream.catch((err: any) => - err?._tag === "error" && err?.metadata - ? Stream.fromEffect( - Effect.gen(function*() { - const metadata = err.metadata as Invalidation.CommandMetaData - const invalidationKeys = yield* InvalidationKeysFromServer - yield* Effect.forEach(metadata.invalidateQueries, invalidationKeys.add, { - discard: true - }) - const dependencyRecorder = yield* DataDependencies.getDataDependencyRecorder - yield* Effect.forEach( - metadata.dataDependencies.writes, - dependencyRecorder.write, - { discard: true } - ) - return yield* Effect.fail(err.error) - }) - ) - : Stream.fail(err) - ), - // local: true — see buildEffect above. - Stream.provide(layers, { local: true }), - Stream.provide(svcs) - ) - }) - .pipe(Effect.provide(svcs)) - ) - ) - ) - // Forwarding the server's dependency metadata needs a recorder in scope. A parent - // query/command (vue) provides one; a top-level stream call gets a fresh per-request one. - return Stream.unwrap( - Effect.map( - DataDependencies.DataDependencyRecorder, - (current) => - current === "root" - ? Stream.provide(stream, DataDependencies.DataDependencyRecorder.layer) - : stream - ) - ) - } - - // @ts-expect-error doc - prev[cur] = Object.keys(fields).length === 0 - ? { - handler: isStream ? constant(buildStream({})) : constant(buildEffect({})), - ...requestMeta - } - : { - handler: isStream - ? (req: any) => buildStream(req) - : (req: any) => buildEffect(req), - ...requestMeta - } - - return prev - }, {} as Client>) - } - }) - - const register: ManagedRuntime.ManagedRuntime[] = [] - yield* Effect.addFinalizer(() => Effect.forEach(register, (mr) => mr.disposeEffect)) - - const cacheL = new Map>>() - - function makeClientForCached(requestLevelLayers: Layer.Layer, options?: ClientForOptions) { - let cache = cacheL.get(requestLevelLayers) - if (!cache) { - cache = new Map>() - cacheL.set(requestLevelLayers, cache) - } - - return Effect.fnUntraced(function*(models: M) { - const found = cache.get(models) as Client> | undefined - if (found) { - return found - } - const m = yield* makeClientFor(models, requestLevelLayers, options) - cache.set(models, m.client) - register.push(m.mr) - return m.client - }) - } - - return makeClientForCached - }) - -/** - * Used to create clients for resource modules. - */ -export class ApiClientFactory - extends Context.Opaque>()("ApiClientFactory") -{ - static readonly layer = (config: ApiConfig) => - ApiClientFactory.toLayer(makeApiClientFactory).pipe(Layer.provide(RpcSerializationLayer(config))) - static readonly layerFromConfig = Layer.unwrap( - Effect.gen(function*() { - const config = yield* DefaultApiConfig - return ApiClientFactory.layer(config) - }) - ) - - static readonly makeFor = - (requestLevelLayers: Layer.Layer, options?: ClientForOptions) => - ( - resource: M - ) => - ApiClientFactory.use((apiClientFactory) => { - const f = apiClientFactory(requestLevelLayers, options) - return f(resource) - }) -} diff --git a/packages/effect-app/src/client/clientFor.ts b/packages/effect-app/src/client/clientFor.ts deleted file mode 100644 index bb4da478e..000000000 --- a/packages/effect-app/src/client/clientFor.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as Record from "effect/Record" -import type * as Stream from "effect/Stream" -import type { Path } from "path-parser" -import qs from "query-string" -import type * as Effect from "../Effect.ts" -import type * as S from "../Schema.ts" -import { type Req } from "./apiClientFactory.ts" - -export function makePathWithQuery( - path: Path, - pars: Record< - string, - | string - | number - | boolean - | readonly string[] - | readonly number[] - | readonly boolean[] - | null - > -) { - const forQs = Record.filter(pars, (_, k) => !path.params.includes(k)) - const q = forQs // { ...forQs, _: JSON.stringify(forQs) } // TODO: drop completely individual keys from query?, sticking to json only - return ( - path.build(pars, { ignoreSearch: true, ignoreConstraints: true }) - + (Object.keys(q).length - ? "?" + qs.stringify(q) - : "") - ) -} - -export function makePathWithBody( - path: Path, - pars: Record< - string, - | string - | number - | boolean - | readonly string[] - | readonly number[] - | readonly boolean[] - | null - > -) { - return path.build(pars, { ignoreSearch: true, ignoreConstraints: true }) -} - -export type Requests = RequestsAny -export type RequestsAny = Record - -export type ExtractModuleName = - { [K in keyof M]: M[K] extends { moduleName: infer N extends string } ? N : never }[keyof M] extends - infer R extends string ? R - : string - -export type Client = RequestHandlers< - never, - never, - M, - ModuleName -> - -export type ExtractResponse = T extends S.Codec ? T["Type"] - : T extends unknown ? void - : never - -export type ExtractEResponse = T extends S.Codec ? T["Encoded"] - : T extends unknown ? void - : never - -export interface ClientForOptions { - readonly skipQueryKey?: readonly string[] - /** - * Middleware tag to attach to every rpc on the client. Schema-only — the - * client never invokes the middleware (no Live impl required), but its - * declared `error` schema joins the rpc failure union via - * `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. Required when - * middleware can throw errors that aren't part of the resource's declared - * error union (e.g. auth middleware throwing `NotLoggedInError`); without - * it the client decode would fail with a `SchemaError` for stream rpcs. - */ -} - -// $Project/$Configuration.Index -// -> "$Project", "$Configuration", "Index" -export const makeQueryKey = ({ id, options }: { id: string; options?: ClientForOptions }) => - id - .split("/") - .filter((segment: string) => !options || !options.skipQueryKey?.includes(segment)) - .map((segment: string) => "$" + segment) - .join(".") - .split(".") - -export interface RequestHandlerWithInput { - handler: (i: I) => Effect.Effect - id: Id - options?: ClientForOptions - Request: Request - queryKeyProjectionHash?: string - disableQueryInvalidation?: boolean -} - -/** Type alias: a no-input handler is simply `RequestHandlerWithInput`. */ -export type RequestHandler = RequestHandlerWithInput< - void, - A, - E, - R, - Request, - Id -> - -export interface RequestStreamHandlerWithInput { - handler: (i: I) => Stream.Stream - id: Id - options?: ClientForOptions - Request: Request - /** - * Phantom type property (never set at runtime) that carries the `Final` type to - * `StreamMutationWithExtensions`. The tilde prefix follows the Effect convention for - * phantom/virtual properties and prevents accidental runtime access. - * Stream failures bubble through the execute effect's typed error channel `E`; - * the reactive `AsyncResult` ref also mirrors the failure for live progress UI. - */ - readonly "~final"?: Final -} - -/** Type alias: a no-input stream handler is simply `RequestStreamHandlerWithInput`. */ -export type RequestStreamHandler = - RequestStreamHandlerWithInput - -// make sure this is exported or d.ts of apiClientFactory breaks?! -export type RequestInputFromMake any }> = Parameters extends - [] ? void : Parameters[0] - -// Has no input only when the request schema declares no payload fields (the auto-added -// `_tag` field is ignored). Any payload fields (even all-optional) produce a function handler. -type HasNoFields = I extends { readonly fields: infer F extends S.Struct.Fields } - ? [Exclude] extends [never] ? true : false - : false - -type RequestInput any }> = Parameters[0] - -/** - * Caller-facing input type for a request. `void` when the request schema has no fields; - * otherwise `make`'s first param type. - */ -export type HandlerInput any }> = HasNoFields extends true ? void - : RequestInput - -/** Extracts the final-value type from a stream request. Defaults to the success type when no `final` schema is set. */ -type FinalTypeOf = T extends { readonly final: infer F extends S.Top } ? F["Type"] - : T["success"]["Type"] - -// `T["success"]` / `T["error"]` are constrained to `S.Top` via `Req`, so we -// can read `["DecodingServices"]` directly. Avoids the conditional in -// `S.Codec.DecodingServices = X extends Top ? X["DecodingServices"] : never`, -// which tsgo (native) fails to reduce in this generic position and leaves as -// `unknown`, polluting the `R` channel of every client handler. -type RequestHandlerFor = T["stream"] extends true - ? RequestStreamHandlerWithInput< - HandlerInput, - T["success"]["Type"], - T["error"]["Type"] | E, - R | T["success"]["DecodingServices"] | T["error"]["DecodingServices"], - T, - Id, - FinalTypeOf - > - : RequestHandlerWithInput< - HandlerInput, - T["success"]["Type"], - T["error"]["Type"] | E, - R | T["success"]["DecodingServices"] | T["error"]["DecodingServices"], - T, - Id - > - -export type RequestHandlers = { - [K in keyof M as M[K] extends Req ? K : never]: Extract extends infer T extends Req - ? RequestHandlerFor - : never -} diff --git a/packages/effect-app/src/client/errors.ts b/packages/effect-app/src/client/errors.ts deleted file mode 100644 index 988b45f4d..000000000 --- a/packages/effect-app/src/client/errors.ts +++ /dev/null @@ -1,310 +0,0 @@ -/** @effect-diagnostics overriddenSchemaConstructor:skip-file */ -import { TaggedErrorClass } from "effect-app/Schema" -import * as Cause from "effect/Cause" -import * as S from "../Schema.ts" - -export const tryToJson = (error: unknown) => { - try { - const errorJson = (error as any).toJSON() - return errorJson && typeof errorJson === "object" ? errorJson as Record : { error } - } catch { - try { - return { error: (error as any).toString() } - } catch (err) { - try { - return { error: `Failed to convert error: ${err}` } - } catch { - return { error: `Failed to convert error: unknown failure` } - } - } - } -} - -// eslint-disable-next-line unused-imports/no-unused-vars -// @ts-expect-error type not used -export class NotFoundError extends TaggedErrorClass>()("NotFoundError", { - type: S.String, - id: S.Unknown -}) { - constructor( - props: { type: string; id: unknown; cause?: unknown }, - disableValidation?: boolean - ) { - super(props, disableValidation as any) - } - override get message() { - return `Didn't find ${(this as any).type}#${JSON.stringify((this as any).id)}` - } - override toString() { - return `NotFoundError: ${this.message}` - } -} - -const messageFallback = (messageOrObject?: string | { message: string }) => - typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject ?? "" } - -export class InvalidStateError extends TaggedErrorClass()("InvalidStateError", { - message: S.String -}) { - constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super( - typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject }, - disableValidation as any - ) - } - override toString() { - return `InvalidStateError: ${this.message}` - } -} - -export class ServiceUnavailableError extends TaggedErrorClass()("ServiceUnavailableError", { - message: S.String -}) { - constructor(messageOrObject: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super( - typeof messageOrObject === "object" ? messageOrObject : { message: messageOrObject }, - disableValidation as any - ) - } - override toString() { - return `ServiceUnavailableError: ${this.message}` - } -} - -export class ValidationError extends TaggedErrorClass()("ValidationError", { - errors: S.Array(S.Unknown) -}) { - constructor( - props: { errors: ReadonlyArray; cause?: unknown }, - disableValidation?: boolean - ) { - super(props, disableValidation as any) - } - override get message() { - return `Validation failed: ${(this as any).errors.map((e: any) => JSON.stringify(e, undefined, 2)).join(",\n")}` - } - override toString() { - return `ValidationError: ${this.message}` - } -} - -export class NotLoggedInError extends TaggedErrorClass()("NotLoggedInError", { - message: S.String -}) { - constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super(messageFallback(messageOrObject), disableValidation as any) - } - override toString() { - return `NotLoggedInError: ${this.message}` - } -} - -/** - * The user carries a valid Userprofile, but there is a problem with the login none the less. - */ -export class LoginError extends TaggedErrorClass()("NotLoggedInError", { - message: S.String -}) { - constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super(messageFallback(messageOrObject), disableValidation as any) - } - override toString() { - return `LoginError: ${this.message}` - } -} - -export class UnauthorizedError extends TaggedErrorClass()("UnauthorizedError", { - message: S.String -}) { - constructor(messageOrObject?: string | { message: string; cause?: unknown }, disableValidation?: boolean) { - super(messageFallback(messageOrObject), disableValidation as any) - } - override toString() { - return `UnauthorizedError: ${this.message}` - } -} - -type OptimisticConcurrencyDetails = { - readonly type: string - readonly id: string - readonly code: number - readonly current?: string | undefined - readonly found?: string | undefined -} - -export class OptimisticConcurrencyException extends TaggedErrorClass()( - "OptimisticConcurrencyException", - { message: S.String } -) { - readonly details?: OptimisticConcurrencyDetails - readonly raw?: unknown - constructor( - args: - | OptimisticConcurrencyDetails - | ({ message: string; cause?: unknown; raw?: unknown }), - disableValidation?: boolean - ) { - super( - "message" in args ? args : { message: `Existing ${args.type} ${args.id} record changed` }, - disableValidation as any - ) - if (!("message" in args)) { - this.details = args - } - } - override toString() { - return `OptimisticConcurrencyException: ${this.message}` - } -} - -/** - * Raised by a store adapter when a database operation fails for an - * infrastructure reason (request timeout, throttle, 5xx, dropped socket, or any - * non-conflict error). Distinct from `OptimisticConcurrencyException`, which is - * an expected etag conflict. - * - * `transient` is set by the adapter for failures worth retrying (timeout / - * throttle / 5xx / network). `cause` is the underlying adapter error, carried - * through a `Defect` schema so it serializes (an `Error` encodes to - * `{ name, message, cause }`) instead of breaking JSON encoding of the channel. - * - * Treated by the api/client/FE machinery like an unexpected (500-class) error. - */ -export class DatabaseError extends TaggedErrorClass()( - "DatabaseError", - { - message: S.String, - transient: S.Boolean, - cause: S.optional(S.Defect()) - } -) { - constructor( - args: { message?: string; transient?: boolean; cause?: unknown }, - disableValidation?: boolean - ) { - super( - { - message: args.message ?? "Database operation failed", - transient: args.transient ?? false, - cause: args.cause - }, - disableValidation as any - ) - } - override toString() { - return `DatabaseError: ${this.message}` - } -} - -const MutationOnlyErrors = [ - InvalidStateError, - OptimisticConcurrencyException -] as const - -const GeneralErrors = [ - NotFoundError, - NotLoggedInError, - LoginError, - UnauthorizedError, - ValidationError, - ServiceUnavailableError, - DatabaseError -] as const - -export const SupportedErrors = S.Union([ - ...MutationOnlyErrors, - ...GeneralErrors -]) -// .pipe(named("SupportedErrors")) -// .pipe(withDefaultMake) -export type SupportedErrors = S.Schema.Type - -// ideal? -// export const QueryErrors = union({ ...GeneralErrors }) -// .pipe(named("QueryErrors")) -// .pipe(withDefaultMake) -// export type QueryErrors = Schema.Type -// export const MutationErrors = union({ ...GeneralErrors, ...GeneralErrors }) -// .pipe(named("MutationErrors")) -// .pipe(withDefaultMake) - -// export type MutationErrors = Schema.Type - -export const MutationErrors = SupportedErrors -export const QueryErrors = SupportedErrors -export type MutationErrors = S.Schema.Type -export type QueryErrors = S.Schema.Type - -export const ErrorSilenced = Symbol.for("effect-app/error-silenced") -export const isErrorSilenced = (e: unknown): boolean => - typeof e === "object" && e !== null && ErrorSilenced in e ? !!e[ErrorSilenced] : false -export const silenceError = (e: Record) => { - e[ErrorSilenced] = true -} - -export class CauseException extends Error { - readonly originalCause: Cause.Cause - readonly _tag: string - - constructor(originalCause: Cause.Cause, _tag: string) { - const limit = Error.stackTraceLimit - Error.stackTraceLimit = 0 - super() - Error.stackTraceLimit = limit - this.originalCause = originalCause - this._tag = _tag - this.cause = Cause.squash(originalCause) - // v4: makeFiberFailure removed — use Cause.prettyErrors instead - const errors = Cause.prettyErrors(originalCause) - const first = errors[0] - if (first) { - this.name = first.name - this.message = first.message - if (first.stack) { - this.stack = first.stack - } - } else { - this.name = "CauseException" - this.message = Cause.pretty(originalCause) - } - } - toReport() { - return { - _tag: this._tag, - name: this.name, - message: this.message - } - } - - toJSON() { - return { - _tag: this._tag, - name: this.name, - message: this.message, - originalCause: this.originalCause - } - } - - [Symbol.for("nodejs.util.inspect.custom")]() { - return this.toJSON() - } - override toString() { - return `[${this._tag}] ` + Cause.pretty(this.originalCause) - } -} - -export const tryToReport = (error: { toReport(): Record; toString(): string }) => { - try { - return error.toReport() - } catch { - try { - return { error: error.toString() } - } catch (err) { - try { - return { error: `Failed to convert error: ${err}` } - } catch { - return { error: `Failed to convert error: unknown failure` } - } - } - } -} diff --git a/packages/effect-app/src/client/makeClient.ts b/packages/effect-app/src/client/makeClient.ts deleted file mode 100644 index c1785f084..000000000 --- a/packages/effect-app/src/client/makeClient.ts +++ /dev/null @@ -1,629 +0,0 @@ -import type * as Exit from "effect/Exit" -import { type GetContextConfig, type RpcContextMap } from "../rpc/RpcContextMap.ts" -import * as S from "../Schema.ts" -import { AST } from "../Schema.ts" -import type { ClientForOptions } from "./clientFor.ts" - -/** - * Minimal structural shape for an rpc-client middleware tag. - * Captures only what `makeRpcClient` and routing/client factory consume from it - * (no `Default` layer required — that is provided to `makeRouter`). - */ -export interface ClientMiddleware> { - readonly requestContextMap: RequestContextMap - readonly requestContext: unknown - readonly provides?: unknown - readonly requires?: unknown -} - -const merge = (a: any, b: Array) => - a !== undefined && b.length ? S.Union([a, ...b]) : a !== undefined ? a : b.length ? S.Union(b) : S.Never - -type SchemaOrFields = T extends S.Top ? T : T extends S.Struct.Fields ? S.Struct : S.Void - -type TaggedRequestSchema = S.Struct< - { readonly _tag: S.tag } & Payload -> - -type QueryOnlyRequests = { - [K in keyof Resource as Resource[K] extends { readonly type: "query" } ? K : never]: Resource[K] -} - -type QueryOnlyResources = { - [K in keyof Resources]: QueryOnlyRequests -} - -type InputFromPayload = keyof Payload extends never ? void - : S.Struct["Type"] - -type OutputFromSuccess = Success extends typeof S.Void ? void : Success["Type"] - -type InvalidationResources = Record> - -/** - * An invalidation instruction returned from `invalidatesQueries`. One of: - * - a raw query key (`ReadonlyArray`) - * - an RPC handler object (`{ id, options? }`) — query key derived from `id` - * - a compatibility `{ filters: { queryKey }, options? }` shape - * - * Predicate filters are intentionally not represented: atom invalidation is keyed by - * concrete query keys. - */ -export type InvalidateQueryInstruction< - Filters = { readonly queryKey: ReadonlyArray }, - Options = Record -> = - | ReadonlyArray - | { - readonly id: string - readonly options?: ClientForOptions - } - | { - readonly filters: Filters - readonly options?: Options - } - -export type InvalidationCallback = ( - queryKey: readonly string[], - resources: QueryOnlyResources, - ...args: [Input] extends [void] ? [exit: Exit.Exit] - : [input: Input, exit: Exit.Exit] -) => ReadonlyArray - -export type InvalidationConfig = { - readonly invalidatesQueries: InvalidationCallback - readonly invalidationResources?: Resources - readonly disableQueryInvalidation?: boolean -} - -type InvalidationConfigForCommand< - Resources, - Payload extends S.Struct.Fields, - Success extends S.Top, - Error extends S.Top -> = InvalidationConfig< - Resources, - InputFromPayload, - OutputFromSuccess, - Error["Type"] -> - -export const configureInvalidation = () => -( - invalidatesQueries: InvalidationCallback, NoInfer, NoInfer> -): InvalidationConfig => ({ invalidatesQueries }) - -export const configureInvalidationCallback = () => -( - invalidatesQueries: InvalidationCallback, NoInfer, NoInfer> -): InvalidationCallback => invalidatesQueries - -export const configureInvalidationResources = () => - ({}) as Pick, "invalidationResources"> - -type TaggedRequestForResult< - Self, - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top, - Error extends S.Top, - Config, - ModuleName extends string, - Type extends "command" | "query", - Stream extends boolean, - Resources = never, - Final extends S.Top = never, - Middleware = unknown -> = - & S.Opaque, {}> - & { - readonly fields: TaggedRequestSchema["fields"] - readonly _tag: Tag - readonly success: Success - readonly error: Error - readonly config: Config - readonly id: `${ModuleName}.${Tag}` - readonly moduleName: ModuleName - readonly type: Type - readonly stream: Stream - readonly middleware?: Middleware - readonly "~invalidationResources"?: Resources - } - & ([Final] extends [never] ? {} : { readonly final: Final }) - -export const makeRpcClient = < - Middleware extends ClientMiddleware>, - GeneralErrors extends S.Top = never ->(middleware: Middleware, generalErrors?: GeneralErrors) => { - // Long way around Context/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??) - type ServiceMap = { - success: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields - error: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields - final?: S.Top | S.Struct.Fields // optional final-value schema for stream requests - stream?: boolean // request metadata — stripped from stored config - } - - type RequestConfig = GetContextConfig - - // Errors raised by RPC middleware (e.g. `NotLoggedInError` from auth) used to - // be merged into `resource.error` here so they would surface in the wire - // failure schema. That merging is gone — the canonical source for middleware - // errors is now the middleware tag attached to the rpc on both server and - // client (see `ApiClientFactory.makeFor({ middleware })`). `Rpc.exitSchema` - // unions in `rpc.middlewares[*].error` automatically. - type MergeError = [GeneralErrors] extends [never] ? SchemaOrFields : S.Union<[SchemaOrFields, GeneralErrors]> - type ErrorResult = C extends { error: infer E } ? MergeError - : [GeneralErrors] extends [never] ? typeof S.Never - : GeneralErrors - - function makeRequestClass>( - tag: Tag, - fields: Fields, - config?: C - ) { - const failureSchema = merge( - config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined, - [generalErrors].filter(Boolean) - ) - const successSchema = config?.success - ? S.isSchema(config.success) - ? AST.isVoid(config.success.ast) ? S.Void : config.success - : S.Struct(config.success) - : S.Void - - const finalConfig = (config as any)?.final - const finalSchema = finalConfig && S.isSchema(finalConfig) ? finalConfig : undefined - - // Strip stream from the stored config — it's request metadata, not handler config - const { stream: _stream, ...restConfig } = config ?? ({} as C) - - const RequestClass = S.Opaque()(S.TaggedStruct(tag, fields)) - Object.assign(RequestClass, { - _tag: tag, - success: successSchema, - error: failureSchema, - ...(finalSchema !== undefined ? { final: finalSchema } : {}), - config: restConfig - }) - - return RequestClass - } - - function makeTaggedRequestWithMeta< - ModuleName extends string, - Type extends "command" | "query" - >( - moduleName: ModuleName, - type: Type - ) { - function TaggedRequestWithMeta(): { - // ─── stream: true overloads (must come before non-stream so TypeScript picks them) ─────────── - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record = RequestConfig & Record - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true; success: Success; error: Error; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { - success: Success - error: Error - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record & { error?: never } = - & RequestConfig - & Record - & { error?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true; success: Success; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { success: Success } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - // ─── stream: true without `success` overloads ──────────────────────────────────────────────── - < - Tag extends string, - Payload extends S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - C extends RequestConfig & Record & { success?: never } = - & RequestConfig - & Record - & { success?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true; error: Error }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & { error: Error } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - typeof S.Void, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - never, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - C extends RequestConfig & Record & { success?: never; error?: never } = - & RequestConfig - & Record - & { success?: never; error?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { stream: true }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & Partial>>, - "success" | "error" | "stream" - >, - ModuleName, - Type, - true, - Resources, - never, - Middleware - > - // ─── non-stream overloads ──────────────────────────────────────────────────────────────────── - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record = RequestConfig & Record - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { success: Success; error: Error; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { - success: Success - error: Error - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - Success extends S.Top | S.Struct.Fields, - Final extends S.Top | S.Struct.Fields = never, - C extends RequestConfig & Record & { error?: never } = - & RequestConfig - & Record - & { - error?: never - } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { success: Success; final?: Final }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - OutputFromSuccess>, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - SchemaOrFields, - ErrorResult, - Omit< - & Omit - & { - success: Success - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - SchemaOrFields, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - [Final] extends [never] ? never : SchemaOrFields, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - Error extends S.Top | S.Struct.Fields, - C extends RequestConfig & Record & { success?: never } - >( - tag: Tag, - fields: Payload, - config: - & Omit - & { error: Error }, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & { - error: Error - } - & Partial< - InvalidationConfigForCommand< - Resources, - Payload, - typeof S.Void, - ErrorResult - > - >, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - never, - Middleware - > - < - Tag extends string, - Payload extends S.Struct.Fields, - C extends RequestConfig & Record & { success?: never; error?: never } - >( - tag: Tag, - fields: Payload, - config: Omit, - invalidatesQueries?: InvalidationCallback< - Resources, - InputFromPayload, - void, - ErrorResult["Type"] - > - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult, - Omit< - & Omit - & Partial>>, - "success" | "error" | "stream" - >, - ModuleName, - Type, - false, - Resources, - never, - Middleware - > - ( - tag: Tag, - fields: Payload - ): TaggedRequestForResult< - Self, - Tag, - Payload, - typeof S.Void, - ErrorResult<{}>, - // Empty config, NOT `Record`: the config type flows into - // `GetEffectContext`/`GetEffectError` as `T`, and `keyof Record` - // is `string` — so an inverted key like `allowAnonymous` would satisfy - // `key extends keyof T`, then `T[key]` (`never`) `extends true` (true, since - // `never` is assignable to everything) and the middleware wrongly takes the - // "allowAnonymous: true" (anonymous) branch, dropping `UserProfile`. `{}` has - // `keyof {} = never`, so no config key is treated as set and the defaults - // (auth required, `UserProfile` provided) apply — same as passing `{}` explicitly. - {}, - ModuleName, - Type, - false, - never, - never, - Middleware - > - } { - return (( - tag: Tag, - fields: Fields, - config?: C, - invalidatesQueries?: InvalidationCallback - ) => { - const isStream = (config as any)?.stream === true - const requestConfig = invalidatesQueries === undefined ? config : { ...config, invalidatesQueries } - const cls = makeRequestClass(tag, fields, requestConfig) - Object.assign(cls, { - id: `${moduleName}.${tag}`, - moduleName, - type, - stream: isStream, - middleware - }) - return cls - }) as any - } - return Object.assign(TaggedRequestWithMeta, { moduleName, type } as const) - } - - function TaggedRequestFor(moduleName: ModuleName) { - const Query = makeTaggedRequestWithMeta(moduleName, "query") - const Command = makeTaggedRequestWithMeta(moduleName, "command") - - return { - moduleName, - /** - * Create query request classes for this module. - * Queries read state and should not mutate server state. - * Pass `stream: true` in the config to produce a Stream of `success` values (QueryStream behaviour). - */ - Query, - /** - * Create command request classes for this module. - * Commands mutate state and should avoid returning complex read models. - * Pass `stream: true` in the config to produce a Stream of `success` values (CommandStream behaviour). - */ - Command - } as const - } - - return { - TaggedRequestFor - } -} diff --git a/packages/effect-app/src/faker.ts b/packages/effect-app/src/faker.ts deleted file mode 100644 index 601683305..000000000 --- a/packages/effect-app/src/faker.ts +++ /dev/null @@ -1,32 +0,0 @@ -// FILE HAS SIDE EFFECTS! -import type { Faker } from "@faker-js/faker" -import * as FC from "fast-check" - -// TODO: inject faker differently, so we dont care about multiple instances of library. - -// eslint-disable-next-line prefer-const -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let faker: Faker = undefined as any as Faker -export function setFaker(f: Faker) { - faker = f -} - -export function getFaker() { - if (!faker) throw new Error("You forgot to load faker library") - return faker -} - -export const fakerToArb = (fakerGen: () => T) => (fc: typeof FC) => { - return FC.noBias(FC.noShrink( - fc - .integer() - .map((seed) => { - faker.seed(seed) // seed the generator - return fakerGen() // call it - }) - )) -} - -export const fakerArb = ( - gen: (fake: Faker) => () => T -): (a: any) => FC.Arbitrary => fakerToArb(() => gen(getFaker())()) diff --git a/packages/effect-app/src/http.ts b/packages/effect-app/src/http.ts deleted file mode 100644 index 51bad709f..000000000 --- a/packages/effect-app/src/http.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./http/internal/lib.ts" diff --git a/packages/effect-app/src/http/Request.ts b/packages/effect-app/src/http/Request.ts deleted file mode 100644 index c42206ee1..000000000 --- a/packages/effect-app/src/http/Request.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { HttpClientResponse } from "effect/http/HttpClientResponse" -import * as Option from "effect/Option" -import * as Effect from "../Effect.ts" -import { HttpClient, HttpClientError, HttpClientRequest, HttpHeaders } from "./internal/lib.ts" - -export interface ResponseWithBody extends Pick { - readonly body: A -} - -// TODO: consider rebuilding the text/json helpers to use a cached effect -// https://discord.com/channels/795981131316985866/1098177242598756412/1168565257569046712 - -export const responseWithJsonBody = ( - response: HttpClientResponse -) => - Effect.map(response.json, (body): ResponseWithBody => ({ - body, - headers: response.headers, - status: response.status, - remoteAddress: response.remoteAddress - })) - -export const demandJson = (client: HttpClient.HttpClient) => - HttpClient - .mapRequest(client, (_) => HttpClientRequest.acceptJson(_)) - .pipe(HttpClient.transform((r, request) => - Effect.tap(r, (response) => - Option - .exists( - HttpHeaders.get(response.headers, "Content-Type"), - (_) => _.startsWith("application/json") - ) - ? Effect.void - : Effect.fail( - new HttpClientError.DecodeError({ - request, - response, - description: "not json response: " - + Option.getOrElse(HttpHeaders.get(response.headers, "Content-Type"), () => "") - }) - )) - )) diff --git a/packages/effect-app/src/http/internal/lib.ts b/packages/effect-app/src/http/internal/lib.ts deleted file mode 100644 index 6c035c281..000000000 --- a/packages/effect-app/src/http/internal/lib.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * as HttpHeaders from "effect/http/Headers" -export * as HttpBody from "effect/http/HttpBody" -export * as HttpClient from "effect/http/HttpClient" -export * as HttpClientError from "effect/http/HttpClientError" -export * as HttpClientRequest from "effect/http/HttpClientRequest" -export * as HttpClientResponse from "effect/http/HttpClientResponse" -// HttpLayerRouter removed in v4 — use HttpRouter instead -export * as HttpMiddleware from "effect/http/HttpMiddleware" -export * as HttpRouter from "effect/http/HttpRouter" -export * as HttpServer from "effect/http/HttpServer" -export * as HttpServerError from "effect/http/HttpServerError" -export * as HttpServerRequest from "effect/http/HttpServerRequest" -export * as HttpServerResponse from "effect/http/HttpServerResponse" diff --git a/packages/effect-app/src/ids.ts b/packages/effect-app/src/ids.ts deleted file mode 100644 index 09dc25f4b..000000000 --- a/packages/effect-app/src/ids.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { brandedStringId, type Codec, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake } from "effect-app/Schema" -import type { B } from "effect-app/Schema/schema" -import * as Effect from "effect/Effect" -import type { Simplify } from "effect/Types" -import * as S from "./Schema.ts" -import { extendM } from "./utils.ts" - -export interface RequestIdBrand extends StringIdBrand { - readonly RequestId: unique symbol -} - -export type RequestId = NonEmptyString255 -/** - * Schema for a request id. - * - * A request id may be made from a span id, which does not comply with the - * `StringId` schema (hence the looser `NonEmptyString255` base). - * - * `.withConstructorDefault` => fresh `StringId` (construction-only; not - * applied during decode — cannot be used to JIT-migrate database fields). - * See `./Schema/ext.ts` for the full policy note. - */ -const requestIdSamples = S.makeFilter(() => undefined, { - arbitraryConstraint: { - minLength: 21, - maxLength: 21, - patterns: [{ source: "^[A-Za-z0-9_-]{21}$", flags: "" }] - } -}) - -export const RequestId = extendM( - Object - .assign( - Object.create(NonEmptyString255) as {}, - NonEmptyString255.pipe(S.check(requestIdSamples)) as unknown as Codec - ), - (s) => { - function make(): NonEmptyString255 - function make(input: string, options?: S.MakeOptions): NonEmptyString255 - function make(input?: string, options?: S.MakeOptions): NonEmptyString255 { - const id = input === undefined ? StringId.make() : StringId.make(input, options) - return id - } - return ({ - make, - /** - * Construction-only default: fresh `StringId`. Applied only when the - * field is omitted from `.make(...)` input. NOT applied during decode — - * cannot be used to JIT-migrate database fields. See `./Schema/ext.ts` - * file-level note. - */ - withConstructorDefault: S.withConstructorDefault(Effect.sync(make))(s as typeof s & S.WithoutConstructorDefault) - }) - } -) - .pipe(withDefaultMake) - -export interface UserProfileIdBrand extends Simplify & StringIdBrand> {} -export type UserProfileId = string & UserProfileIdBrand -/** - * Branded `StringId` for user profiles. - * - * Exposes `.withConstructorDefault` (fresh id) — construction-only; not - * applied during decode. See `./Schema/ext.ts` for the full policy note. - */ -export const UserProfileId = brandedStringId() diff --git a/packages/effect-app/src/index.ts b/packages/effect-app/src/index.ts deleted file mode 100644 index 5f15b05be..000000000 --- a/packages/effect-app/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -// eslint-disable-next-line import/no-unassigned-import -import "./builtin.ts" - -export * as Fnc from "./Function.ts" -export * as Utils from "./utils.ts" - -export * as Array from "./Array.ts" -export * as Config from "./Config.ts" -export * as ConfigProvider from "./ConfigProvider.ts" -export * as Context from "./Context.ts" -export * as Effect from "./Effect.ts" -export * as Layer from "./Layer.ts" -export * as Model from "./Model.ts" -export * as NonEmptySet from "./NonEmptySet.ts" -export * as Set from "./Set.ts" -export * as Store from "./Store.ts" - -export { type NonEmptyArray, type NonEmptyReadonlyArray } from "./Array.ts" - -export * from "effect" - -export type * as Types from "./Types.ts" - -export * as SecretURL from "./Config/SecretURL.ts" -export * as RpcX from "./rpc.ts" -export * as S from "./Schema.ts" -export { copy } from "./utils.ts" diff --git a/packages/effect-app/src/logger.ts b/packages/effect-app/src/logger.ts deleted file mode 100644 index 3b1c9822b..000000000 --- a/packages/effect-app/src/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { makeLog } from "effect-app/utils/logger" - -export const PreludeLogger = makeLog("effect-app", "info") diff --git a/packages/effect-app/src/middleware.ts b/packages/effect-app/src/middleware.ts deleted file mode 100644 index 4cb4937af..000000000 --- a/packages/effect-app/src/middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as Context from "effect-app/Context" -import { RpcMiddleware } from "./rpc.ts" - -export class DevMode extends Context.Reference("DevMode", { defaultValue: () => false }) {} - -export class RequestCacheMiddleware extends RpcMiddleware.Tag()("RequestCacheMiddleware") {} - -export class ConfigureInterruptibilityMiddleware - extends RpcMiddleware.Tag()("ConfigureInterruptibilityMiddleware") -{} - -export class LoggerMiddleware extends RpcMiddleware.Tag()("LoggerMiddleware") {} - -export class DevModeMiddleware extends RpcMiddleware.Tag()("DevModeMiddleware") {} - -/** - * Generic middlewares attached by `makeRouter` to every request. - * - * Invalidation key wrap/unwrap is handled by the routing layer (server) and the - * api client factory (client) directly — there is no middleware tag for it. - */ -export const DefaultGenericMiddlewares = [ - RequestCacheMiddleware, - ConfigureInterruptibilityMiddleware, - LoggerMiddleware, - DevModeMiddleware -] as const diff --git a/packages/effect-app/src/migration/nativeEffect.ts b/packages/effect-app/src/migration/nativeEffect.ts deleted file mode 100644 index 59dc42e43..000000000 --- a/packages/effect-app/src/migration/nativeEffect.ts +++ /dev/null @@ -1,645 +0,0 @@ -/** - * Rewrites source that imported effect-app helpers now provided by Effect. - * - * The rewrite is textual and keeps the rest of the file. It understands named - * imports, aliases, and `import * as` namespaces from the effect-app entry points - * listed in {@link nativeEffectReplacements}. - */ - -export interface NativeEffectReplacement { - readonly module: string - readonly name: string - /** Expression that replaces a reference to `name`. */ - readonly expression: string - readonly note: string - readonly imports: ReadonlyArray - /** When set, a call `name(...args)` is rewritten instead of the bare identifier. */ - readonly call?: "date-add" | "date-sub" | "chunk" -} - -const struct = [["Struct", "effect/Struct"]] as const -const record = [["Record", "effect/Record"]] as const -const str = [["String", "effect/String"]] as const -const option = [["Option", "effect/Option"]] as const -const array = [["Array", "effect/Array"]] as const -const chunk = [["Chunk", "effect/Chunk"]] as const -const tuple = [["Tuple", "effect/Tuple"]] as const -const effect = [["Effect", "effect/Effect"]] as const -const dateTime = [["DateTime", "effect/DateTime"], ["pipeZonedLocal", "effect-app/DateTime"]] as const -const deferred = [["Deferred", "effect/Deferred"]] as const -const result = [["Result", "effect/Result"]] as const -const absurd = [["absurd", "effect/Function"]] as const - -export const nativeEffectReplacements: ReadonlyArray = [ - { - module: "effect-app/utils", - name: "typedKeysOf", - expression: "Struct.keys", - imports: struct, - note: "Struct.keys returns `(keyof T & string)[]`." - }, - { - module: "effect-app/utils", - name: "typedValuesOf", - expression: "Record.values", - imports: record, - note: - "Record.values reads a record with one value type. When each value's type depends on its key, use Object.values." - }, - { - module: "effect-app/utils", - name: "capitalize", - expression: "String.capitalize", - imports: str, - note: "Same first-character capitalization, including the empty string." - }, - { - module: "effect-app/utils", - name: "uncapitalize", - expression: "String.uncapitalize", - imports: str, - note: "Same first-character uncapitalization, including the empty string." - }, - { - module: "effect-app/utils", - name: "assertUnreachable", - expression: "absurd", - imports: absurd, - note: "absurd throws when an impossible value is reached. The error text no longer includes the value." - }, - { - module: "effect-app/Function", - name: "tuple", - expression: "Tuple.make", - imports: tuple, - note: "Tuple.make returns a mutable tuple. It is assignable wherever a readonly tuple was expected." - }, - { - module: "effect-app/Array", - name: "toNonEmptyArray", - expression: "Option.liftPredicate(Array.isReadonlyArrayNonEmpty)", - imports: [...option, ...array], - note: "Option of the same readonly array when it is non-empty." - }, - { - module: "effect-app/Array", - name: "NEROArrayFromArray", - expression: "Option.liftPredicate(Array.isReadonlyArrayNonEmpty)", - imports: [...option, ...array], - note: "Same as toNonEmptyArray." - }, - { - module: "effect-app/Array", - name: "NEAFromArray", - expression: "Option.liftPredicate(Array.isArrayNonEmpty)", - imports: [...option, ...array], - note: "Option of the same mutable array when it is non-empty." - }, - { - module: "effect-app/Array", - name: "chunk_", - expression: "Array.chunksOf", - imports: [...array, ...chunk], - call: "chunk", - note: "Was a Chunk of mutable slices. Now a Chunk of Array.chunksOf groups. An empty input is an empty Chunk." - }, - { - module: "effect-app/Array", - name: "_chunk_", - expression: "Array.chunksOf", - imports: array, - note: "The generator form is gone. Array.chunksOf returns the groups directly." - }, - { - module: "effect-app/Option", - name: "toBool", - expression: "Option.isSome", - imports: option, - note: "isSome is the boolean test and also narrows to Some." - }, - { - module: "effect-app/Effect", - name: "annotateLogscoped", - expression: "Effect.annotateLogsScoped", - imports: effect, - note: "The old name was a typo of Effect.annotateLogsScoped." - }, - { - module: "effect-app/Effect", - name: "await_", - expression: "Deferred.await", - imports: deferred, - note: "Await a Deferred. Effect.await_ was Deferred.await." - }, - { - module: "effect-app/Chunk", - name: "findFirstSimple", - expression: "Chunk.findFirst", - imports: chunk, - note: "The alias passed the chunk first. Chunk.findFirst is dual, so the data-first call still typechecks." - }, - { - module: "effect-app/Chunk", - name: "findLastSimple", - expression: "Chunk.findLast", - imports: chunk, - note: "Alias of Chunk.findLast." - }, - { - module: "effect-app/Chunk", - name: "ChunkPartition", - expression: "Chunk.partition", - imports: chunk, - note: "Alias of Chunk.partition." - }, - { - module: "effect-app/Chunk", - name: "uniq", - expression: - "(equivalence) => (self) => Chunk.fromIterable(Array.dedupeWith(Chunk.toReadonlyArray(self), equivalence))", - imports: [...chunk, ...array], - note: "Keeps the first match under a custom equivalence. Equal values can use Chunk.dedupe instead." - }, - { - module: "effect-app/Chunk", - name: "elem", - expression: "(equivalence, value) => Chunk.containsWith(equivalence)(value)", - imports: chunk, - note: "Equivalence is treated as symmetric. containsWith applies it as equivalence(needle, element)." - }, - { - module: "effect-app/Schema", - name: "DateValid", - expression: "Date", - imports: [["Date", "effect-app/Schema"]], - note: "DateValid was an alias of effect-app Schema.Date. Schema.Date already rejects invalid dates." - }, - { - module: "effect-app/_ext/misc", - name: "encaseMaybeInEffect_", - expression: "Effect.fromOption", - imports: effect, - note: "Effect.fromOption(option, onNone) fails with the lazy error on None." - }, - { - module: "effect-app/_ext/misc", - name: "encaseMaybeEither_", - expression: "Result.fromOption", - imports: result, - note: "Result.fromOption(option, onNone)." - }, - { - module: "effect-app/_ext/misc", - name: "toNullable", - expression: "(effect) => Effect.map(effect, Option.getOrNull)", - imports: [...effect, ...option], - note: "Maps a successful Option to its value or null." - }, - { - module: "effect-app/_ext/misc", - name: "flatMapScoped", - expression: "(scoped, f) => Effect.scoped(Effect.flatMap(scoped, f))", - imports: effect, - note: "Closes the scope after the continuation." - }, - { - module: "effect-app/_ext/misc", - name: "scope", - expression: "(scoped, effect) => Effect.scoped(Effect.andThen(scoped, effect))", - imports: effect, - note: "Runs the second effect inside the first effect's scope, then closes it." - }, - ...([ - ["DateAddDays", "days", "date-add"], - ["DateAddHours", "hours", "date-add"], - ["DateAddMinutes", "minutes", "date-add"], - ["DateAddSeconds", "seconds", "date-add"], - ["DateAddWeeks", "weeks", "date-add"], - ["DateAddMonths", "months", "date-add"], - ["DateAddYears", "years", "date-add"], - ["DateSubDays", "days", "date-sub"], - ["DateSubHours", "hours", "date-sub"], - ["DateSubMinutes", "minutes", "date-sub"], - ["DateSubSeconds", "seconds", "date-sub"], - ["DateSubWeeks", "weeks", "date-sub"], - ["DateSubMonths", "months", "date-sub"], - ["DateSubYears", "years", "date-sub"] - ] as const) - .map(([name, unit, call]): NativeEffectReplacement => ({ - module: "effect-app/_ext/date", - name, - expression: unit, - imports: dateTime, - call, - note: - "pipeZonedLocal attaches the process timezone, runs DateTime.add or DateTime.subtract, and returns the instant with toDateUtc." - })) -] - -interface Binding { - readonly replacement: NativeEffectReplacement - readonly local: string -} - -const byModule = new Map>() -for (const replacement of nativeEffectReplacements) { - const existing = byModule.get(replacement.module) ?? new Map() - existing.set(replacement.name, replacement) - byModule.set(replacement.module, existing) -} - -const moduleNames = new Set(byModule.keys()) - -interface Rendered { - readonly text: string - readonly end: number -} - -/** Rewrite one source file. `source` is the file text. */ -export const migrateEffectAppSource = (source: string): string => { - const imports = findImports(source) - const bindings = new Map() - const namespaceBindings = new Map>() - const edits: Array<{ start: number; end: number; text: string }> = [] - const needed = new Map>() - const maskedSpans: Array<{ start: number; end: number }> = [] - - for (const statement of imports) { - const table = byModule.get(statement.module) - if (table === undefined) continue - maskedSpans.push({ start: statement.start, end: statement.end }) - - if (statement.namespace !== undefined) { - namespaceBindings.set(statement.namespace, table) - continue - } - - const removed: Array = [] - for (const specifier of statement.specifiers) { - const replacement = table.get(specifier.imported) - if (replacement === undefined) continue - bindings.set(specifier.local, { replacement, local: specifier.local }) - removed.push(specifier) - rememberImports(needed, replacement) - } - if (removed.length === 0) continue - if (removed.length === statement.specifiers.length) { - edits.push({ start: statement.start, end: statement.end, text: "" }) - } else { - const kept = statement.specifiers.filter((specifier) => !removed.includes(specifier)) - edits.push({ - start: statement.clauseStart, - end: statement.clauseEnd, - text: `{ ${kept.map((specifier) => specifier.text).join(", ")} }` - }) - } - } - - const mask = maskSpans(maskNonCode(source), maskedSpans) - - for (const [local, binding] of bindings) { - for (const match of identifierSpans(mask, local)) { - const rendered = render(binding.replacement, source, match + local.length) - edits.push({ start: match, end: rendered.end, text: rendered.text }) - } - } - - for (const [namespace, table] of namespaceBindings) { - let used = false - for (const match of identifierSpans(mask, namespace)) { - const after = skipSpaces(source, match + namespace.length) - if (source[after] !== ".") continue - const propertyStart = after + 1 - const property = readIdentifier(source, propertyStart) - if (property === undefined) continue - const replacement = table.get(property.name) - if (replacement === undefined) { - used = true - continue - } - rememberImports(needed, replacement) - const rendered = render(replacement, source, propertyStart + property.name.length) - edits.push({ start: match, end: rendered.end, text: rendered.text }) - } - if (!used) { - const statement = imports.find((item) => item.namespace === namespace && byModule.has(item.module)) - if (statement !== undefined) edits.push({ start: statement.start, end: statement.end, text: "" }) - } - } - - return insertImports(applyEdits(source, edits), needed) -} - -const render = (replacement: NativeEffectReplacement, source: string, afterName: number): Rendered => { - const bare = { text: replacement.expression, end: afterName } - if (replacement.call === undefined) return bare - const open = skipSpaces(source, afterName) - if (source[open] !== "(") return bare - const args = splitArgs(source, open) - if (args === undefined) return bare - if (replacement.call === "chunk" && args.args.length === 2) { - return { - text: `Chunk.fromIterable(Array.chunksOf(${args.args[0]}, ${args.args[1]}))`, - end: args.end - } - } - if ((replacement.call === "date-add" || replacement.call === "date-sub") && args.args.length === 2) { - const method = replacement.call === "date-add" ? "add" : "subtract" - return { - text: `pipeZonedLocal(${args.args[0]}, DateTime.${method}({ ${replacement.expression}: ${args.args[1]} }))`, - end: args.end - } - } - return bare -} - -const rememberImports = (needed: Map>, replacement: NativeEffectReplacement) => { - for (const [local, from] of replacement.imports) { - const names = needed.get(from) ?? new Set() - names.add(local) - needed.set(from, names) - } -} - -interface Specifier { - readonly imported: string - readonly local: string - readonly text: string -} - -interface ImportStatement { - readonly start: number - readonly end: number - readonly clauseStart: number - readonly clauseEnd: number - readonly module: string - readonly namespace?: string - readonly specifiers: ReadonlyArray -} - -const findImports = (source: string): ReadonlyArray => { - const found: Array = [] - const pattern = /\bimport\s+(type\s+)?([\s\S]*?)\s+from\s+["']([^"']+)["']/g - for (const match of source.matchAll(pattern)) { - const clause = match[2] ?? "" - const moduleName = match[3] ?? "" - if (!moduleNames.has(moduleName)) continue - const start = lineStart(source, match.index ?? 0) - const end = lineEnd(source, (match.index ?? 0) + match[0].length) - const clauseStart = (match.index ?? 0) + match[0].indexOf(clause) - const namespace = /^\*\s+as\s+([A-Za-z_$][\w$]*)/.exec(clause.trim()) - found.push({ - start, - end, - clauseStart, - clauseEnd: clauseStart + clause.length, - module: moduleName, - ...(namespace?.[1] === undefined ? {} : { namespace: namespace[1] }), - specifiers: namespace === null ? parseSpecifiers(clause) : [] - }) - } - return found -} - -const parseSpecifiers = (clause: string): ReadonlyArray => { - const brace = clause.indexOf("{") - if (brace < 0) return [] - const end = clause.lastIndexOf("}") - const body = clause.slice(brace + 1, end) - return body.split(",").flatMap((part) => { - const text = part.trim() - if (text === "" || text.startsWith("type ")) return [] - const alias = /^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/.exec(text) - if (alias === null) return [] - return [{ imported: alias[1]!, local: alias[2] ?? alias[1]!, text }] - }) -} - -const escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") - -const identifierSpans = (mask: string, name: string): ReadonlyArray => { - const spans: Array = [] - const pattern = new RegExp(`(? { - const match = /^[A-Za-z_$][\w$]*/.exec(source.slice(index)) - return match === null ? undefined : { name: match[0] } -} - -const splitArgs = ( - source: string, - open: number -): { readonly args: ReadonlyArray; readonly end: number } | undefined => { - const balanced = readBalanced(source, open, "(", ")") - if (balanced === undefined) return undefined - const args: Array = [] - let depth = 0 - let quote: "'" | "\"" | "`" | undefined - let start = 0 - const body = balanced.body - for (let i = 0; i < body.length; i++) { - const char = body[i] - if (quote !== undefined) { - if (char === "\\") { - i++ - continue - } - if (char === quote) quote = undefined - continue - } - if (char === "'" || char === "\"" || char === "`") { - quote = char - continue - } - if (char === "(" || char === "[" || char === "{") depth++ - if (char === ")" || char === "]" || char === "}") depth-- - if (char === "," && depth === 0) { - args.push(body.slice(start, i).trim()) - start = i + 1 - } - } - const last = body.slice(start).trim() - if (last !== "") args.push(last) - return { args, end: balanced.end } -} - -const readBalanced = ( - source: string, - open: number, - left: string, - right: string -): { readonly body: string; readonly end: number } | undefined => { - if (source[open] !== left) return undefined - let depth = 0 - let quote: "'" | "\"" | "`" | undefined - for (let i = open; i < source.length; i++) { - const char = source[i] - if (quote !== undefined) { - if (char === "\\") { - i++ - continue - } - if (char === quote) quote = undefined - continue - } - if (char === "'" || char === "\"" || char === "`") { - quote = char - continue - } - if (char === left) depth++ - if (char === right) { - depth-- - if (depth === 0) return { body: source.slice(open + 1, i), end: i + 1 } - } - } - return undefined -} - -const maskSpans = (mask: string, spans: ReadonlyArray<{ start: number; end: number }>): string => { - const chars = [...mask] - for (const span of spans) { - for (let i = span.start; i < span.end && i < chars.length; i++) { - if (chars[i] !== "\n") chars[i] = " " - } - } - return chars.join("") -} - -const skipSpaces = (source: string, index: number): number => { - let i = index - while (i < source.length && (source[i] === " " || source[i] === "\n" || source[i] === "\t")) i++ - return i -} - -const lineStart = (source: string, index: number): number => { - const start = source.lastIndexOf("\n", index - 1) - return start < 0 ? 0 : start + 1 -} - -const lineEnd = (source: string, index: number): number => { - const end = source.indexOf("\n", index) - return end < 0 ? source.length : end + 1 -} - -const applyEdits = (source: string, edits: ReadonlyArray<{ start: number; end: number; text: string }>): string => { - const ordered = [...edits].sort((a, b) => b.start - a.start) - let next = source - for (const edit of ordered) next = next.slice(0, edit.start) + edit.text + next.slice(edit.end) - return next -} - -const insertImports = (source: string, needed: ReadonlyMap>): string => { - const lines: Array = [] - for (const [from, names] of needed) { - for (const name of names) { - if (hasImport(source, name, from)) continue - lines.push( - namedImportNames.has(name) - ? `import { ${name} } from "${from}"` - : `import * as ${name} from "${from}"` - ) - } - } - if (lines.length === 0) return source.replace(/^\n+/, "") - const lastImport = lastImportEnd(source) - const block = `${lines.sort().join("\n")}\n` - if (lastImport === 0) return `${block}${source.replace(/^\n+/, "")}` - return `${source.slice(0, lastImport)}${block}${source.slice(lastImport)}`.replace(/^\n+/, "") -} - -const namedImportNames = new Set(["absurd", "Date", "pipeZonedLocal"]) - -const hasImport = (source: string, name: string, from: string): boolean => { - const fromPattern = from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") - const namespace = new RegExp(`import\\s+\\*\\s+as\\s+${name}\\s+from\\s+["']${fromPattern}["']`) - const named = new RegExp(`import\\s+(type\\s+)?\\{[^}]*\\b${name}\\b[^}]*}\\s+from\\s+["']${fromPattern}["']`) - const barrel = name === name[0]?.toUpperCase() - ? new RegExp(`import\\s+(type\\s+)?\\{[^}]*\\b${name}\\b[^}]*}\\s+from\\s+["']effect["']`) - : undefined - return namespace.test(source) || named.test(source) || barrel?.test(source) === true -} - -const lastImportEnd = (source: string): number => { - const pattern = /\bimport\s+(type\s+)?[\s\S]*?\s+from\s+["'][^"']+["']/g - let end = 0 - for (const match of source.matchAll(pattern)) { - end = lineEnd(source, (match.index ?? 0) + match[0].length) - } - return end -} - -const maskNonCode = (source: string): string => { - const chars = [...source] - let i = 0 - const blank = (from: number, to: number) => { - for (let j = from; j < to; j++) { - if (chars[j] !== "\n") chars[j] = " " - } - } - while (i < source.length) { - const two = source.slice(i, i + 2) - if (two === "//") { - const end = source.indexOf("\n", i) - blank(i, end < 0 ? source.length : end) - i = end < 0 ? source.length : end - continue - } - if (two === "/*") { - const end = source.indexOf("*/", i + 2) - const stop = end < 0 ? source.length : end + 2 - blank(i, stop) - i = stop - continue - } - const char = source[i] - if (char === "'" || char === "\"") { - const stop = scanQuoted(source, i, char) - blank(i, stop) - i = stop - continue - } - if (char === "`") { - const stop = scanTemplate(source, i) - blank(i, stop) - i = stop - continue - } - i++ - } - return chars.join("") -} - -const scanQuoted = (source: string, start: number, quote: "'" | "\""): number => { - for (let i = start + 1; i < source.length; i++) { - if (source[i] === "\\") { - i++ - continue - } - if (source[i] === quote) return i + 1 - } - return source.length -} - -const scanTemplate = (source: string, start: number): number => { - for (let i = start + 1; i < source.length; i++) { - if (source[i] === "\\") { - i++ - continue - } - if (source[i] === "`") return i + 1 - if (source.slice(i, i + 2) === "${") { - let depth = 1 - i += 2 - while (i < source.length && depth > 0) { - if (source[i] === "{") depth++ - else if (source[i] === "}") depth-- - i++ - } - i-- - } - } - return source.length -} diff --git a/packages/effect-app/src/rpc.ts b/packages/effect-app/src/rpc.ts deleted file mode 100644 index e07797b67..000000000 --- a/packages/effect-app/src/rpc.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * as Invalidation from "./rpc/Invalidation.ts" -export * as MiddlewareMaker from "./rpc/MiddlewareMaker.ts" -export * as RpcContextMap from "./rpc/RpcContextMap.ts" -export * as RpcMiddleware from "./rpc/RpcMiddleware.ts" diff --git a/packages/effect-app/src/rpc/Invalidation.ts b/packages/effect-app/src/rpc/Invalidation.ts deleted file mode 100644 index 2e6db6dee..000000000 --- a/packages/effect-app/src/rpc/Invalidation.ts +++ /dev/null @@ -1,278 +0,0 @@ -import * as Ref from "effect/Ref" -import { Rpc } from "effect/rpc" -import { type ClientForOptions, makeQueryKey } from "../client/clientFor.ts" -import * as Context from "../Context.ts" -import * as DataDependencies from "../DataDependencies.ts" -import * as Effect from "../Effect.ts" -import * as S from "../Schema.ts" - -/** - * Shorthand for a handler-derived invalidation key. - * Accepts an RPC handler object so `add(GetMe)` is equivalent to - * `add(makeQueryKey(GetMe))`. - */ -export type InvalidationKeyInput = InvalidationKey | { readonly id: string; readonly options?: ClientForOptions } - -const normalizeKey = (input: InvalidationKeyInput): InvalidationKey => { - if (Array.isArray(input)) return input - const handler = input as { id: string; options?: ClientForOptions } - return makeQueryKey(handler.options ? { id: handler.id, options: handler.options } : { id: handler.id }) -} - -// Disambiguates `add` overloads: a `string[]` means a single key; an array -// containing arrays-or-objects means a batch of inputs. -const isBatch = ( - input: InvalidationKeyInput | ReadonlyArray -): input is ReadonlyArray => - Array.isArray(input) && input[0] !== undefined && typeof input[0] !== "string" - -const normalizeInputs = ( - input: InvalidationKeyInput | ReadonlyArray -): ReadonlyArray => isBatch(input) ? input.map(normalizeKey) : [normalizeKey(input)] - -/** - * A single segment within an `InvalidationKey` array. - * Accepts any JSON-compatible value: string, number, boolean, null, - * arrays and objects recursively — matching TanStack Query's `queryKey` element type. - */ -export const InvalidationKeySegment = S.Json -export type InvalidationKeySegment = S.Schema.Type - -/** Schema for a single invalidation key – an array of segments compatible with TanStack Query `queryKey`. */ -export const InvalidationKey = S.Array(InvalidationKeySegment) -export type InvalidationKey = S.Schema.Type - -/** Schema for the full set of invalidation keys – an array of `InvalidationKey`. */ -export const InvalidationKeys = S.Array(InvalidationKey) -export type InvalidationKeys = S.Schema.Type - -/** Metadata included in every command response for server-driven cache invalidation. */ -export const CommandMetaData = S.Struct({ - invalidateQueries: InvalidationKeys, - dataDependencies: DataDependencies.DataDependencySet -}) -export type CommandMetaData = S.Schema.Type - -export const emptyDataDependencies: DataDependencies.DataDependencySet = { - reads: DataDependencies.empty(), - writes: DataDependencies.empty() -} - -export const makeMetaData = ( - invalidateQueries: InvalidationKeys, - dataDependencies: DataDependencies.DataDependencySet = emptyDataDependencies -): CommandMetaData => ({ invalidateQueries, dataDependencies }) - -export const QueryResponseWithMetaData = (success: S) => - S.Struct({ payload: success, metadata: S.Struct({ dataDependencies: DataDependencies.DataDependencySet }) }) - -/** - * Wraps a command's success schema so that the wire format carries both the `payload` - * (the handler's actual return value) and `metadata` (server-driven cache invalidation keys). - * Transparent to users: the server handler returns the plain payload and the client receives - * the plain payload — wrapping/unwrapping is handled internally by the routing layer. - */ -export const CommandResponseWithMetaData = (success: S) => - S.Struct({ payload: success, metadata: CommandMetaData }) - -/** - * Wraps a command's failure schema so that the wire format carries both the `error` - * (the handler's actual failure value) and `metadata` (server-driven cache invalidation keys - * accumulated thus far before the failure occurred). - * Transparent to users: the server handler fails with the plain error and the client receives - * the plain error — wrapping/unwrapping is handled internally by the routing layer. - */ -export const CommandFailureWithMetaData = (error: E) => - S.Struct({ _tag: S.Literal("CommandFailureWithMetaData"), error, metadata: CommandMetaData }) - -/** - * Stream chunk schema for stream responses with metadata. - * Each item is either a data value, an intermediate "metadata" signal carrying cache - * invalidation keys accumulated since the previous drain, or a final "done" signal. - * Transparent to users: stream handlers return plain values and clients receive plain values — - * wrapping/unwrapping is handled internally by the routing layer. - * - * The "done" chunk is always the last item in the stream and carries any remaining invalidation - * keys. An optional "metadata" chunk may appear after any "value" chunk and carries keys - * accumulated since the last drain (V3: mid-stream invalidation). - */ -export const StreamResponseChunk = (success: S) => - S.Union([ - S.Struct({ _tag: S.Literal("value"), value: success }), - S.Struct({ _tag: S.Literal("metadata"), metadata: CommandMetaData }), - S.Struct({ _tag: S.Literal("done"), metadata: CommandMetaData }) - ]) - -export type StreamResponseChunk = - | { readonly _tag: "value"; readonly value: A } - | { readonly _tag: "metadata"; readonly metadata: CommandMetaData } - | { readonly _tag: "done"; readonly metadata: CommandMetaData } - -/** - * Stream chunk schema for stream failures with metadata. - * Used to signal a stream failure while still carrying cache invalidation keys - * accumulated thus far. - */ -export const StreamFailureChunk = (error: E) => - S.Struct({ _tag: S.Literal("error"), error, metadata: CommandMetaData }) - -export type StreamFailureChunk = { readonly _tag: "error"; readonly error: E; readonly metadata: CommandMetaData } - -/** - * Context annotation for declaring static cache invalidation keys on a low-level `Rpc` definition. - * These keys are always included in the command response metadata, regardless of the handler logic. - * - * Prefer using `makeQueryKey` over raw string arrays to stay in sync with the actual query - * definitions without manual string maintenance: - * - * ```ts - * import { makeQueryKey } from "effect-app/client" - * import { Invalidation } from "effect-app/rpc" - * import * as UserRsc from "../User/index.ts" // separate module to avoid circular deps - * - * class UpdateProfile extends Rpc.make("UpdateProfile", { ... }) - * .annotate(Invalidation.Invalidates, [makeQueryKey(UserRsc.GetMe), makeQueryKey(UserRsc.GetProfile)]) {} - * ``` - * - * **Circular dependency note:** if mutations and queries live in the same file you may hit a - * circular reference at evaluation time. The idiomatic fix is to move mutations into their own - * module (e.g. `User/mutations.ts`) that directly imports the relevant query classes rather than - * re-exporting them through a barrel. - * - * For the higher-level `Command`/`Query` builders from `makeRpcClient`, use the - * `invalidatesQueries` callback argument instead (it receives the same query keys at runtime). - */ -export const Invalidates = Context.Reference>( - "effect-app/rpc/Invalidates", - { defaultValue: () => [] } -) -export type Invalidates = typeof Invalidates - -/** The shape of the per-request service that accumulates invalidation keys. */ -export interface InvalidationSetService { - readonly add: ( - input: InvalidationKeyInput | ReadonlyArray - ) => Effect.Effect - readonly get: Effect.Effect> - /** - * V3: Reads all currently accumulated keys and resets the bucket to empty. - * Used by the stream routing layer to emit intermediate "metadata" chunks - * without re-sending keys that have already been forwarded to the client. - */ - readonly drain: Effect.Effect> -} - -/** - * Request-scoped service for accumulating invalidation keys dynamically inside a handler. - * Provided by `InvalidationMiddlewareLive` for every RPC call; has a no-op default so it is - * safe to use even when the HTTP middleware is absent (tests, workers, etc.). - * - * Use `InvalidationSet.add(key)` as a shorthand to skip `.use(_ => _.add(key))`. The - * underlying service is still available via `.use` / `.useSync` for advanced cases. - * - * `add` accepts an RPC handler directly (e.g. `UserRsc.GetMe`) — its query key is derived via - * `makeQueryKey` so keys stay in sync with the actual query definitions. Raw `InvalidationKey` - * arrays and arrays of either form are also accepted. - * - * ```ts - * import * as Effect from "effect/Effect" - * import { Invalidation } from "effect-app/rpc" - * import * as CartRsc from "../Cart/queries.ts" - * import * as UserRsc from "../User/queries.ts" - * - * const handler = Effect.fnUntraced(function*(req: UpdateCartRequest) { - * const cart = yield* CartRepo.save(req.cart) - * - * // single handler - * yield* Invalidation.InvalidationSet.add(UserRsc.GetMe) - * - * // batch - * if (cart.isCheckedOut) { - * yield* Invalidation.InvalidationSet.add([CartRsc.GetCartStats, UserRsc.GetMe]) - * } - * - * return cart - * }) - * ``` - * - * You can combine static (`Invalidates` annotation) and dynamic (`InvalidationSet.use`) keys: - * the annotation pre-populates the set before the handler runs; dynamic additions accumulate - * throughout the handler. All keys are included in the command response metadata. - */ -const InvalidationSetRef = Context.Reference( - "effect-app/rpc/InvalidationSet", - { - defaultValue: () => ({ - add: (_input: InvalidationKeyInput | ReadonlyArray) => Effect.void, - get: Effect.succeed([] as ReadonlyArray), - drain: Effect.succeed([] as ReadonlyArray) - }) - } -) -export const InvalidationSet = Object.assign(InvalidationSetRef, { - /** - * Shortcut for `InvalidationSet.use(_ => _.add(input))`. Accepts a single - * `InvalidationKeyInput` or an array of them. - */ - add: (input: InvalidationKeyInput | ReadonlyArray) => - InvalidationSetRef.use((_) => _.add(input)) -}) -export type InvalidationSet = typeof InvalidationSetRef - -/** Creates a fresh `InvalidationSet` implementation backed by a `Ref`. */ -export const makeInvalidationSet = (ref: Ref.Ref>): InvalidationSetService => ({ - add: (input) => Ref.update(ref, (keys) => [...keys, ...normalizeInputs(input)]), - get: Ref.get(ref), - drain: Ref.getAndSet(ref, []) -}) - -/** - * `Rpc.Custom` definition for command RPCs that wrap the success/error schemas - * with `CommandResponseWithMetaData` / `CommandFailureWithMetaData`. The wrap - * lets server forward accumulated invalidation keys on both success and - * handler-thrown failure paths. Middleware-thrown errors bypass the wrap - * (the handler never ran, so no metadata) and flow raw at the Cause level — - * the client decodes them via the `rpc.middlewares[*].error` failure-union - * channel of `Rpc.exitSchema`. - */ -// eslint-disable-next-line import/namespace -export interface CommandRpc extends Rpc.Custom { - readonly out: Rpc.Custom.Out< - ReturnType>, - ReturnType> - > -} - -/** - * Custom Rpc constructor for command RPCs. - * Wraps the success schema with `CommandResponseWithMetaData` and the error - * schema with `CommandFailureWithMetaData`. - */ -export const makeCommandRpc = Rpc.custom(({ defect, error, success }) => ({ - success: CommandResponseWithMetaData(success), - error: CommandFailureWithMetaData(error), - defect -})) - -/** - * `Rpc.Custom` definition for stream RPCs that wrap the success/error schemas - * with `StreamResponseChunk` / `StreamFailureChunk`. - */ -// eslint-disable-next-line import/namespace -export interface StreamRpc extends Rpc.Custom { - readonly out: Rpc.Custom.Out< - ReturnType>, - ReturnType> - > -} - -/** - * Custom Rpc constructor for stream RPCs. - * Wraps the success schema with `StreamResponseChunk` and - * the error schema with `StreamFailureChunk`. - */ -export const makeStreamRpc = Rpc.custom(({ defect, error, success }) => ({ - success: StreamResponseChunk(success), - error: StreamFailureChunk(error), - defect -})) diff --git a/packages/effect-app/src/rpc/MiddlewareMaker.ts b/packages/effect-app/src/rpc/MiddlewareMaker.ts deleted file mode 100644 index 4c5b67469..000000000 --- a/packages/effect-app/src/rpc/MiddlewareMaker.ts +++ /dev/null @@ -1,492 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array" -import * as Effect from "effect/Effect" -import * as Layer from "effect/Layer" -import { Rpc, type RpcGroup, type RpcSchema } from "effect/rpc" -import { type HandlersFrom } from "effect/rpc/RpcGroup" -import * as S from "effect/Schema" -import type * as Scope from "effect/Scope" -import { type Simplify } from "effect/Types" -import * as Context from "../Context.ts" -import { PreludeLogger } from "../logger.ts" -import { type TypeTestId } from "../TypeTest.ts" -import { type GetContextConfig, type RequestContextMapTagAny, type RpcContextMap } from "./RpcContextMap.ts" -import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewareV4, type TagClassAny } from "./RpcMiddleware.ts" -import * as RpcMiddlewareX from "./RpcMiddleware.ts" - -// adapter for effect/rpc v3 middleware provides. (in effect-smol (v4), it's just a Service Identifier, no tags.) -// hm? -type MakeTags = A - -export interface MiddlewareMaker< - Self, - Id extends string, - RequestContextMap extends Record, - MiddlewareProviders extends ReadonlyArray -> extends - RpcMiddlewareX.TagClass< - Self, - Id, - Simplify< - & (Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? {} : { - readonly requires: MakeTags< - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > - > - }) - & (MiddlewareMaker.ManyErrors extends never ? {} - : { - readonly error: S.Codec> - }) - & (MiddlewareMaker.ManyProvided extends never ? {} - : { readonly provides: MakeTags> }) - >, - { - provides: MiddlewareMaker.ManyProvided extends never ? never - : MakeTags> - requires: Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? never - : MakeTags< - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > - > - } - > -{ - readonly layer: Layer.Layer> - readonly requestContext: RequestContextTag - readonly requestContextMap: RequestContextMap -} - -export interface RequestContextTag> - extends Context.Service<"RequestContextConfig", GetContextConfig> -{} - -export namespace MiddlewareMaker { - export type Any = TagClassAny - - export type ApplyServices = Exclude> | Required - - export type ApplyManyServices, R> = - | Exclude }[number]> - | { [K in keyof A]: Required }[number] - - export type ManyProvided> = A extends NonEmptyReadonlyArray - ? { [K in keyof A]: Provided }[number] - : Provided - export type ManyRequired> = A extends NonEmptyReadonlyArray - ? { [K in keyof A]: Required }[number] - : Required - export type ManyErrors> = A extends NonEmptyReadonlyArray - ? { [K in keyof A]: Errors }[number] - : Errors - - export type Provided = T extends TagClassAny ? T extends { provides: infer _P } ? _P - : never - : never - - export type Errors = T extends TagClassAny ? T extends { error: S.Top } ? T["error"]["Type"] - : never - : never - - export type Required = T extends TagClassAny ? T extends { requires: infer _R } ? _R - : never - : never -} - -// the following implements sort of builder pattern -// we support both sideways and upwards elimination of dependencies - -// it's for dynamic middlewares -type GetDependsOnKeys = MW extends { dependsOn: NonEmptyReadonlyArray } ? { - [K in keyof MW["dependsOn"]]: MW["dependsOn"][K] extends AnyDynamic ? MW["dependsOn"][K]["dynamic"]["key"] - : never - }[keyof MW["dependsOn"]] - : never - -type FilterInDynamicMiddlewares< - MWs extends ReadonlyArray, - RequestContextMap extends Record -> = { - [K in keyof MWs]: MWs[K] extends { dynamic: RpcDynamic } ? MWs[K] - : never -} - -type RecursiveHandleMWsSideways< - MWs, - R extends { - self: any - id: string - rcm: Record - provided: keyof R["rcm"] // that's fine - middlewares: ReadonlyArray - dmp: any - middlewareR: any - } -> = MWs extends [] ? R - : MWs extends [infer F, ...infer Rest extends ReadonlyArray] - ? F extends MiddlewareMaker.Any ? RecursiveHandleMWsSideways[number]["dynamic"]["key"], - // F is fine here because only dynamic middlewares will have 'dependsOn' prop - GetDependsOnKeys - > - middlewares: [...R["middlewares"], F] - dmp: [FilterInDynamicMiddlewares<[F], R["rcm"]>[number]] extends [never] ? R["dmp"] - : - & R["dmp"] - & { - [U in FilterInDynamicMiddlewares<[F], R["rcm"]>[number] as U["dynamic"]["key"]]: U - } - middlewareR: MiddlewareMaker.ApplyManyServices<[F], R["middlewareR"]> - }> - // TypeScript inference fails when checking F extends MiddlewareMaker.Any during F's inference - // if F is a class with static properties - deferring the check avoids this limitation - : `Absurd: F must extend MiddlewareMaker.Any` - : never - -export interface BuildingMiddleware< - Self, - Id extends string, - RequestContextMap extends Record, - Provided extends keyof RequestContextMap, - Middlewares extends ReadonlyArray, - DynamicMiddlewareProviders, - out MiddlewareR extends { _tag: string } = never -> { - rpc: < - const Tag extends string, - Payload extends S.Top | S.Struct.Fields = typeof S.Void, - Success extends S.Top = typeof S.Void, - Error extends S.Top = typeof S.Never, - const Stream extends boolean = false, - Config extends GetContextConfig = {} - >(tag: Tag, options?: { - readonly payload?: Payload - readonly success?: Success - readonly error?: Error - readonly stream?: Stream - readonly config?: Config - readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? (( - payload: Payload extends S.Struct.Fields ? Simplify["Type"]> : Payload["Type"] - ) => string) - : never - }) => - & Rpc.Rpc< - Tag, - Payload extends S.Struct.Fields ? S.Struct : Payload, - Stream extends true ? RpcSchema.Stream : Success, - Stream extends true ? typeof S.Never : Error - > - & { readonly config: Config } - - middleware>( - ...mw: MWs - ): RecursiveHandleMWsSideways extends infer Res extends { - self: any - id: string - rcm: RequestContextMap - provided: keyof RequestContextMap - middlewares: ReadonlyArray - dmp: any - middlewareR: any - } ? MiddlewaresBuilder< - Res["self"], - Res["id"], - Res["rcm"], - Res["provided"], - Res["middlewares"], - Res["dmp"], - Res["middlewareR"] - > - : never - - // helps debugging what are the missing requirements (type only) - readonly [TypeTestId]: { - missingDynamicMiddlewares: Exclude - missingContext: MiddlewareR - } -} - -export type MiddlewaresBuilder< - Self, - Id extends string, - RequestContextMap extends Record, - Provided extends keyof RequestContextMap = never, - Middlewares extends ReadonlyArray = [], - DynamicMiddlewareProviders = unknown, - MiddlewareR extends { _tag: string } = never -> = - & BuildingMiddleware< - Self, - Id, - RequestContextMap, - Provided, - Middlewares, - DynamicMiddlewareProviders, - MiddlewareR - > - & // keyof Omit extends never is true when all the dynamic middlewares are provided - // MiddlewareR is never when all the required services from generic & dynamic middlewares are provided - (keyof Omit extends never ? [MiddlewareR] extends [never] ? MiddlewareMaker< - Self, - Id, - RequestContextMap, - Middlewares - > - : { new(_: never): {} } - : { new(_: never): {} }) - -const middlewareMaker = Effect.fnUntraced(function*< - MiddlewareProviders extends ReadonlyArray ->(middlewares: MiddlewareProviders) { - type Middleware = RpcMiddlewareV4< - MiddlewareMaker.ManyProvided, - MiddlewareMaker.ManyErrors, - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? never - : Exclude, MiddlewareMaker.ManyProvided> - > - type Next = Parameters[0] - type Options = Parameters[1] - - // we want to run them in reverse order because latter middlewares will provide context to former ones - const reversed = middlewares.toReversed() - const context = yield* Effect.context() - - // returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements - // v4: wrap middleware takes (effect, options) as two params instead of a single options bag - return (next: Next, options: Options) => { - // we start with the actual handler - let handler = next - - // inspired from Effect/RpcMiddleware - for (const tag of reversed) { - // use the tag to get the middleware from context - const middleware = Context.getUnsafe(context, tag) - - // wrap the current handler, allowing the middleware to run before and after it - handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe( - Effect.andThen(middleware(handler, options)) - ) as any - } - return handler - } -}) - -const makeMiddlewareBasic = () => -// by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys -< - const Id extends string, - RequestContextMap extends Record, - MiddlewareProviders extends ReadonlyArray ->( - id: Id, - rcm: RequestContextMap, - ...make: MiddlewareProviders -) => { - // reverse middlewares and wrap one after the other - const middleware = middlewareMaker(make) - - // Per-middleware error: union of the static `error` on the tag (if any) AND - // the rcm config entry pointed at by the middleware's `dynamic.key` (if any). - // Reason: middlewares declared with `dynamic: RequestContextMap.get("foo")` - // don't set a static `error` field — at runtime their `.error` defaults to - // `S.Never`. Without pulling from rcm, the composite middleware's - // `.error` collapses to `Never`, and `Rpc.exitSchema` (which walks - // `rpc.middlewares[*].error` to build the wire failure union) can't decode - // the actual middleware-thrown error type. Critical for stream rpcs whose - // top-level `errorSchema` is force-set to `Never` by effect-rpc. - const isMeaningfulError = (e: S.Top | undefined): e is S.Top => e !== undefined && e !== null && e !== S.Never - const rcmRecord = rcm as Record - const failures: Array = make.flatMap((_) => { - const out: Array = [] - if (isMeaningfulError(_.error)) out.push(_.error) - const key = _.dynamic?.key as string | undefined - if (key && rcmRecord[key] && isMeaningfulError(rcmRecord[key].error)) { - out.push(rcmRecord[key].error) - } - return out - }) - const provides = make.flatMap((_) => !_.provides ? [] : Array.isArray(_.provides) ? _.provides : [_.provides]) - const requires = make - .flatMap((_) => !_.requires ? [] : Array.isArray(_.requires) ? _.requires : [_.requires]) - .filter((_) => !provides.includes(_)) - - const [firstFailure, ...restFailures] = failures - - const MiddlewareMaker = RpcMiddlewareX.Tag()(id, { - error: (firstFailure - ? S.Union([firstFailure, ...restFailures]) - : S.Never) as unknown as MiddlewareMaker.ManyErrors extends never ? never - : S.Codec>, - requires: (requires.length > 0 - ? requires - : undefined) as unknown as Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > extends never ? never : [ - MakeTags< - Exclude< - MiddlewareMaker.ManyRequired, - MiddlewareMaker.ManyProvided - > - > - ], - provides: (provides.length > 0 - ? provides - : undefined) as unknown as MiddlewareMaker.ManyProvided extends never ? never - : MakeTags> - }) - - const layer = Layer - .effect( - MiddlewareMaker, - middleware as Effect.Effect< - any - > - // todo; they dont change the type.. - // Effect.Error, - // Effect.Services - ) - - // add to the tag a default implementation - return Object.assign(MiddlewareMaker, { - layer, - // tag to be used to retrieve the RequestContextConfig from Rpc annotations - requestContext: Context.Service<"RequestContextConfig", GetContextConfig>( - "RequestContextConfig" - ), - requestContextMap: rcm - }) -} - -export const Tag = () => -< - const Id extends string, - RequestContextMap extends RequestContextMapTagAny ->(id: Id, rcm: RequestContextMap): MiddlewaresBuilder => { - const allMiddleware: MiddlewareMaker.Any[] = [] - const requestContext = Context.Service<"RequestContextConfig", GetContextConfig>( - "RequestContextConfig" - ) - const it = { - id, - // rpc with config - rpc: < - const Tag extends string, - Payload extends S.Top | S.Struct.Fields = typeof S.Void, - Success extends S.Top = typeof S.Void, - Error extends S.Top = typeof S.Never, - const Stream extends boolean = false, - Config extends GetContextConfig = {} - >(tag: Tag, options?: { - readonly payload?: Payload - readonly success?: Success - readonly error?: Error - readonly stream?: Stream - readonly config?: Config - readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? (( - payload: Payload extends S.Struct.Fields ? Simplify["Type"]> : Payload["Type"] - ) => string) - : never - }): - & Rpc.Rpc< - Tag, - Payload extends S.Struct.Fields ? S.Struct : Payload, - // TODO: enhance `Error`. type based on middleware config. - Stream extends true ? RpcSchema.Stream : Success, - Stream extends true ? typeof S.Never : Error - > - & { config: Config } => - { - const config = options?.config ?? {} as Config - - // The rpc's `error` schema carries ONLY the request's own declared errors. - // Middleware errors (rcm-derived) reach the wire via the middleware tag - // attached to the rpc group later (`RpcGroup.middleware(...)` at the - // routing/client level), and are unioned into the failure schema by - // `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk. - // @ts-expect-error — TypeScript can't prove Simplify ≡ { [K in keyof T]: T[K] } for unresolved generics (primaryKey) - const rpc = Rpc.make(tag, { - ...options?.payload !== undefined ? { payload: options.payload } : {}, - ...options?.success !== undefined ? { success: options.success } : {}, - ...options?.error !== undefined ? { error: options.error } : {}, - ...options?.stream !== undefined ? { stream: options.stream } : {}, - ...options?.primaryKey !== undefined ? { primaryKey: options.primaryKey } : {} - }) as any - - return Object.assign(rpc.annotate(requestContext, config), { config }) - }, - middleware: (...middlewares: any[]) => { - for (const mw of middlewares) { - // recall that we run middlewares in reverse order - allMiddleware.unshift(mw) - } - return allMiddleware.filter((m) => !!m.dynamic).length !== Object.keys(rcm.config).length - // for sure, until all the dynamic middlewares are provided it's non sensical to call makeMiddlewareBasic - ? it - // actually, we don't know yet if MiddlewareR is never, but we can't easily check it at runtime - : Object.assign(makeMiddlewareBasic()(id, rcm.config, ...allMiddleware), it) - } - } - return it as any -} - -// alternatively consider group.serverMiddleware? hmmm -export const middlewareGroup = < - RequestContextMap extends Record, - Middleware extends RpcMiddlewareX.TagClassAny & { - readonly requestContext: RequestContextTag - readonly requestContextMap: RequestContextMap - } ->( - middleware: Middleware -) => -(group: RpcGroup.RpcGroup) => { - type RN = AddMiddleware - const middlewaredGroup = group.middleware(middleware) as unknown as RpcGroup.RpcGroup - const toLayerOriginal = middlewaredGroup.toLayer.bind(middlewaredGroup) - return Object.assign(middlewaredGroup, { - toLayerDynamic: < - Handlers extends HandlersFrom, - EX = never, - RX = never - >( - build: - | Handlers - | Effect.Effect - ): Layer.Layer< - Rpc.ToHandler, - EX, - | Exclude - | RpcMiddlewareX.HandlersContext - > => { - return toLayerOriginal(build as any) as any // ?? - } - }) -} diff --git a/packages/effect-app/src/rpc/README.md b/packages/effect-app/src/rpc/README.md deleted file mode 100644 index 192abc189..000000000 --- a/packages/effect-app/src/rpc/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Effect RPC extensions - -The extensions use V4 format of RPC middleware: - -- supports `requires` besides `provides` -- `requires` and `provides` should be set as second generic argument: `Tag`. -- `wrap: true` is the default, there is no classic `provides: Tag` - -## Features - -### Bundle Middleware - -Compose multiple middleware into a single middleware, so that you can use them easily together as a unit. -The middleware group is fully typesafe, supporting chained or sideways elimination of requirements. -All configured dynamic middleware must be provided or the bundle is not complete. - -### Dynamic Middleware - -Dynamic middleware is middleware that is being controlled by Configuration set on the RPC. -The middleware generally runs globally, but it's behaviour depends on configuration on the RPC. - -For instance, `AllowAnonymous`, with a default of `false`, would either provide a `UserProfile` service, or fail with a `NotLoggedInError`. -therefore `UserProfile` is eliminated from a handler implementing that rpc. -If the RPC however opts out and specifies: `allowAnonymous: true`, the `UserProfile` service is no longer eliminated from the handler, -and thus one has to use `Effect.serviceOption(UserProfile)` instead, to not get a type error. - -For instance, `RequireRoles` would by default require a `manager` role, but can be opted out from per RPC, perhaps lowering to `user` or `anonymous` role. - -### Create RPCGroups and Handlers from a common set of Middleware - -You create a bundle of middleware, containing standard or dynamic middleware, and from it you can create all your rpc groups and handlers. -NOTE: perhaps not as useful anymore if support for dynamic middleware gets integrated directly into effect RPC. -`middleware.group`, `middleware.rpc` and `group.toLayerDynamic` would become obsolete, and `RpcGroup.make().middleware(globalMiddleware)` would be simpler. - -## Examples - -See [tests](../../../infra/test/rpc-multi-middleware.test.ts) - -## Future - -Gauge interest to integrate natively into `@effect/rpc` package, and then integrate. diff --git a/packages/effect-app/src/rpc/RpcContextMap.ts b/packages/effect-app/src/rpc/RpcContextMap.ts deleted file mode 100644 index a3de0a89a..000000000 --- a/packages/effect-app/src/rpc/RpcContextMap.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { type AnyWithProps } from "effect/rpc/Rpc" -import type * as S from "effect/Schema" -import * as Context from "../Context.ts" -import { type RpcDynamic } from "./RpcMiddleware.ts" - -type Values> = T[keyof T] - -/** - * Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect Context. - * Unless explicitly configured as `true`. - */ -export type RpcContextMap = { - // todo; rename Provides - service: Service - error: E - contextActivation: true - inverted: false -} - -export declare namespace RpcContextMap { - /** - * Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect Context. - * Unless explicitly omitted. - */ - export type Inverted = { - service: Service - error: E - contextActivation: false - inverted: true - } - - export type Custom = { - service: Service - error: E - contextActivation: C - inverted: false - } - - export type Any = { - service: any - error: S.Top - contextActivation: any - inverted: boolean - } -} - -export type GetContextConfig> = { - [K in keyof RequestContextMap]?: RequestContextMap[K]["inverted"] extends true - ? RequestContextMap[K]["contextActivation"] extends true ? false - : RequestContextMap[K]["contextActivation"] extends false ? true - : RequestContextMap[K]["contextActivation"] - : RequestContextMap[K]["contextActivation"] -} - -export type GetEffectContext, T> = Values< - // inverted: contextActivation is false => remove if explicitly set to true (like allowAnonymous: true disables auth and auth service and related errors) - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends true ? never - : key extends keyof T ? T[key] extends true ? never : key - : key - ]: // TODO: or as an Optional available? - RequestContextMap[key]["service"] - } - // normal: contextActivation is true => add if explicitly set to true - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends false ? never - : key extends keyof T ? T[key] extends true ? key : never - : never - ]: // TODO: or as an Optional available? - RequestContextMap[key]["service"] - } -> - -export type GetEffectError, T> = Values< - // inverted: contextActivation is false => remove if explicitly set to true (like allowAnonymous: true disables auth and auth service and related errors) - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends true ? never - : key extends keyof T ? T[key] extends true ? never : key - : key - ]: // TODO: or as an Optional available? - RequestContextMap[key]["error"] - } - // normal: contextActivation is true => add if explicitly set to true - & { - [ - key in keyof RequestContextMap as RequestContextMap[key]["contextActivation"] extends false ? never - : key extends keyof T ? T[key] extends true ? key : never - : never - ]: // TODO: or as an Optional available? - RequestContextMap[key]["error"] - } -> - -const tag = Context.Service("RequestContextConfig") - -export const makeMap = >(config: Config) => { - const cls = class { - readonly config: Config - constructor() { - this.config = config - } - } - return Object.assign(cls, { - config, /** Retrieves RequestContextConfig out of the Rpc annotations */ - getConfig: (rpc: AnyWithProps): GetContextConfig => { - return Context.getOrElse(rpc.annotations, tag as any, () => ({})) - }, - /** Adapter used when setting the dynamic prop on a middleware implementation */ - get: < - Key extends (keyof Config) & string - >(key: Key): RpcDynamic => ({ - key, - settings: { service: config[key]!["service"] } as Config[Key] - }) - }) -} - -export const make = () => -( - error: E -): RpcContextMap => ({ - service: null as Service, - error, - contextActivation: true, - inverted: false -}) - -export const makeInverted = () => -( - error: E -): RpcContextMap.Inverted => ({ - service: null as Service, - error, - contextActivation: false, - inverted: true -}) - -export const makeCustom = () => -( - error: E, - contextActivation: C -): RpcContextMap.Custom => ({ - service: null as Service, - error, - contextActivation, - inverted: false -}) - -export type RequestContextMapTagAny = { readonly config: Record } diff --git a/packages/effect-app/src/rpc/RpcMiddleware.ts b/packages/effect-app/src/rpc/RpcMiddleware.ts deleted file mode 100644 index d4090157e..000000000 --- a/packages/effect-app/src/rpc/RpcMiddleware.ts +++ /dev/null @@ -1,244 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { type NonEmptyReadonlyArray } from "effect/Array" -import type * as Effect from "effect/Effect" -import { type Rpc, RpcMiddleware } from "effect/rpc" -import { type TypeId } from "effect/rpc/RpcMiddleware" -import type * as S from "effect/Schema" -import type * as Scope from "effect/Scope" -import type * as Stream from "effect/Stream" -import type * as Context from "../Context.ts" -import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.ts" - -export type RpcMiddlewareV4 = RpcMiddleware.RpcMiddleware - -export type RpcOptionsOriginal = { - readonly optional?: boolean - readonly error?: S.Top - readonly requiredForClient?: boolean -} - -export type RpcDynamic = { - key: Key - settings: A -} - -export type AnyDynamic = { dynamic: RpcDynamic } - -export type DependsOn = { - readonly dependsOn: NonEmptyReadonlyArray | undefined -} - -export interface RpcOptionsDynamic extends RpcOptionsOriginal { - readonly dynamic: RpcDynamic - readonly dependsOn?: NonEmptyReadonlyArray | undefined -} - -export type Dynamic = Options extends RpcOptionsDynamic ? true : false - -export interface RpcMiddlewareDynamic extends RpcMiddleware.RpcMiddleware {} - -export interface TagClassAny extends RpcMiddleware.AnyService { - readonly optional: boolean - readonly provides: any - readonly requires: any - readonly error: S.Top - readonly dynamic?: RpcDynamic | undefined - readonly dependsOn?: NonEmptyReadonlyArray | undefined -} - -export declare namespace TagClass { - /** - * @since 1.0.0 - * @category models - */ - export type FailureSchema = Options extends { readonly error: S.Top; readonly optional?: false } - ? Options["error"] - // actually not, the Failure depends on Dynamic Middleware Configuration! - // : Options extends { readonly dynamic: RpcDynamic } ? A["error"] - : typeof S.Never - - /** - * @since 1.0.0 - * @category models - */ - export type Failure = Options extends { readonly error: S.Codec; readonly optional?: false } ? _A - // actually not, the Failure depends on Dynamic Middleware Configuration! - : Options extends { readonly dynamic: RpcDynamic } ? A["error"]["Type"] - : never - - /** - * @since 1.0.0 - * @category models - */ - // Avoid `S.Codec.DecodingServices = X extends Top ? X["DecodingServices"] : never`: - // tsgo fails to reduce it here and leaves `unknown`. `FailureSchema` is - // always `S.Top` (either `Options["error"]` or `typeof S.Never`), so read directly. - export type FailureContext = FailureSchema["DecodingServices"] - - /** - * @since 1.0.0 - * @category models - */ - export type FailureService = Optional extends true ? unknown : Failure - - /** - * @since 1.0.0 - * @category models - */ - export type Optional = Options extends { readonly optional: true } ? true : false - - /** - * @since 1.0.0 - * @category models - */ - export type RequiredForClient = Options extends { readonly requiredForClient: true } ? true : false - - /** - * @since 1.0.0 - * @category models - */ - export interface Base< - Self, - Name extends string, - Options, - Service, - Config extends { - requires?: any - provides?: any - } - > extends Context.Service { - new(_: never): Context.ServiceClass.Shape - readonly [TypeId]: TypeId - readonly optional: Optional - readonly error: FailureSchema - readonly "~ClientError": Options extends { readonly clientError: infer CE } ? CE : never - readonly provides: "provides" extends keyof Config ? Config["provides"] : never - readonly requires: "requires" extends keyof Config ? Config["requires"] : never - readonly dynamic: Options extends RpcOptionsDynamic ? Options["dynamic"] - : undefined - readonly dependsOn: Options extends DependsOn ? Options["dependsOn"] : undefined - readonly requiredForClient: RequiredForClient - } -} - -export interface TagClass< - Self, - Name extends string, - Options, - Config extends { - requires?: any - provides?: any - } = { requires: never; provides: never } -> extends - TagClass.Base< - Self, - Name, - Options, - Options extends RpcOptionsDynamic ? RpcMiddlewareDynamic< - TagClass.FailureService, - "requires" extends keyof Config ? Config["requires"] : never, - { [K in Options["dynamic"]["key"]]?: Options["dynamic"]["settings"]["contextActivation"] } - > - : RpcMiddlewareV4< - "provides" extends keyof Config ? Config["provides"] : never, - TagClass.Failure, - "requires" extends keyof Config ? Config["requires"] : never - >, - Config - > -{} - -export const Tag = < - Self, - Config extends { - requires?: any - provides?: any - } = { requires: never; provides: never } ->() => -< - const Name extends string, - const Options extends RpcOptionsOriginal | RpcOptionsDynamic = {} ->( - id: Name, - options?: Options -): TagClass => - class extends RpcMiddleware.Service()(id, options as any) { - static readonly requires: "requires" extends keyof Config ? Config["requires"] : never - static readonly provides: "provides" extends keyof Config ? Config["provides"] : never - static readonly dynamic = options && "dynamic" in options ? options.dynamic : undefined - static readonly dependsOn = options && "dependsOn" in options ? options.dependsOn : undefined - } as any - -// not needed if there's official support in Rpc.Rpc. -export type AddMiddleware = R extends Rpc.Rpc< - infer _Tag, - infer _Payload, - infer _Success, - infer _Error, - infer _Middleware -> ? - & Rpc.Rpc< - _Tag, - _Payload, - _Success, - _Error, - _Middleware | Middleware - > - & { readonly config: R extends { readonly config: infer _C } ? _C : never } - : never - -// customized versions to handle dynamically eliminated context. -export type HandlersContext = keyof Handlers extends infer K - ? K extends keyof Handlers & string ? HandlerContext : never - : never - -export type HandlerContext = [Rpc.IsStream] extends - [true] ? Handler extends (...args: any) => - | Stream.Stream - | Rpc.Wrapper> - | Effect.Effect< - infer _A, - infer _EX, - infer _R - > - | Rpc.Wrapper< - Effect.Effect< - infer _A, - infer _EX, - infer _R - > - > ? Exclude, Scope.Scope> - : never - : Handler extends ( - ...args: any - ) => Effect.Effect | Rpc.Wrapper> - ? ExcludeProvides<_R, Rpcs, K> - : never - -// new -export type ExtractDynamicallyProvides = R extends - Rpc.Rpc ? _Middleware extends { - readonly requestContextMap: infer _RC - } ? _RC extends Record // ? GetEffectContext<_RC, { allowAnonymous: false }> - ? R extends { readonly config: infer _C } ? GetEffectContext<_RC, _C> - : GetEffectContext<_RC, {}> - : never - : never - : never - -export type ExtractProvides = R extends - Rpc.Rpc ? _Middleware extends { - readonly provides: infer _P - } ? [_P] extends [never] ? never - : _P /*_P extends Context.Service ? _I - : never */ - : never - : never - -export type ExcludeProvides = Exclude< - Env, - // customisation is down here. - ExtractProvides | ExtractDynamicallyProvides -> diff --git a/packages/effect-app/src/runtime.ts b/packages/effect-app/src/runtime.ts deleted file mode 100644 index a8b004539..000000000 --- a/packages/effect-app/src/runtime.ts +++ /dev/null @@ -1,57 +0,0 @@ -import * as Exit from "effect/Exit" -import { flow } from "effect/Function" -import * as Logger from "effect/Logger" -import * as ManagedRuntime from "effect/ManagedRuntime" -import { CauseException } from "./client/errors.ts" -import { type Context } from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" - -export const makeAppRuntime = Effect.fnUntraced(function*(layer: Layer.Layer) { - const l = layer.pipe( - Layer.provide(Logger.layer([Logger.consolePretty()])), - Layer.orDie - ) - const mrt = ManagedRuntime.make(l) - yield* mrt.contextEffect - return Object.assign(mrt, { - [Symbol.dispose]() { - return Effect.runSync(mrt.disposeEffect) - }, - - [Symbol.asyncDispose]() { - return mrt.dispose() - } - }) // as we initialise here, there is no more error left. -}) - -export function initializeSync(layer: Layer.Layer) { - const runtime = Effect.runSync(makeAppRuntime(layer)) - return runtime -} - -export function initializeAsync(layer: Layer.Layer) { - return Effect - .runPromise(makeAppRuntime(layer)) -} - -// we wrap into CauseException because we want to keep the full cause of the failure. -export const makeRunPromise = (services: Context) => - flow(Effect.runPromiseExitWith(services), (_) => - _.then( - Exit.match({ - onFailure: (cause) => Promise.reject(new CauseException(cause, "runPromise")), - onSuccess: (value) => Promise.resolve(value) - }) - )) - -export const makeRunSync = (services: Context) => - flow( - Effect.runSyncExitWith(services), - Exit.match({ - onFailure: (cause) => { - throw new CauseException(cause, "runSync") - }, - onSuccess: (value) => value - }) - ) diff --git a/packages/effect-app/src/setupRequest.ts b/packages/effect-app/src/setupRequest.ts deleted file mode 100644 index a4c02eda7..000000000 --- a/packages/effect-app/src/setupRequest.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { SqlClient } from "effect/sql" -import * as Tracer from "effect/Tracer" -import { DataDependencyRecorder } from "./DataDependencies.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" -import * as Option from "./Option.ts" -import { LocaleRef, RequestContext, spanAttributes } from "./RequestContext.ts" -import * as RequestScopedDependencies from "./RequestScopedDependencies.ts" -import { NonEmptyString255 } from "./Schema.ts" -import { ContextMapContainer, storeId } from "./Store.ts" - -const withSqlTransaction = (self: Effect.Effect): Effect.Effect => - Effect.serviceOption(SqlClient.SqlClient).pipe( - Effect.flatMap(Option.match({ - onNone: () => self, - onSome: (sql) => sql.withTransaction(self).pipe(Effect.orDie) - })) - ) - -export const getRequestContext = Effect - .all({ - span: Effect.currentSpan.pipe(Effect.orDie), - locale: LocaleRef, - namespace: storeId - }) - .pipe( - Effect.map(({ locale, namespace, span }) => - RequestContext.make({ - span: Tracer.externalSpan(span), - locale, - namespace, - name: NonEmptyString255(span.name) - }) - ) - ) - -export const getRC = Effect.all({ - locale: LocaleRef, - namespace: storeId -}) - -const withRequestSpan = (name = "request", options?: Tracer.SpanOptions) => (f: Effect.Effect) => - Effect.andThen( - getRC, - (ctx) => - f.pipe( - Effect.withSpan(name, { - ...options, - attributes: { ...spanAttributes({ ...ctx, name: NonEmptyString255(name) }), ...options?.attributes } - }, { - captureStackTrace: options?.captureStackTrace ?? false - }), - // TODO: false - // request context info is picked up directly in the logger for annotations. - Effect.withLogSpan(name) - ) - ) - -export interface SetupRequestOptions { - readonly withTransaction?: boolean -} - -export const requestStateLayer = RequestScopedDependencies.layer(ContextMapContainer, DataDependencyRecorder) - -// Build `layer` against the ambient (request) scope rather than a sub-scope of the -// returned Effect. Required when the returned value is a streaming HttpServerResponse: -// the response body keeps producing chunks (and using layer-provided state) after the -// Effect returns, so a sub-scope would close too early and run finalizers mid-stream. -export const provideOnRequestScope = - (layer: Layer.Layer) => (self: Effect.Effect) => - Effect.gen(function*() { - const requestScope = yield* Effect.scope - // Fresh MemoMap per request: `Layer.buildWithScope` would otherwise reuse - // the ambient MemoMap living on the HTTP server fiber, sharing the built - // value (e.g. ContextMap) across every request handled by that server. - const memoMap = yield* Layer.makeMemoMap - const ctx = yield* Layer.buildWithMemoMap(layer, memoMap, requestScope) - return yield* Effect.provide(self, ctx) - }) - -export const setupRequestContextFromCurrent = - (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => (self: Effect.Effect) => - self - .pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(name, options), - Effect.provide(requestStateLayer, { local: true }) - ) - -// Streaming variant: binds ContextMapContainer to the ambient (request) scope so its -// finalizer (clear()) runs only after the response body is fully drained, not when the -// outer Effect returns its HttpServerResponse value. Use for handlers that return a -// streaming HttpServerResponse (e.g. SSE) — see RequestContextMiddleware for context. -export const setupStreamingRequestContextFromCurrent = - (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => (self: Effect.Effect) => - self.pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(name, options), - provideOnRequestScope(requestStateLayer) - ) - -// TODO: consider integrating Effect.withParentSpan -export function setupRequestContext( - self: Effect.Effect, - requestContext: RequestContext, - options?: SetupRequestOptions -) { - const layer = Layer.mergeAll( - requestStateLayer, - Layer.succeed(LocaleRef, requestContext.locale), - Layer.succeed(storeId, requestContext.namespace) - ) - return self - .pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(requestContext.name), - Effect.provide(layer, { local: true }) - ) -} - -export function setupRequestContextWithCustomSpan( - self: Effect.Effect, - requestContext: RequestContext, - name: string, - options?: Tracer.SpanOptions & SetupRequestOptions -) { - const layer = Layer.mergeAll( - requestStateLayer, - Layer.succeed(LocaleRef, requestContext.locale), - Layer.succeed(storeId, requestContext.namespace) - ) - return self - .pipe( - options?.withTransaction === true ? withSqlTransaction : (_) => _, - withRequestSpan(name, options), - Effect.provide(layer, { local: true }) - ) -} diff --git a/packages/effect-app/src/toast.ts b/packages/effect-app/src/toast.ts deleted file mode 100644 index d9b674db4..000000000 --- a/packages/effect-app/src/toast.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as Context from "./Context.ts" -import { accessEffectFn } from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Option from "./Option.ts" - -export type ToastId = string | number -export type ToastOpts = { id?: ToastId; timeout?: number; groupId?: string; requestId?: string } -export type ToastOptsInternal = { id?: ToastId | null; timeout?: number; groupId?: string; requestId?: string } - -export type UseToast = () => { - error: (this: void, message: string, options?: ToastOpts) => ToastId - warning: (this: void, message: string, options?: ToastOpts) => ToastId - success: (this: void, message: string, options?: ToastOpts) => ToastId - info: (this: void, message: string, options?: ToastOpts) => ToastId - dismiss: (this: void, id: ToastId) => void -} - -export class CurrentToastId extends Context.Opaque()("CurrentToastId") {} - -/** fallback to CurrentToastId when available unless id is explicitly set to a value or null */ -export const wrap = (toast: ReturnType) => { - const wrap = (toastHandler: (message: string, options?: ToastOpts) => ToastId) => { - return (message: string, options?: ToastOptsInternal) => - Effect.serviceOption(CurrentToastId).pipe( - Effect.flatMap((currentToast) => - Effect.sync(() => { - const { id: _id, ...rest } = options ?? {} - const id = _id !== undefined - ? _id ?? undefined - : Option.getOrUndefined(Option.map(currentToast, (_) => _.toastId)) - // when id is undefined, we may end up with no toast at all.. - return toastHandler(message, id !== undefined ? { ...rest, id } : rest) - }) - ) - ) - } - return { - error: wrap(toast.error), - info: wrap(toast.info), - success: wrap(toast.success), - warning: wrap(toast.warning), - dismiss: (toastId: ToastId) => Effect.sync(() => toast.dismiss(toastId)) - } -} - -type ToastShape = ReturnType - -export class Toast extends Context.Opaque()("Toast") { - static readonly error = accessEffectFn(this, "error") - static readonly info = accessEffectFn(this, "info") - static readonly success = accessEffectFn(this, "success") - static readonly warning = accessEffectFn(this, "warning") - static readonly dismiss = accessEffectFn(this, "dismiss") -} diff --git a/packages/effect-app/src/transform.ts b/packages/effect-app/src/transform.ts deleted file mode 100644 index 0855cb3a0..000000000 --- a/packages/effect-app/src/transform.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { NonEmptyReadonlyArray } from "effect/Array" -import * as Option from "effect/Option" -import type { Misc, Union } from "ts-toolbelt" -import type * as SET from "./Set.ts" - -// type SomeObject = { -// 0: Option.Option -// a: { -// b: Option.Option -// g: Option.Option> -// h: Option.Option<{ i: Option.Option }> -// } -// c: { d: Array }>> } -// } -// type test0 = Transform -// type test1 = Transform - -type OptOf = Union.Exclude< - A extends Option.Some ? X | null : A, - Option.None -> -// eslint-disable-next-line @typescript-eslint/no-explicit-any - -export type TransformRoot = O extends Option.Option ? Transform> - : Transform -export type Transform = O extends Misc.BuiltIn | Misc.Primitive ? O - : { - [K in keyof O]: OptOf extends infer X ? X extends (infer Y)[] ? OptOf>[] - : X extends NonEmptyReadonlyArray ? NonEmptyReadonlyArray>> - : X extends SET.Set ? SET.Set>> - : X extends readonly (infer Y)[] ? readonly OptOf>[] - : Transform - : never - } - -export const encodeOptsAsNullable = (root: T): TransformRoot => encodeOptsAsNullable_(root, new Map()) - -const encodeOptsAsNullable_ = (value: any, cacheMap: Map): any => { - const cacheEntry = cacheMap.get(value) - if (cacheEntry) { - return cacheEntry - } - - if (Array.isArray(value)) { - const newAr: typeof value = [] - cacheMap.set(value, newAr) - value.forEach((x) => newAr.push(encodeOptsAsNullable_(x, cacheMap))) - return newAr - } - - if ( - value instanceof Date - || typeof value === "function" - || (typeof value === "object" && value !== null && "then" in value && typeof value.then === "function") - ) { - return value - } - - if (value instanceof Set) { - const newValue = [...value] - cacheMap.set(value, newValue) - return newValue - } - - if (value instanceof Object) { - if (value._tag === "Some" || value._tag === "None") { - const v = value as Option.Option - return encodeOptsAsNullable_(Option.getOrNull(v), cacheMap) - } - const newObj = {} as Record - cacheMap.set(value, newObj) - - Object.keys(value).forEach((key) => { - newObj[key] = encodeOptsAsNullable_(value[key], cacheMap) - }) - return newObj - } - return value -} diff --git a/packages/effect-app/src/utils.ts b/packages/effect-app/src/utils.ts deleted file mode 100644 index 6c96e8f55..000000000 --- a/packages/effect-app/src/utils.ts +++ /dev/null @@ -1,884 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-function-type */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-redundant-type-constituents */ -import * as Cause from "effect/Cause" -import * as Effect from "effect/Effect" -import * as Exit from "effect/Exit" -import * as Fiber from "effect/Fiber" -import { dual } from "effect/Function" -import * as Option from "effect/Option" -import { isFunction } from "effect/Predicate" -import * as Record from "effect/Record" -import * as Result from "effect/Result" -import { identity, pipe } from "./Function.ts" -import type { DeepMutable, Equals, Mutable } from "./Types.ts" - -// codegen:start {preset: barrel, include: ./utils/*.ts, nodir: false } -export * from "./utils/effectify.ts" -export * from "./utils/extend.ts" -export * from "./utils/gen.ts" -export * from "./utils/logger.ts" -export * from "./utils/logLevel.ts" -// codegen:end - -export * from "effect/Utils" - -export const cloneTrait = Symbol.for("clone-trait") -export interface Clone { - [cloneTrait](this: this, that: any): this -} - -export const unsafeRight = (ei: Result.Result) => { - if (Result.isFailure(ei)) { - console.error(ei.failure) - throw ei.failure - } - return ei.success -} - -export const unsafeSome = (makeErrorMessage: () => string) => (o: Option.Option) => { - if (Option.isNone(o)) { - throw new Error(makeErrorMessage()) - } - return o.value -} - -export function toString(v: unknown) { - return `${v}` -} - -export type Constructor = { new(...args: any[]): T } -export type ThenArg = T extends Promise ? U - : T extends (...args: any[]) => Promise ? V - : T - -/** - * @deprecated TODO: does not filter optional fields! - */ -type NoUndefinedField = { [P in keyof T]: Exclude } -export function dropUndefinedT>( - input: A -): NoUndefinedField { - const newR = pipe( - input, - Record.filter((x): x is A => x !== undefined) - ) - return newR as any -} - -type IsOptional, K extends keyof T> = {} extends Pick ? true : false - -type WithUndefined> = { - [K in keyof T]: IsOptional extends true ? T[K] | undefined : T[K] -} - -type WithUndefinedRecursive> = { - [K in keyof T]: WithUndefinedRecursive extends infer $RTK - ? IsOptional extends true ? $RTK | undefined : $RTK - : never -} - -/** a version of @see dropUndefinedT that keeps support for go to definition, auto completion, and documentation of fields! */ -export function dropUndefinedT2>() { - return ( - input: WithUndefined - ): Desired => { - const newR = pipe( - input as any, - Record.filter((x): x is Desired => x !== undefined) - ) - return newR as any - } -} - -/** - * A recursive version of @see dropUndefinedT that preserves IDE support for - * go-to-definition, auto-completion, and field documentation. - * - * Note: This utility does not physically remove `undefined` fields at runtime. - * It provides type-level compatibility with libraries that use `prop?: type` - * syntax. Before the introduction of `exactOptionalPropertyTypes`, this syntax - * implicitly allowed `undefined` values. Libraries that adopted `prop?: type` - * before this flag was introduced still accept `undefined` at runtime. - */ - -export function dropUndefinedRec>() { - return ( - input: WithUndefinedRecursive - ): Desired => { - // doesn't drop for real - return input as any - } -} - -export type Dictionary = { - readonly [P in string]: T -} - -export function dropUndefined( - input: Dictionary -): Dictionary { - const newR = pipe( - input, - Record.filter((x): x is A => x !== undefined) - ) - return newR -} - -type GetTag = T extends { _tag: infer K } ? K : never -export const isOfType = (tag: GetTag) => (e: { _tag: string }): e is T => e._tag === tag - -export function pretty(o: unknown): string { - return JSON.stringify(o, undefined, 2) ?? "undefined" -} - -export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I -) => void ? I - : never - -// generally A | B is not assignable to A & B -export type IsUnion = [T] extends [UnionToIntersection] ? false : true - -export type EnforceNonEmptyRecord = keyof R extends never ? never : R - -export function intersect( - ...as: AS -): UnionToIntersection<{ [k in keyof AS]: AS[k] }[number]> { - return as.reduce((a: any, b: any) => Object.assign(a, b), {}) as any -} - -export type IsEqualTo = (() => T extends X ? 1 : 2) extends < - T ->() => T extends Y ? 1 : 2 ? true - : false - -export const unifyIndex = Symbol() -export type unifyIndex = typeof unifyIndex - -export interface UnifiableIndexed<_X> {} -export type UnifiableIndexedURI = keyof UnifiableIndexed - -export interface Unifiable { - // Sync: [X] extends [Sync] ? Sync : never - // Effect: [X] extends [Effect] - // ? [X] extends [Sync] - // ? never - // : Effect.Effect - // : never - Unify: [X] extends [{ readonly [unifyIndex]: infer K }] ? K extends UnifiableIndexedURI ? UnifiableIndexed[K] - : never - : never -} - -export type Unify = Unifiable[keyof Unifiable] extends never ? X - : Unifiable[keyof Unifiable] - -// forked from https://github.com/Alorel/typescript-lazy-get-decorator - -type DecoratorReturn = PropertyDescriptor | NewDescriptor - -function decorateNew( - inp: NewDescriptor, - setProto: boolean, - makeNonConfigurable: boolean, - resultSelector: ResultSelectorFn -): NewDescriptor { - const out: NewDescriptor = Object.assign({}, inp) - if (out.descriptor) { - out.descriptor = Object.assign({}, out.descriptor) - } - const actualDesc: PropertyDescriptor = out.descriptor || /* istanbul ignore next */ out - - const originalMethod = validateAndExtractMethodFromDescriptor(actualDesc) - const isStatic = inp.placement === "static" - - actualDesc.get = function(this: any): any { - return getterCommon( - isStatic ? this : Object.getPrototypeOf(this), - out.key, - isStatic, - !!actualDesc.enumerable, - originalMethod, - this, - // eslint-disable-next-line prefer-rest-params - arguments, - setProto, - makeNonConfigurable, - resultSelector - ) - } - - return out -} - -function decorateLegacy( - target: any, - key: PropertyKey, - descriptor: PropertyDescriptor | undefined, - setProto: boolean, - makeNonConfigurable: boolean, - // tslint:enable:bool-param-default - resultSelector: ResultSelectorFn -): PropertyDescriptor { - /* istanbul ignore if */ - if (!descriptor) { - descriptor = Object.getOwnPropertyDescriptor(target, key) - if (!descriptor) { - const e = Object.assign(new Error("@LazyGetter is unable to determine the property descriptor"), { - $key: key, - $target: target - }) - throw e - } - } - - const originalMethod = validateAndExtractMethodFromDescriptor(descriptor) - - return Object.assign({}, descriptor, { - get(this: any): any { - return getterCommon( - target, - key, - Object.getPrototypeOf(target) === Function.prototype, - !!descriptor.enumerable, - originalMethod, - this, - // eslint-disable-next-line prefer-rest-params - arguments, - setProto, - makeNonConfigurable, - resultSelector - ) - } - }) -} - -/** Signifies that the modified property descriptor can be reset to its original state */ -interface ResettableDescriptor { - /** - * Restore the property descriptor on the given class instance or prototype and re-apply the lazy getter. - * @param on The class instance or prototype - */ - reset(on: any): void -} - -/** ES7 proposal descriptor, tweaked for Babel */ -interface NewDescriptor extends PropertyDescriptor { - descriptor?: PropertyDescriptor - - key: PropertyKey - - kind: string - - placement: string -} - -/** A filter function that must return true for the value to cached */ -export type ResultSelectorFn = (v: any) => boolean - -function defaultFilter(): boolean { - return true -} - -function validateAndExtractMethodFromDescriptor(desc: PropertyDescriptor): Function { - // eslint-disable-next-line @typescript-eslint/unbound-method - const originalMethod = desc.get - - if (!originalMethod) { - throw new Error("@LazyGetter can only decorate getters!") - } else if (!desc.configurable) { - throw new Error("@LazyGetter target must be configurable") - } - - return originalMethod -} - -function getterCommon( // tslint:disable-line:parameters-max-number - target: any, - key: PropertyKey, - isStatic: boolean, - enumerable: boolean, - originalMethod: Function, - thisArg: any, - args: IArguments, - setProto: boolean, - makeNonConfigurable: boolean, - resultSelector: ResultSelectorFn -): any { - const value = originalMethod.apply(thisArg, args) - - if (resultSelector(value)) { - const newDescriptor: PropertyDescriptor = { - configurable: !makeNonConfigurable, - enumerable, - value - } - - if (isStatic || setProto) { - Object.defineProperty(target, key, newDescriptor) - } - - if (!isStatic) { - Object.defineProperty(thisArg, key, newDescriptor) - } - } - - return value -} - -/** - * Evaluate the getter function and cache the result - * @param [setProto=false] Set the value on the class prototype as well. Only applies to non-static getters. - * @param [makeNonConfigurable=false] Set to true to make the resolved property non-configurable - * @param [resultSelector] A filter function that must return true for the value to cached - * @return A decorator function - */ -export function LazyGetter( - setProto = false, - makeNonConfigurable = false, - resultSelector: ResultSelectorFn = defaultFilter -): MethodDecorator & ResettableDescriptor { - let desc: PropertyDescriptor - let prop: PropertyKey - let args: [targetOrDesc: any, key: PropertyKey | undefined, descriptor: PropertyDescriptor | undefined] | undefined - let isLegacy: boolean - - function decorator( - targetOrDesc: any, - key: PropertyKey | undefined, - descriptor: PropertyDescriptor | undefined - ): DecoratorReturn { - args = [targetOrDesc, key, descriptor] - if (key === undefined) { - if (typeof desc === "undefined") { - const newDescriptor = targetOrDesc as NewDescriptor - isLegacy = false - prop = newDescriptor.key - desc = Object.assign( - {}, - newDescriptor.descriptor - /* istanbul ignore next */ || targetOrDesc - ) - } - - return decorateNew(targetOrDesc, setProto, makeNonConfigurable, resultSelector) - } else { - if (typeof desc === "undefined") { - isLegacy = true - prop = key - desc = Object.assign( - {}, - descriptor - /* istanbul ignore next */ || Object.getOwnPropertyDescriptor( - targetOrDesc, - key - ) - ) - } - - return decorateLegacy( - targetOrDesc, - key, - descriptor, - setProto, - makeNonConfigurable, - resultSelector - ) - } - } - - decorator.reset = setProto - ? thrower - : (on: any): void => { - if (!on) { - throw new Error("Unable to restore descriptor on an undefined target") - } - if (!desc) { - throw new Error( - "Unable to restore descriptor. Did you remember to apply your decorator to a method?" - ) - } - if (!args) { - throw new Error("Unable to restore descriptor before applying the decorator.") - } - // Restore descriptor to its original state - Object.defineProperty(on, prop, desc) - const ret = decorator.apply(null, args) - const restoredDescriptor = isLegacy ? ret : "descriptor" in ret && ret.descriptor ? ret.descriptor : ret - Object.defineProperty(on, prop, restoredDescriptor) - } - - return decorator -} - -function thrower(): never { - throw new Error("This decoration modifies the class prototype and cannot be reset.") -} - -export type RefinementWithIndex = (i: I, a: A) => a is B - -export type PredicateWithIndex = (i: I, a: A) => boolean - -export type Erase = R & K extends K & infer R1 ? R1 : R - -/** from ts-toolbelt, minimal port of Compute */ - -export type Depth = "flat" | "deep" - -type Errors = Error -// | EvalError -// | RangeError -// | ReferenceError -// | SyntaxError -// | TypeError -// | URIError - -type Numeric = - // | Number - // | BigInt // not needed - // | Math - Date - -type Textual = - // | String - RegExp - -type Arrays = - // | Array - // | ReadonlyArray - | Int8Array - | Uint8Array - | Uint8ClampedArray - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array -// | BigInt64Array -// | BigUint64Array - -type Maps = - // | Map - // | Set - | ReadonlyMap - | ReadonlySet - | WeakMap - | WeakSet - -type Structures = - | ArrayBuffer - // | SharedArrayBuffer - // | Atomics - | DataView -// | JSON - -type Abstractions = Function | Promise | Generator -// | GeneratorFunction - -type WebAssembly = never - -export type BuiltInObject = - | Errors - | Numeric - | Textual - | Arrays - | Maps - | Structures - | Abstractions - | WebAssembly - -export type ComputeRaw = A extends Function ? A - : - & { - [K in keyof A]: A[K] - } - & {} - -export type ComputeFlat = A extends BuiltInObject ? A - : - & { - [K in keyof A]: A[K] - } - & {} - -export type ComputeDeep = A extends BuiltInObject ? A - : - & { - [K in keyof A]: ComputeDeep - } - & {} - -export type Compute = { - flat: ComputeFlat - deep: ComputeDeep -}[depth] - -///// - -export const LazySymbol = Symbol("lazy") - -interface Lazy { - [LazySymbol]: Record -} - -export function lazyGetter(creator: (target: T) => T2) { - const key = Symbol(creator.name) - const f = (target: T): T2 => { - let lazy = (target as unknown as Lazy)[LazySymbol] - if (!lazy) { - lazy = {} - Object.defineProperty(target, LazySymbol, { enumerable: false, value: lazy }) - } else if (lazy[key]) { - return lazy[key] - } - const value = creator(target) - lazy[key] = value - return value - } - Object.defineProperty(f, "name", { - enumerable: false, - value: `Lazy<${creator.name}>` - }) - return f -} - -export function exhaustiveMatch() { - return any>>(handlers: Out) => (t: T): ReturnType => handlers[t](t) -} - -export function exhaustiveMatch_(t: T) { - return any>>(handlers: Out): ReturnType => handlers[t](t) -} - -export type OptPromise any> = ( - ...args: Parameters -) => Promise> | ReturnType - -export function access(t: Record) { - return (key: T) => t[key] -} - -export function todayAtUTCNoon() { - const localDate = new Date() - const utcDateAtNoon = Date.UTC( - localDate.getFullYear(), - localDate.getMonth(), - localDate.getDate(), - 12 - ) - return new Date(utcDateAtNoon) -} - -function anyOp_$(self: T) { - return { - get subject() { - return self - } - } -} - -export function anyOp$(self: T): AnyOps { - return anyOp_$(self) -} - -export interface AnyOps { - subject: T -} - -export const clone = dual< - (f: NoInfer) => (self: A) => A, - (self: A, f: A) => A ->(2, (self, f) => { - if (cloneTrait in (self as any)) { - const selfWithClone = self as typeof self & Clone - return selfWithClone[cloneTrait](f) - } - return Object.setPrototypeOf(f, Object.getPrototypeOf(self)) as typeof self -}) - -export const copy = dual< - { - (f: (a: A) => Partial>): (self: A) => A - (f: Partial>): (self: A) => A - }, - { - (self: A, f: (a: A) => Partial): A - (self: A, f: Partial): A - } ->(2, (self: A, f: Partial | ((a: A) => Partial)) => clone(self, { ...self, ...(isFunction(f) ? f(self) : f) })) - -export type CopyOriginU any> = - & { - [K in keyof U & keyof InstanceType]?: U[K] - } - & {} - -export type CopyOriginRet = - & { - [K in keyof A | keyof U]: K extends keyof U ? U[K] : A[K & keyof A] - } - & {} - -export type CopyOriginSelf = Equals<{}, U> extends true - ? Equals extends true ? `updates argument is empty or contains only extra properties` - : A - : A - -export interface StructuralCopyOrigin { - >( - f: (a: A) => - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): (self: CopyOriginSelf) => CopyOriginRet - >( - updates: - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): (self: CopyOriginSelf) => CopyOriginRet - >( - self: CopyOriginSelf, - f: (a: A) => - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): CopyOriginRet - >( - self: CopyOriginSelf, - updates: - & { - [K in keyof U & keyof Self]?: U[K] - } - & {} - ): CopyOriginRet -} - -// just one input param: the convention is that the ctor takes an object -// containing the properties of the value (I can't put object there as type because of contravariance) -/** - * By design this does not return `Self` directly. - * - * The return type is computed from `Self` and the update payload so callers can - * expose an explicit structural return type that remains assignable to `Self`. - */ -export const copyOrigin = any>(ctor: Ctor) => - dual< - { - , U extends Partial>>( - f: (a: A) => CopyOriginU - ): (self: CopyOriginSelf) => CopyOriginRet - , U extends Partial>>( - updates: CopyOriginU - ): (self: CopyOriginSelf) => CopyOriginRet - }, - { - , U extends Partial>>( - self: CopyOriginSelf, - f: (a: A) => CopyOriginU - ): CopyOriginRet - , U extends Partial>>( - self: CopyOriginSelf, - updates: CopyOriginU - ): CopyOriginRet - } - >( - 2, - , U extends Partial>>( - self: Equals<{}, U> extends true ? `updates argument is empty or contains only extra properties` : A, - f: - | CopyOriginU - | ((a: A) => CopyOriginU) - ): CopyOriginRet => { - const o = { ...self, ...(isFunction(f) ? f(self as any) : f) } - - if (cloneTrait in (self as any)) { - const selfWithClone = self as typeof self & Clone - return selfWithClone[cloneTrait](o) - } - - return new ctor(o) - } - ) - -export function debug(a: AnyOps, name: string) { - let r: string | A = a.subject - try { - r = pretty(a.subject) - } catch { /* empty */ } - return Effect - .logDebug("print") - .pipe( - Effect.annotateLogs(name, `${r}`), - Effect - .map(() => a.subject) - ) -} - -export function debugUnsafe(a: AnyOps, name: string) { - console.log(name, a.subject) - return a.subject -} - -export function spread< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Fields extends Record, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - NewProps ->(fields: Fields, fnc: (fields: Fields) => NewProps) { - return fnc(fields) -} - -export function spreadS< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Fields extends Record ->(fields: Fields, fnc: (fields: Fields) => Fields) { - return fnc(fields) -} - -export function makeAzureFriendly(path: string) { - return path.replace(/\//g, "___SL@SH___") -} - -export function undoAzureFriendly(path: T): T { - return path.replace(/___SL@SH___/g, "/") as T -} - -export function arrayMove( - arrInput: readonly T[], - oldIndex: number, - newIndex: number -) { - const arr: (T | undefined)[] = [...arrInput] - while (oldIndex < 0) { - oldIndex += arr.length - } - while (newIndex < 0) { - newIndex += arr.length - } - if (newIndex >= arr.length) { - let k = newIndex - arr.length + 1 - while (k--) { - arr.push(undefined) - } - } - arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]) - return arr -} - -export function arrayMoveDropUndefined( - arrInput: readonly (T | undefined)[], - oldIndex: number, - newIndex: number -): T[] { - return arrayMove(arrInput, oldIndex, newIndex).filter((x): x is T => x !== undefined) -} - -export function arMoveElDropUndefined(el: T, newIndex: number) { - return (arrInput: ReadonlyArray): Option.Option> => { - const ar = [...arrInput] - const index = ar.findIndex((x) => x === el) - if (index === -1) { - return Option.none() - } - return Option.some(arrayMoveDropUndefined(ar, index, newIndex)) - } -} - -export function setMoveElDropUndefined(el: T, newIndex: number) { - return (arrInput: ReadonlySet): Option.Option> => - pipe([...arrInput], arMoveElDropUndefined(el, newIndex), Option.map((ar) => new Set(ar))) -} - -type RemoveNonArray = T extends readonly any[] ? T : never -export function isNativeTuple(a: A): a is RemoveNonArray { - return Array.isArray(a) -} - -export const mutable: { (a: A, deep: true): DeepMutable; (a: A): Mutable } = identity - -/** - * Recursively removes all elements assignable to type `E` from tuple `T`. - * - * @example - * ```typescript - * type Mixed = [1, "hello", 2, "world", 3] - * type Numbers = ExcludeFromTuple - * // Result: [1, 2, 3] - * ``` - */ -export type ExcludeFromTuple = T extends [infer F, ...infer R] - ? [F] extends [E] ? ExcludeFromTuple - : [F, ...ExcludeFromTuple] - : [] - -export const addAbortToRuntimeFiber = (fiber: Fiber.Fiber, signal: AbortSignal) => { - const abort = () => Effect.runSync(Fiber.interrupt(fiber)) - if (signal.aborted) { - abort() - return fiber - } - signal.addEventListener("abort", abort) - return fiber -} - -export const runtimeFiberAsPromise = (fiber: Fiber.Fiber, signal?: AbortSignal) => { - if (signal) addAbortToRuntimeFiber(fiber, signal) - return new Promise((resolve, reject) => - fiber.addObserver((exit) => { - if (Exit.isSuccess(exit)) { - resolve(exit.value) - } else { - // eslint-disable-next-line - reject(Cause.squash(exit.cause)) - } - }) - ) -} - -// unifies any input to be an effect. -export const wrapEffect = >( - m: ((...args: Args) => A) | ((...args: Args) => Effect.Effect) | I -) => { - if (typeof m === "function") { - return (...args: Args): Effect.Effect => { - const r = (m as any)(...args) - if (Effect.isEffect(r)) return r as Effect.Effect - return Effect.succeed(r) - } - } - return (): Effect.Effect => Effect.succeed(m) -} - -export type UnionToTuples = [T] extends [never] ? [] - : T extends any ? - | [T, ...UnionToTuples>] - | UnionToTuples> - : [] - -const genConstructor = (function*() {}).constructor - -/** - * @example - * ```ts - * import * as Utils from "effect/Utils" - * - * function* generatorFn() { - * yield 1 - * yield 2 - * } - * - * console.log(Utils.isGeneratorFunction(generatorFn)) // true - * console.log(Utils.isGeneratorFunction(() => {})) // false - * ``` - * - * @category predicates - * @since 3.11.0 - */ -export const isGeneratorFunction = (u: unknown): u is (...args: Array) => Generator => - isFunction(u) && u.constructor === genConstructor diff --git a/packages/effect-app/src/utils/effectify.ts b/packages/effect-app/src/utils/effectify.ts deleted file mode 100644 index 2aab5cad9..000000000 --- a/packages/effect-app/src/utils/effectify.ts +++ /dev/null @@ -1,260 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ - -import * as Effect from "effect/Effect" - -interface Callback { - (err: E, a?: A): void -} - -type ArgsWithCallback, E, A> = [...args: Args, cb: Callback] - -type WithoutNull = unknown extends A ? void : Exclude - -type Effectify = T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - (...args: ArgsWithCallback): infer _R10 -} ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - (...args: Args8): Effect.Effect, E> - (...args: Args9): Effect.Effect, E> - (...args: Args10): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - (...args: Args8): Effect.Effect, E> - (...args: Args9): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - (...args: Args8): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - (...args: Args7): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - (...args: Args6): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - (...args: Args5): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - (...args: Args4): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - (...args: Args3): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - } ? { - (...args: Args1): Effect.Effect, E> - (...args: Args2): Effect.Effect, E> - } - : T extends { - (...args: ArgsWithCallback): infer _R1 - } ? { - (...args: Args1): Effect.Effect, E> - } - : never - -type CallbackError = T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - (...args: ArgsWithCallback): infer _R10 -} ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - (...args: ArgsWithCallback): infer _R9 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - (...args: ArgsWithCallback): infer _R8 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - (...args: ArgsWithCallback): infer _R7 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - (...args: ArgsWithCallback): infer _R6 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - (...args: ArgsWithCallback): infer _R5 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - (...args: ArgsWithCallback): infer _R4 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - (...args: ArgsWithCallback): infer _R3 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - (...args: ArgsWithCallback): infer _R2 - } ? NonNullable - : T extends { - (...args: ArgsWithCallback): infer _R1 - } ? NonNullable - : never - -export const effectify: { - ) => any>(fn: F): Effectify> - ) => any, E>( - fn: F, - onError: (error: CallbackError, args: Parameters) => E - ): Effectify - ) => any, E, E2>( - fn: F, - onError: (error: CallbackError, args: Parameters) => E, - onSyncError: (error: unknown, args: Parameters) => E2 - ): Effectify -} = - // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type - ((fn: Function, onError?: (e: any, args: any) => any, onSyncError?: (e: any, args: any) => any) => - (...args: Array) => - Effect.callback((resume) => { - try { - fn(...args, (err: Error | null, result: A) => { - if (err) { - resume(Effect.fail(onError ? onError(err, args) : err)) - } else { - resume(Effect.succeed(result)) - } - }) - } catch (err) { - resume(onSyncError ? Effect.fail(onSyncError(err, args)) : Effect.die(err)) - } - })) as any diff --git a/packages/effect-app/src/utils/extend.ts b/packages/effect-app/src/utils/extend.ts deleted file mode 100644 index b956b3781..000000000 --- a/packages/effect-app/src/utils/extend.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Own-property define that shadows getter-only prototype fields such as - * Effect 4 `Schema.make`. `Object.assign` uses [[Set]] and cannot. - */ -export const defineOwn = (target: object, key: PropertyKey, value: unknown) => { - Object.defineProperty(target, key, { - value, - writable: true, - enumerable: true, - configurable: true - }) -} - -const assignOwn = (target: T, source: object): T => { - for (const key of Reflect.ownKeys(source)) { - const desc = Object.getOwnPropertyDescriptor(source, key) - if (!desc?.enumerable) continue - defineOwn(target, key, (source as Record)[key]) - } - return target -} - -export const extend = (a: T, ext: X) => { - assignOwn(a, ext) - return a as T & X -} - -export const extendM = (a: T, ext: (a: T) => X) => extend(a, ext(a)) diff --git a/packages/effect-app/src/utils/gen.ts b/packages/effect-app/src/utils/gen.ts deleted file mode 100644 index e2bae01ce..000000000 --- a/packages/effect-app/src/utils/gen.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { type Effect } from "effect/Effect" - -export namespace EffectGenUtils { - export type Success = EG extends Effect ? A - // there could be a case where the generator function does not yield anything, so we need to handle that - : EG extends (..._: infer _3) => Generator ? A - // generators yield Effect values - : EG extends (..._: infer _3) => Generator, infer A, infer _2> ? A - : never - - export type Error = EG extends Effect ? E - // there could be a case where the generator function does not yield anything, so we need to handle that - : EG extends (..._: infer _3) => Generator ? never - // generators yield Effect values - : EG extends (..._: infer _3) => Generator, infer _A, infer _2> ? E - : never - - export type Context = EG extends Effect ? R - // there could be a case where the generator function does not yield anything, so we need to handle that - : EG extends (..._: infer _3) => Generator ? never - // generators yield Effect values - : EG extends (..._: infer _3) => Generator, infer _A, infer _2> ? R - : never -} diff --git a/packages/effect-app/src/utils/logLevel.ts b/packages/effect-app/src/utils/logLevel.ts deleted file mode 100644 index dba7d1e27..000000000 --- a/packages/effect-app/src/utils/logLevel.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type * as LogLevel from "effect/LogLevel" - -export const LogLevelToSentry = (level: LogLevel.LogLevel) => { - switch (level) { - case "Debug": - return "debug" as const - case "Info": - return "info" as const - case "Warn": - return "warning" as const - case "Error": - return "error" as const - case "Fatal": - return "fatal" as const - } - return "log" as const -} diff --git a/packages/effect-app/src/utils/logger.ts b/packages/effect-app/src/utils/logger.ts deleted file mode 100644 index 68c0f9ab6..000000000 --- a/packages/effect-app/src/utils/logger.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import * as Effect from "effect/Effect" -import type * as LogLevel from "effect/LogLevel" -import * as Context from "../Context.ts" - -type Levels = "info" | "debug" | "warn" | "error" - -export class LogLevels extends Context.Reference("LogLevels", { - defaultValue: () => new Map() -}) {} - -export const makeLog = (namespace: string, defaultLevel: Levels = "warn") => { - const level = LogLevels.use((levels) => Effect.succeed(levels.get(namespace) ?? defaultLevel)) - const withLogNamespace = Effect.annotateLogs({ logNamespace: namespace }) - return { - logWarning: (...message: ReadonlyArray) => - Effect.flatMap(level, (l) => - l === "info" || l === "debug" || l === "warn" - ? Effect.logWarning(...message).pipe(withLogNamespace) - : Effect.void), - logError: (...message: ReadonlyArray) => Effect.logError(...message).pipe(withLogNamespace), - logFatal: (...message: ReadonlyArray) => Effect.logFatal(...message).pipe(withLogNamespace), - logInfo: (...message: ReadonlyArray) => - Effect.flatMap( - level, - (l) => l === "info" || l === "debug" ? Effect.logInfo(...message).pipe(withLogNamespace) : Effect.void - ), - logDebug: (...message: ReadonlyArray) => - Effect.flatMap(level, (l) => l === "debug" ? Effect.logDebug(...message).pipe(withLogNamespace) : Effect.void), - // for now always log - logWithLevel: (level: LogLevel.Severity, ...message: ReadonlyArray) => - Effect.logWithLevel(level)(...message).pipe(withLogNamespace) - } -} diff --git a/packages/effect-app/src/validation.ts b/packages/effect-app/src/validation.ts deleted file mode 100644 index 24f9ade4b..000000000 --- a/packages/effect-app/src/validation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Predicate } from "./Function.ts" - -export const all_ = (v: T, ...a: Predicate[]) => !a.some((x) => !x(v)) -export const all = (...a: Predicate[]) => (v: T) => all_(v, ...a) - -export const maxN = (max: number) => (v: number) => v <= max -export const minN = (min: number) => (v: number) => v >= min - -export const max = (max: number) => { - const f = maxN(max) - return (v: { length: number }) => f(v.length) -} -export const min = (min: number) => { - const f = minN(min) - return (v: { length: number }) => f(v.length) -} - -export * from "./validation/validators.ts" diff --git a/packages/effect-app/src/validation/validators.ts b/packages/effect-app/src/validation/validators.ts deleted file mode 100644 index f2ec83f69..000000000 --- a/packages/effect-app/src/validation/validators.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Not importing from /es/lib because they are .js files and bundlers probably assume they're cjs :( -import isEmail, { type IsEmailOptions } from "validator/lib/isEmail.js" - -// Source https://emailregex.com/ -// eslint-disable-next-line no-control-regex -// const EMAIL_REGEX = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/ - -// allow for "/", "+", "-", " .js" and digits -const PHONE_REGEX = /^\+?[\d-/ ]*$/ - -const PHONE_LENGTH = 7 - -/** - * Validate emails according to RFC 5322 - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const isValidEmail = isEmail as any as (str: string, options?: IsEmailOptions) => boolean - -/** - * Validates that phone number contains at least 7 numbers. - * Allowed characters are [0-1] "/", "+", "-", " .js" - */ -export const isValidPhone = (phone: string): boolean => { - const isValidPhone = PHONE_REGEX.test(phone) - if (!isValidPhone) return false - - const isValidLength = phone.split("").filter((c) => !isNaN(Number(c))).length >= PHONE_LENGTH - if (!isValidLength) return false - - return true -} diff --git a/packages/effect-app/src/withToast.ts b/packages/effect-app/src/withToast.ts deleted file mode 100644 index 9b0b6bb73..000000000 --- a/packages/effect-app/src/withToast.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as Cause from "effect/Cause" -import * as Fiber from "effect/Fiber" -import * as Context from "./Context.ts" -import * as Effect from "./Effect.ts" -import * as Layer from "./Layer.ts" -import type * as Option from "./Option.ts" -import * as S from "./Schema.ts" -import { CurrentToastId, Toast, type ToastId } from "./toast.ts" -import { wrapEffect } from "./utils.ts" - -export interface ToastOptions, WaiR, SucR, ErrR> { - stableToastId?: undefined | string | ((...args: Args) => string | undefined) - timeout?: number - showSpanInfo?: false - groupId?: string - onWaiting: - | string - | ((...args: Args) => string | null) - | null - | (( - ...args: Args - ) => Effect.Effect) - onSuccess: - | string - | ((a: A, ...args: Args) => string | null) - | null - | (( - a: A, - ...args: Args - ) => Effect.Effect) - onFailure: - | string - | (( - error: Option.Option, - ...args: Args - ) => string | { level: "warn" | "error"; message: string }) - | (( - error: Option.Option, - ...args: Args - ) => Effect.Effect) -} - -// @effect-diagnostics-next-line missingEffectServiceDependency:off -export class WithToast extends Context.Service()("WithToast", { - make: Effect.gen(function*() { - const toast = yield* Toast - return ( - options: ToastOptions - ) => - Effect.fnUntraced(function*(self: Effect.Effect, ...args: Args) { - const baseTimeout = options.timeout ?? 3_000 - - const stableToastId = typeof options.stableToastId === "function" - ? options.stableToastId(...args) - : options.stableToastId - - const requestId: string = yield* Effect.currentSpan.pipe( - Effect.map((span) => span.traceId), - Effect.orElseSucceed(() => S.StringId.make()) - ) - const groupId = options.groupId - const meta = { ...(groupId !== undefined ? { groupId } : {}), requestId } - - const t = yield* wrapEffect(options.onWaiting)(...args) - const toastId: ToastId | undefined = t === null - ? stableToastId - : stableToastId ?? `wait-${Math.random().toString(36).slice(2)}` - - const waitingFiber = t === null ? undefined : yield* Effect.forkChild( - Effect.sleep("1 seconds").pipe( - Effect.andThen(toast.info(t, { id: toastId!, timeout: Infinity, ...meta })) - ) - ) - const interruptWaiting = waitingFiber ? Fiber.interrupt(waitingFiber) : Effect.void - - return yield* self.pipe( - Effect.tap(Effect.fnUntraced(function*(a) { - yield* interruptWaiting - const t = yield* wrapEffect(options.onSuccess)(a, ...args) - if (t === null) { - return - } - yield* toast.success( - t, - toastId !== undefined - ? { id: toastId, timeout: baseTimeout, ...meta } - : { timeout: baseTimeout, ...meta } - ) - })), - Effect.tapCause(Effect.fnUntraced(function*(cause) { - yield* interruptWaiting - yield* Effect.logDebug( - "WithToast - caught error cause: " + Cause.squash(cause), - Cause.hasInterruptsOnly(cause), - cause - ) - - if (Cause.hasInterruptsOnly(cause)) { - if (toastId) yield* toast.dismiss(toastId) - return - } - - const spanInfo = options.showSpanInfo !== false - ? yield* Effect.currentSpan.pipe( - Effect.map((span) => `\nTrace: ${span.traceId}\nSpan: ${span.spanId}`), - Effect.orElseSucceed(() => "") - ) - : "" - - const t = yield* wrapEffect(options.onFailure)(Cause.findErrorOption(cause), ...args) - const opts = { timeout: baseTimeout * 2, ...meta } - - if (typeof t === "object") { - const message = t.message + spanInfo - return t.level === "warn" - ? yield* toast.warning(message, toastId !== undefined ? { ...opts, id: toastId } : opts) - : yield* toast.error(message, toastId !== undefined ? { ...opts, id: toastId } : opts) - } - yield* toast.error(t + spanInfo, toastId !== undefined ? { ...opts, id: toastId } : opts) - }, Effect.uninterruptible)), - toastId !== undefined ? Effect.provideService(CurrentToastId, CurrentToastId.of({ toastId })) : (_) => _ - ) - }) - }) -}) { - static readonly DefaultWithoutDependencies = Layer.effect(this, this.make) - static readonly Default = this.DefaultWithoutDependencies - - static readonly handle = , R, WaiR = never, SucR = never, ErrR = never>( - options: ToastOptions - ): (self: Effect.Effect, ...args: Args) => Effect.Effect => - (self, ...args) => this.use((_) => _(options)(self, ...args)) -} diff --git a/packages/effect-app/test/dateTimeLocal.test.ts b/packages/effect-app/test/dateTimeLocal.test.ts deleted file mode 100644 index 924cd9f52..000000000 --- a/packages/effect-app/test/dateTimeLocal.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { makeZonedLocal, pipeZonedLocal } from "effect-app/DateTime" -import * as DateTime from "effect/DateTime" -import { describe, expect, test } from "vitest" - -describe("local Date helpers", () => { - const start = new Date("2024-03-09T17:00:00.000Z") - - test("makeZonedLocal keeps the instant and uses the process zone", () => { - const zoned = makeZonedLocal(start) - expect(zoned.epochMilliseconds).toBe(start.getTime()) - expect(DateTime.zoneToString(zoned.zone)).toBe(DateTime.zoneToString(DateTime.zoneMakeLocal())) - }) - - test("pipeZonedLocal adds a local calendar day and returns the instant", () => { - const expected = new Date(start.getTime()) - expected.setDate(expected.getDate() + 1) - expect(pipeZonedLocal(start, DateTime.add({ days: 1 })).getTime()).toBe(expected.getTime()) - }) - - test("pipeZonedLocal runs every step before toDateUtc", () => { - const expected = new Date(start.getTime()) - expected.setDate(expected.getDate() + 1) - expected.setMonth(expected.getMonth() + 1) - const result = pipeZonedLocal(start, DateTime.add({ days: 1 }), DateTime.add({ months: 1 })) - expect(result.getTime()).toBe(expected.getTime()) - }) - - test("pipeZonedLocal accepts as many functions as pipe", () => { - const keep = (self: A) => self - const expected = new Date(start.getTime()) - expected.setDate(expected.getDate() + 1) - const result = pipeZonedLocal( - start, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - keep, - DateTime.add({ days: 1 }) - ) - expect(result.getTime()).toBe(expected.getTime()) - }) -}) diff --git a/packages/effect-app/test/moreStrings.test.ts b/packages/effect-app/test/moreStrings.test.ts deleted file mode 100644 index 88d014338..000000000 --- a/packages/effect-app/test/moreStrings.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as S from "effect-app/Schema" -import * as Arbitrary from "effect/Arbitrary" -import * as Effect from "effect/Effect" -import { customRandom, urlAlphabet } from "nanoid" -import { test } from "vitest" - -const nanoidAlphabet = new Set(urlAlphabet) - -const isNanoId = (value: string) => value.length === 21 && Array.from(value).every((char) => nanoidAlphabet.has(char)) - -test("StringId make generates nanoid-shaped values", () => { - for (let i = 0; i < 20; i++) { - const value = S.StringId.make() - expect(isNanoId(value)).toBe(true) - expect(S.is(S.StringId)(value)).toBe(true) - } -}) - -test("StringId native Arbitrary samples unique nanoid-shaped values", () => { - const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(S.StringId), { count: 20, seed: 5 })) - expect(new Set(samples).size).toBe(20) - for (const value of samples) { - expect(isNanoId(value)).toBe(true) - expect(S.is(S.StringId)(value)).toBe(true) - } -}) - -// Pre Effect 4.0.0-rc.114 StringIdArb (rc.112, 8bd5bfe64): -// fc.uint8Array({ minLength: 10 * 21, maxLength: 10 * 21 }) -// .map((bytes) => customRandom(urlAlphabet, 21, (n) => bytes.subarray(0, n))()) -test("StringId toCodecArbitrary uses the pre-rc.114 StringIdArb byte map", () => { - const size = 21 - const length = 10 * size - const stringIdArbMap = (bytes: Uint8Array) => customRandom(urlAlphabet, size, (n) => bytes.subarray(0, n))() - const bytes = Uint8Array.from({ length }, (_, i) => (i * 17 + 3) & 0xff) - const fromOld = stringIdArbMap(bytes) - expect(isNanoId(fromOld)).toBe(true) - // Same mapping the schema's toCodecArbitrary decode runs (customRandom + subarray). - expect(customRandom(urlAlphabet, size, (n) => bytes.subarray(0, n))()).toBe(fromOld) -}) - -test("prefixedStringId native Arbitrary matches pre-rc.114 prefix + nanoid map", () => { - const FooId = S.prefixedStringId()("foo", "FooId") - const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(FooId), { count: 10, seed: 5 })) - expect(new Set(samples).size).toBe(10) - for (const value of samples) { - expect(value.startsWith("foo-")).toBe(true) - expect(S.is(FooId)(value)).toBe(true) - const rest = value.slice("foo-".length) - expect(rest.length).toBeGreaterThan(0) - expect(Array.from(rest).every((char) => nanoidAlphabet.has(char))).toBe(true) - } -}) - -test("Url native Arbitrary samples http(s) URLs", () => { - const samples = Effect.runSync(Arbitrary.sampleEffect(Arbitrary.schema(S.Url), { count: 10, seed: 5 })) - expect(new Set(samples).size).toBeGreaterThan(1) - for (const value of samples) { - expect(value.startsWith("https://")).toBe(true) - expect(S.is(S.Url)(value)).toBe(true) - } -}) diff --git a/packages/effect-app/test/nativeEffectMigration.test.ts b/packages/effect-app/test/nativeEffectMigration.test.ts deleted file mode 100644 index 27e3c4eef..000000000 --- a/packages/effect-app/test/nativeEffectMigration.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { migrateEffectAppSource } from "effect-app/migration/nativeEffect" -import { describe, expect, test } from "vitest" - -describe("migrateEffectAppSource", () => { - test("rewrites named helpers and keeps the other imports", () => { - const source = ` -import { typedKeysOf, pretty } from "effect-app/utils" -import { tuple } from "effect-app/Function" -import { toBool, fromBool } from "effect-app/Option" - -const keys = typedKeysOf(value) -const pair = tuple(keys, pretty(value)) -const present = toBool(option) -const flag = fromBool(enabled) -` - const migrated = migrateEffectAppSource(source) - expect(migrated).toContain(`import { pretty } from "effect-app/utils"`) - expect(migrated).toContain(`import { fromBool } from "effect-app/Option"`) - expect(migrated).toContain(`import * as Struct from "effect/Struct"`) - expect(migrated).toContain(`import * as Tuple from "effect/Tuple"`) - expect(migrated).toContain(`import * as Option from "effect/Option"`) - expect(migrated).toContain("const keys = Struct.keys(value)") - expect(migrated).toContain("const pair = Tuple.make(keys, pretty(value))") - expect(migrated).toContain("const present = Option.isSome(option)") - expect(migrated).toContain("const flag = fromBool(enabled)") - expect(migrated).not.toContain("typedKeysOf") - expect(migrated).not.toContain("effect-app/Function") - }) - - test("rewrites aliases, namespaces, calls, and leaves strings and comments", () => { - const source = ` -import { dropUndefined as compact, assertUnreachable as unreachable } from "effect-app/utils" -import * as Dates from "effect-app/_ext/date" -import { chunk_, toNonEmptyArray } from "effect-app/Array" -import { DateValid } from "effect-app/Schema" - -// typedKeysOf stays in this comment -const label = "tuple(typedKeysOf)" -const fields = compact({ a: 1, b: undefined }) -const next = Dates.DateAddDays(today, 2) -const parts = chunk_(items, 10) -const some = toNonEmptyArray(items) -const Day = DateValid -const missing = unreachable(value) -` - const migrated = migrateEffectAppSource(source) - expect(migrated).toContain("// typedKeysOf stays in this comment") - expect(migrated).toContain(`const label = "tuple(typedKeysOf)"`) - expect(migrated).toContain("const fields = compact({ a: 1, b: undefined })") - expect(migrated).toContain("dropUndefined as compact") - expect(migrated).toContain( - "const next = pipeZonedLocal(today, DateTime.add({ days: 2 }))" - ) - expect(migrated).toContain("const parts = Chunk.fromIterable(Array.chunksOf(items, 10))") - expect(migrated).toContain("const some = Option.liftPredicate(Array.isReadonlyArrayNonEmpty)(items)") - expect(migrated).toContain(`import { Date } from "effect-app/Schema"`) - expect(migrated).toContain("const Day = Date") - expect(migrated).toContain("const missing = absurd(value)") - expect(migrated).toContain(`import { pipeZonedLocal } from "effect-app/DateTime"`) - expect(migrated).toContain(`import * as DateTime from "effect/DateTime"`) - expect(migrated).not.toContain("effect-app/_ext/date") - expect(migrated).not.toContain("DateValid") - }) - - test("rewrites a namespace member and keeps the namespace when it still has other members", () => { - const source = ` -import * as Chunk from "effect-app/Chunk" - -const first = Chunk.findFirstSimple(chunk, predicate) -const grouped = Chunk.groupByTChunk_(chunk, key) -` - const migrated = migrateEffectAppSource(source) - expect(migrated).toContain(`import * as Chunk from "effect-app/Chunk"`) - expect(migrated).toContain("const first = Chunk.findFirst(chunk, predicate)") - expect(migrated).toContain("const grouped = Chunk.groupByTChunk_(chunk, key)") - }) -}) diff --git a/packages/effect-app/test/rpc.test.ts b/packages/effect-app/test/rpc.test.ts deleted file mode 100644 index d76ed2e00..000000000 --- a/packages/effect-app/test/rpc.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type * as Effect from "effect/Effect" -import type * as Option from "effect/Option" -import { expect, test } from "vitest" -import { makeRpcClient, NotLoggedInError, UnauthorizedError } from "../src/client.js" -import { S } from "../src/index.js" -import { RpcContextMap } from "../src/rpc.js" - -export class RequestContextMap extends RpcContextMap.makeMap({ - allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError), - requireRoles: RpcContextMap.makeCustom()(UnauthorizedError, Array()), - test: RpcContextMap.make()(S.Never) -}) {} - -const stubMiddleware = { - requestContextMap: RequestContextMap.config, - requestContext: undefined as never -} -const { TaggedRequestFor } = makeRpcClient(stubMiddleware) -const TaggedRequest = TaggedRequestFor("Test").Query - -export class Stats extends TaggedRequest()("Stats", {}, { - allowedRoles: ["manager"], - success: { - usersActive24Hours: S.Finite, - usersActiveLastWeek: S.Finite, - newUsersLast24Hours: S.Finite, - newUsersLastWeek: S.Finite - } -}) {} - -declare const _stats: typeof Stats.Type -declare const _statsSuccess: typeof Stats.success.Type -declare const _statsError: typeof Stats.error.Type -declare const _statsRequestType: typeof Stats.type - -test("Void decodes and encodes as void", () => { - const statsFromMake = Stats.make({}) - const statsFromMakeOption = Stats.makeOption({}) - const statsFromMakeEffect = Stats.makeEffect({}) - - expect(S.decodeUnknownSync(S.Void)(undefined)).toBe(undefined) - expect(S.is(S.Void)(undefined)).toBe(true) - expect(S.decodeUnknownSync(S.Void)("test")).toBe(undefined) - expect(S.is(S.Void)("test")).toBe(true) - expect(S.encodeUnknownSync(S.Void)("test")).toBe(undefined) - expect(S.encodeUnknownSync(S.toCodecJson(S.Void))("test")).toBe(null) - expectTypeOf().toEqualTypeOf() - expectTypeOf().toEqualTypeOf<{ - readonly usersActive24Hours: number - readonly usersActiveLastWeek: number - readonly newUsersLast24Hours: number - readonly newUsersLastWeek: number - }>() - // Resource error carries only `config.error` (and optional `generalErrors`); rcm-derived - // middleware errors no longer leak into `resource.error` — they reach the wire via the - // middleware tag attached to the rpc group (`rpc.middlewares[*].error` failure-union). - expectTypeOf().toEqualTypeOf() - expectTypeOf().toEqualTypeOf<"query">() - expectTypeOf(statsFromMake).toEqualTypeOf() - expectTypeOf(statsFromMakeOption).toEqualTypeOf>() - expectTypeOf(statsFromMakeEffect).toEqualTypeOf>() -}) diff --git a/packages/effect-app/test/rpcMiddlewareContext.test.ts b/packages/effect-app/test/rpcMiddlewareContext.test.ts deleted file mode 100644 index 654a20c7a..000000000 --- a/packages/effect-app/test/rpcMiddlewareContext.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { expectTypeOf, test } from "vitest" -import { makeRpcClient, NotLoggedInError, UnauthorizedError } from "../src/client.js" -import { RpcContextMap } from "../src/rpc.js" - -// A stand-in for an app service (e.g. `UserProfile`) that the `allowAnonymous` -// inverted middleware provides into the handler context whenever auth is -// required (i.e. `allowAnonymous` is not `true`). -interface UserProfile { - readonly _tag: "UserProfile" -} - -// Mirrors a real app request-context map: one inverted key providing a service -// by default (unless opted out), and one custom (role) key. -class RequestContextMap extends RpcContextMap.makeMap({ - allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError), - allowRoles: RpcContextMap.makeCustom()(UnauthorizedError, Array()) -}) {} - -type Map = typeof RequestContextMap.config - -// The context a handler receives for a given rpc config — exactly the -// `GetEffectContext<_RC, rpc.config>` the router computes for the handler -// (see `RpcMiddleware.ts`). Testing it against config literals pins down the -// middleware semantics directly. -type CtxFor = RpcContextMap.GetEffectContext - -test("`GetEffectContext`: the inverted `UserProfile` provision is gated by `allowAnonymous`, not `allowRoles`", () => { - // Empty config = nothing opted out → auth required → `UserProfile` provided. - expectTypeOf>().toEqualTypeOf() - - // `allowRoles` does not gate `UserProfile` — still provided. - expectTypeOf>().toEqualTypeOf() - - // Negative case: opting into anonymous removes the `UserProfile` provision. - expectTypeOf>().toBeNever() - - // Anonymous wins over roles for the inverted provision. - expectTypeOf>().toBeNever() - - // Regression documentation: a config that collapses to `Record` - // (what the no-config command overload used to produce) WRONGLY drops - // `UserProfile` — `keyof Record` is `string`, so `allowAnonymous` - // satisfies `key extends keyof T`, and `T["allowAnonymous"]` (`never`) `extends - // true`, taking the anonymous branch. This is exactly why the overload must - // yield `{}` (see the command-builder test below). - expectTypeOf>>().toBeNever() -}) - -// End-to-end guard for the actual fix: a command defined with NO config object -// must still provide `UserProfile` (auth required by default). This exercises the -// no-config `Command` overload, which now yields an empty `{}` config instead of -// `Record`. -const { TaggedRequestFor } = makeRpcClient({ - requestContextMap: RequestContextMap.config, - requestContext: undefined as never -}) -const Command = TaggedRequestFor("Test").Command - -class NoConfigCmd extends Command()("NoConfigCmd", {}) {} - -test("a no-config command still provides the auth-required `UserProfile` service", () => { - expectTypeOf>().toEqualTypeOf() -}) diff --git a/packages/effect-app/test/schema.test.ts b/packages/effect-app/test/schema.test.ts deleted file mode 100644 index bb9a15d32..000000000 --- a/packages/effect-app/test/schema.test.ts +++ /dev/null @@ -1,681 +0,0 @@ -// import { generateFromArbitrary } from "@effect-app/infra/test" -import * as Array from "effect-app/Array" -import * as S from "effect-app/Schema" -import { specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema" -import * as Effect from "effect/Effect" -import * as Struct from "effect/Struct" -import { describe, expect, expectTypeOf, test } from "vitest" - -const A = S.Struct({ a: S.NonEmptyString255, email: S.NullOr(S.Email) }) -test("works", () => { - console.log(S.StringId.make()) - // console.log(generateFromArbitrary(S.A.make(A)).value) - console.log(S.AST.resolveTitle(S.Email.ast)) - console.log(S.AST.resolveDescription(S.Email.ast)) - console.log(S.toJsonSchemaDocument(S.Email)) - console.log(S.toJsonSchemaDocument(S.Email)) - console.log(S.decodeExit(A)({ a: Array.range(1, 256).join(""), email: "hello" })) -}) - -test("literal default works", () => { - const l = S.Literals(["a", "b"]) - expect(l.Default).toBe("a") - expectTypeOf(l.Default).toEqualTypeOf<"a">() - const s = S.Struct({ l: l.withConstructorDefault }) - expect(s.make({}).l).toBe("a") - - const l2 = l.changeDefault("b") - const s2 = S.Struct({ l: l2.withConstructorDefault }) - expect(s2.make({}).l).toBe("b") -}) - -// Toy schema for `dropConstructorDefault`: a field with a constructor -// default that should NOT auto-populate when deriving a partial-update -// schema from a "create" schema. -const Person = S.Struct({ - name: S.String, - notes: S.NullOr(S.String).pipe(S.optionalKey, S.withConstructorDefault(Effect.succeed(null))) -}) - -test("plain Struct.omit leaks the inherited constructor default (the bug)", () => { - const UpdatePersonBuggy = S.Struct(Struct.omit(Person.fields, [] as const)) - expect(UpdatePersonBuggy.make({ name: "Mario" })).toEqual({ name: "Mario", notes: null }) -}) - -test("Struct.map(S.dropConstructorDefault) strips the inherited constructor default (the fix)", () => { - const UpdatePersonFixed = S.Struct(Struct.map(Struct.omit(Person.fields, [] as const), S.dropConstructorDefault)) - expect(UpdatePersonFixed.make({ name: "Mario" })).toEqual({ name: "Mario" }) -}) - -test("Struct.map(S.dropConstructorDefault) still omits the requested keys, like Struct.omit", () => { - const NameOnly = S.Struct(Struct.map(Struct.omit(Person.fields, ["notes"] as const), S.dropConstructorDefault)) - expect(NameOnly.make({ name: "Mario" })).toEqual({ name: "Mario" }) - expectTypeOf().toEqualTypeOf<{ readonly name: string }>() -}) - -test("makeExactOptional drops the inherited constructor default too", () => { - const UpdatePerson = S.Struct(S.makeExactOptional(Person.fields)) - expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" }) -}) - -test("dropConstructorDefault stays 'no-default' after annotate/annotateKey (Rebuild fix)", () => { - const notesDropped = S.dropConstructorDefault(Person.fields.notes) - expectTypeOf<(typeof notesDropped)["~type.constructor.default"]>().toEqualTypeOf<"no-default">() - - const notesAnnotated = notesDropped.annotate({ description: "no longer defaulted" }) - expectTypeOf<(typeof notesAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">() - - const notesKeyAnnotated = notesDropped.annotateKey({ description: "no longer defaulted (key)" }) - expectTypeOf<(typeof notesKeyAnnotated)["~type.constructor.default"]>().toEqualTypeOf<"no-default">() - - const UpdatePerson = S.Struct({ name: Person.fields.name, notes: notesAnnotated }) - expect(UpdatePerson.make({ name: "Mario" })).toEqual({ name: "Mario" }) -}) - -test("NonEmptyString255.Type uses the named brand alias", () => { - type A = typeof S.NonEmptyString255.Type - type B = string & S.NonEmptyString255Brand - expectTypeOf().toEqualTypeOf() -}) - -test("Opaque accepts an explicit Encoded type", () => { - interface User { - readonly id: string - readonly _tag: "User" - } - - interface UserEncoded { - readonly id: string - } - - const baseSchema = S.Struct({ id: S.String, name: S.String }) - const _UserSchema = S.Opaque()(baseSchema) - - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().not.toEqualTypeOf>() -}) - -test("Opaque with one generic keeps the base encoded shape", () => { - interface User { - readonly id: string - } - - const baseSchema = S.Struct({ id: S.String }) - const _UserSchema = S.Opaque()(baseSchema) - - expectTypeOf>().toEqualTypeOf<{ readonly id: string }>() - expectTypeOf>().toEqualTypeOf() -}) - -test("Opaque preserves optional Struct.make input", () => { - interface User { - readonly a?: string | undefined - readonly b?: number | undefined - } - - const schema = S.Opaque()(S.Struct({ - a: S.optional(S.String), - b: S.optional(S.Number) - })) - - const made = schema.make() - expect(made).toEqual({}) - expectTypeOf(made).toEqualTypeOf() -}) - -test("Opaque preserves optional TaggedStruct.make input", () => { - interface OnlyTag { - readonly _tag: "OnlyTag" - } - - const schema = S.Opaque()(S.TaggedStruct("OnlyTag", {})) - - const made = schema.make() - expect(made).toEqual({ _tag: "OnlyTag" }) - expectTypeOf(made).toEqualTypeOf() -}) - -test("S.Literals([\"A\", \"B\"]).Default is typed as \"A\"", () => { - const l = S.Literals(["A", "B"]) - expect(l.Default).toBe("A") - expectTypeOf(l.Default).toEqualTypeOf<"A">() -}) - -test("Struct.make accepts void when all fields are optional", () => { - const schema = S.Struct({ - a: S.optional(S.String), - b: S.optional(S.Number) - }) - - const made = schema.make() - expect(made).toEqual({}) - expectTypeOf(made).toEqualTypeOf<{ readonly a?: string | undefined; readonly b?: number | undefined }>() -}) - -test("StructNestedEncoded resolves Struct and from.Encoded", () => { - const plain = S.Struct({ a: S.String, b: S.NullOr(S.String) }) - const encodedKeys = plain.pipe(S.encodeKeys({ b: "b_encoded" })) - - expectTypeOf>().toEqualTypeOf<{ - readonly a: string - readonly b: string | null - }>() - - expectTypeOf>().toEqualTypeOf<{ - readonly a: string - readonly b_encoded: string | null - }>() -}) - -test("TaggedStruct.make accepts void when only constructor-default fields exist", () => { - const schema = S.TaggedStruct("OnlyTag", {}) - - const made = schema.make() - expect(made).toEqual({ _tag: "OnlyTag" }) - expectTypeOf(made).toEqualTypeOf<{ readonly _tag: "OnlyTag" }>() -}) - -test("tagged union derives tag map and tags from v4 literal ast", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }), - S.TaggedStruct("C", { c: S.Boolean }) - ]) - const caseA = schema.cases["A"] - const caseB = schema.cases["B"] - const caseC = schema.cases["C"] - const isAOrB = schema.isAnyOf(["A", "B"]) - - expect(caseA.fields._tag.ast.literal).toBe("A") - expect(caseB.fields._tag.ast.literal).toBe("B") - expect(caseC.fields._tag.ast.literal).toBe("C") - expect(S.decodeSync(schema.tags)("A")).toBe("A") - expect(S.decodeSync(schema.tags)("B")).toBe("B") - expect(S.decodeSync(schema.tags)("C")).toBe("C") - expect(() => S.decodeUnknownSync(schema.tags)("D")).toThrow() - - expect(schema.guards.A({ _tag: "A", a: "ok" })).toBe(true) - expect(schema.guards.A({ _tag: "B", b: 1 })).toBe(false) - expect(schema.guards.B({ _tag: "B", b: 1 })).toBe(true) - expect(schema.guards.B({ _tag: "A", a: "ok" })).toBe(false) - expect(schema.guards.C({ _tag: "C", c: true })).toBe(true) - expect(schema.guards.C({ _tag: "A", a: "ok" })).toBe(false) - - expect(isAOrB({ _tag: "A", a: "ok" })).toBe(true) - expect(isAOrB({ _tag: "B", b: 1 })).toBe(true) - expect(isAOrB({ _tag: "C", c: true })).toBe(false) -}) - -test("TaggedUnion tags returns a Literals schema with correct literal values", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("X", { x: S.String }), - S.TaggedStruct("Y", { y: S.Finite }) - ]) - - expect(schema.tags.literals).toEqual(["X", "Y"]) - expectTypeOf(schema.tags.literals).toMatchTypeOf() -}) - -test("TaggedUnion tags.pick returns a subset of the tag literals", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }), - S.TaggedStruct("C", { c: S.Boolean }) - ]) - - const subset = schema.tags.pick(["A", "C"]) - expect(subset.literals).toEqual(["A", "C"]) - expect(S.decodeSync(subset)("A")).toBe("A") - expect(S.decodeSync(subset)("C")).toBe("C") - expect(() => S.decodeUnknownSync(subset)("B")).toThrow() -}) - -test("tags standalone function extracts tags from member schemas", () => { - const members = [ - S.TaggedStruct("Foo", { foo: S.String }), - S.TaggedStruct("Bar", { bar: S.Finite }) - ] as const - - const tagSchema = S.tags(members) - expect(tagSchema.literals).toEqual(["Foo", "Bar"]) - expect(S.decodeSync(tagSchema)("Foo")).toBe("Foo") - expect(S.decodeSync(tagSchema)("Bar")).toBe("Bar") - expect(() => S.decodeUnknownSync(tagSchema)("Baz")).toThrow() -}) - -test("ExtendTaggedUnion adds tags to an existing Union", () => { - const union = S.Union([ - S.TaggedStruct("P", { p: S.String }), - S.TaggedStruct("Q", { q: S.Finite }) - ]) - const extended = S.ExtendTaggedUnion(union) - - expect(extended.tags.literals).toEqual(["P", "Q"]) - expect(S.decodeSync(extended.tags)("P")).toBe("P") - expect(S.decodeSync(extended.tags)("Q")).toBe("Q") - expect(() => S.decodeUnknownSync(extended.tags)("R")).toThrow() - expect(extended.cases["P"].fields._tag.ast.literal).toBe("P") - expect(extended.guards.P({ _tag: "P", p: "ok" })).toBe(true) - expect(extended.guards.P({ _tag: "Q", q: 1 })).toBe(false) -}) - -test("TaggedUnion match dispatches on _tag", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - const matcher = schema.match({ - A: (v) => `got A: ${v.a}`, - B: (v) => `got B: ${v.b}` - }) - expect(matcher({ _tag: "A", a: "hello" })).toBe("got A: hello") - expect(matcher({ _tag: "B", b: 42 })).toBe("got B: 42") -}) - -test("TaggedUnion with single member", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("Only", { val: S.String }) - ]) - - expect(schema.tags.literals).toEqual(["Only"]) - expect(S.decodeSync(schema.tags)("Only")).toBe("Only") - expect(() => S.decodeUnknownSync(schema.tags)("Other")).toThrow() - expect(schema.guards.Only({ _tag: "Only", val: "x" })).toBe(true) -}) - -test("TaggedUnion tags type is narrowed to the exact tag literals", () => { - const schema = S.TaggedUnion([ - S.TaggedStruct("Alpha", { a: S.String }), - S.TaggedStruct("Beta", { b: S.Finite }), - S.TaggedStruct("Gamma", { c: S.Boolean }) - ]) - - type Tags = S.Schema.Type - expectTypeOf().toEqualTypeOf<"Alpha" | "Beta" | "Gamma">() -}) - -test("TaggedUnion with encodeKeys renaming a non-tag key", () => { - const MemberA = S.TaggedStruct("A", { firstName: S.String }).pipe( - S.encodeKeys({ firstName: "first_name" }) - ) - const MemberB = S.TaggedStruct("B", { lastName: S.Finite }).pipe( - S.encodeKeys({ lastName: "last_name" }) - ) - - const schema = S.TaggedUnion([MemberA, MemberB]) - - expect(schema.tags.literals).toEqual(["A", "B"]) - expect(S.decodeSync(schema.tags)("A")).toBe("A") - expect(S.decodeSync(schema.tags)("B")).toBe("B") - - // decode from encoded (snake_case) to decoded (camelCase) - const decoded = S.decodeUnknownSync(schema)({ _tag: "A", first_name: "Alice" }) - expect(decoded).toEqual({ _tag: "A", firstName: "Alice" }) - - const decoded2 = S.decodeUnknownSync(schema)({ _tag: "B", last_name: 42 }) - expect(decoded2).toEqual({ _tag: "B", lastName: 42 }) - - // encode back to snake_case - const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" }) - expect(encoded).toEqual({ _tag: "A", first_name: "Alice" }) - - // guards work on decoded values - expect(schema.guards.A({ _tag: "A", firstName: "Alice" })).toBe(true) - expect(schema.guards.A({ _tag: "B", lastName: 42 })).toBe(false) - expect(schema.guards.B({ _tag: "B", lastName: 42 })).toBe(true) -}) - -test("TaggedUnion with TaggedClass members", () => { - class Foo extends S.TaggedClass()("Foo", { name: S.String }) {} - class Bar extends S.TaggedClass()("Bar", { count: S.Finite }) {} - - const schema = S.TaggedUnion([Foo, Bar]) - - expect(schema.tags.literals).toEqual(["Foo", "Bar"]) - expect(S.decodeSync(schema.tags)("Foo")).toBe("Foo") - expect(S.decodeSync(schema.tags)("Bar")).toBe("Bar") - expect(() => S.decodeUnknownSync(schema.tags)("Baz")).toThrow() - - const decoded = S.decodeUnknownSync(schema)({ _tag: "Foo", name: "Alice" }) - expect(decoded).toBeInstanceOf(Foo) - expect(decoded).toEqual(new Foo({ name: "Alice" })) - - const decoded2 = S.decodeUnknownSync(schema)({ _tag: "Bar", count: 3 }) - expect(decoded2).toBeInstanceOf(Bar) - expect(decoded2).toEqual(new Bar({ count: 3 })) - - expect(schema.guards.Foo(new Foo({ name: "Alice" }))).toBe(true) - expect(schema.guards.Foo(new Bar({ count: 3 }))).toBe(false) - expect(schema.guards.Bar(new Bar({ count: 3 }))).toBe(true) -}) - -describe("ReadonlySetFromArray", () => { - test("decodes an array of strings to a Set", () => { - const schema = S.ReadonlySetFromArray(S.String) - const decoded = S.decodeUnknownSync(schema)(["a", "b", "c"]) - expect(decoded).toEqual(new Set(["a", "b", "c"])) - }) - - test("encodes a Set back to an array", () => { - const schema = S.ReadonlySetFromArray(S.String) - const encoded = S.encodeSync(schema)(new Set(["a", "b"])) - expect(encoded).toEqual(["a", "b"]) - }) - - test("decodes with NumberFromString as value", () => { - const schema = S.ReadonlySetFromArray(S.NumberFromString) - const decoded = S.decodeUnknownSync(schema)(["1", "2", "3"]) - expect(decoded).toEqual(new Set([1, 2, 3])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("encodes with NumberFromString as value", () => { - const schema = S.ReadonlySetFromArray(S.NumberFromString) - const encoded = S.encodeSync(schema)(new Set([1, 2, 3])) - expect(encoded).toEqual(["1", "2", "3"]) - }) - - test("rejects invalid input", () => { - const schema = S.ReadonlySetFromArray(S.NumberFromString) - expect(() => S.decodeUnknownSync(schema)([1, 2])).toThrow() - }) -}) - -describe("ReadonlyMapFromArray", () => { - test("decodes an array of tuples to a Map", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.Finite }) - const decoded = S.decodeUnknownSync(schema)([["a", 1], ["b", 2]]) - expect(decoded).toEqual(new Map([["a", 1], ["b", 2]])) - }) - - test("encodes a Map back to an array of tuples", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.Finite }) - const encoded = S.encodeSync(schema)(new Map([["a", 1], ["b", 2]])) - expect(encoded).toEqual([["a", 1], ["b", 2]]) - }) - - test("decodes with NumberFromString as key", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.String }) - const decoded = S.decodeUnknownSync(schema)([["1", "one"], ["2", "two"]]) - expect(decoded).toEqual(new Map([[1, "one"], [2, "two"]])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("encodes with NumberFromString as key", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.String }) - const encoded = S.encodeSync(schema)(new Map([[1, "one"], [2, "two"]])) - expect(encoded).toEqual([["1", "one"], ["2", "two"]]) - }) - - test("decodes with NumberFromString as value", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.NumberFromString }) - const decoded = S.decodeUnknownSync(schema)([["a", "10"], ["b", "20"]]) - expect(decoded).toEqual(new Map([["a", 10], ["b", 20]])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("encodes with NumberFromString as value", () => { - const schema = S.ReadonlyMapFromArray({ key: S.String, value: S.NumberFromString }) - const encoded = S.encodeSync(schema)(new Map([["a", 10], ["b", 20]])) - expect(encoded).toEqual([["a", "10"], ["b", "20"]]) - }) - - test("decodes with NumberFromString as both key and value", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.NumberFromString }) - const decoded = S.decodeUnknownSync(schema)([["1", "10"], ["2", "20"]]) - expect(decoded).toEqual(new Map([[1, 10], [2, 20]])) - expectTypeOf(decoded).toEqualTypeOf>() - }) - - test("rejects invalid input", () => { - const schema = S.ReadonlyMapFromArray({ key: S.NumberFromString, value: S.String }) - expect(() => S.decodeUnknownSync(schema)([[1, "val"]])).toThrow() - }) -}) - -describe("ReadonlySet (with withConstructorDefault)", () => { - test("make provides withConstructorDefault", () => { - const schema = S.ReadonlySet(S.NumberFromString) - const struct = S.Struct({ items: schema.withConstructorDefault }) - const made = struct.make({}) - expect(made.items).toEqual(new Set()) - }) - - test("decodes a Set with NumberFromString values", () => { - const schema = S.ReadonlySet(S.NumberFromString) - const decoded = S.decodeUnknownSync(schema)(new Set(["1", "2"])) - expect(decoded).toEqual(new Set([1, 2])) - }) - - test("Encoded is a Set, not an array", () => { - const schema = S.ReadonlySet(S.String) - const encoded = S.encodeSync(schema)(new Set(["a"])) - expect(encoded).toEqual(new Set(["a"])) - expectTypeOf(encoded).toEqualTypeOf>() - }) -}) - -describe("ReadonlyMap (with withConstructorDefault)", () => { - test("make provides withConstructorDefault", () => { - const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String }) - const struct = S.Struct({ items: schema.withConstructorDefault }) - const made = struct.make({}) - expect(made.items).toEqual(new Map()) - }) - - test("decodes a Map with NumberFromString keys", () => { - const schema = S.ReadonlyMap({ key: S.NumberFromString, value: S.String }) - const decoded = S.decodeUnknownSync(schema)(new Map([["1", "one"]])) - expect(decoded).toEqual(new Map([[1, "one"]])) - }) - - test("Encoded is a Map, not an array of tuples", () => { - const schema = S.ReadonlyMap({ key: S.String, value: S.Finite }) - const encoded = S.encodeSync(schema)(new Map([["a", 1]])) - expect(encoded).toEqual(new Map([["a", 1]])) - expectTypeOf(encoded).toEqualTypeOf>() - }) -}) - -describe("JSON Schema", () => { - test("Email has format, minLength, maxLength", () => { - const doc = S.toJsonSchemaDocument(S.Email) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/Email" }, - definitions: { - Email: { - type: "string", - title: "Email", - description: "an email according to RFC 5322", - format: "email", - minLength: 2, - maxLength: 998 - } - } - }) - }) - - test("Email specialJsonSchemaDocument flattens allOf", () => { - const doc = specialJsonSchemaDocument(S.Email) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/Email" }, - definitions: { - Email: { - type: "string", - title: "Email", - description: "an email according to RFC 5322", - format: "email", - minLength: 2, - maxLength: 998 - } - } - }) - }) - - test("Date Encoded is Date; JSON codec encodes ISO strings", () => { - const d = new Date("2024-01-01T00:00:00.000Z") - expect(S.decodeUnknownSync(S.Date)(d)).toBe(d) - expect(S.encodeSync(S.Date)(d)).toBe(d) - expect(S.encodeSync(S.toCodecJson(S.Date))(d)).toBe("2024-01-01T00:00:00.000Z") - const doc = S.toJsonSchemaDocument(S.Date) - expect(doc.dialect).toBe("draft-2020-12") - expect(doc.schema).toEqual({ type: "string" }) - }) - - test("DateFromString keeps string Encoded", () => { - const doc = S.toJsonSchemaDocument(S.DateFromString) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/DateOrInvalid" }, - definitions: { - DateOrInvalid: { - type: "string", - description: "an ISO 8601 date string that will be decoded as a Date (may be invalid)", - format: "date-time" - } - } - }) - }) - - test("PhoneNumber has format phone", () => { - const doc = specialJsonSchemaDocument(S.PhoneNumber) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/PhoneNumber" }, - definitions: { - PhoneNumber: { - type: "string", - title: "PhoneNumber", - description: "a phone number with at least 7 digits", - format: "phone" - } - } - }) - }) - - test("Url has format uri", () => { - const doc = specialJsonSchemaDocument(S.Url) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { "$ref": "#/$defs/Url" }, - definitions: { - Url: { - type: "string", - title: "Url", - format: "uri" - } - } - }) - }) -}) - -describe("generateGuards", () => { - const StateSchema = S.TaggedUnion([ - S.TaggedStruct("Active", { since: S.String }), - S.TaggedStruct("Inactive", { reason: S.String }), - S.TaggedStruct("Pending", { eta: S.Finite }) - ]) - - type State = S.Schema.Type - type Entity = { readonly state: State; readonly name: string } - - const { isActive, isAnyOf, isInactive, isPending } = StateSchema.generateGuards("state") - - test("isActive narrows to Active member", () => { - const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - expect(isActive(entity)).toBe(true) - if (isActive(entity)) { - expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>() - } - }) - - test("isActive returns false for non-Active", () => { - const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" } - expect(isActive(entity)).toBe(false) - }) - - test("isInactive narrows to Inactive member", () => { - const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" } - expect(isInactive(entity)).toBe(true) - }) - - test("isPending narrows to Pending member", () => { - const entity: Entity = { state: { _tag: "Pending", eta: 42 }, name: "foo" } - expect(isPending(entity)).toBe(true) - }) - - test("isAnyOf narrows to union of specified members", () => { - const isActiveOrPending = isAnyOf(["Active", "Pending"]) - const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - const pending: Entity = { state: { _tag: "Pending", eta: 5 }, name: "bar" } - const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" } - - expect(isActiveOrPending(active)).toBe(true) - expect(isActiveOrPending(pending)).toBe(true) - expect(isActiveOrPending(inactive)).toBe(false) - - if (isActiveOrPending(active)) { - expectTypeOf(active.state).toEqualTypeOf< - { readonly _tag: "Active"; readonly since: string } | { readonly _tag: "Pending"; readonly eta: number } - >() - } - }) - - test("guards use schema-based validation (built-in guards)", () => { - expect(StateSchema.guards.Active({ _tag: "Active" })).toBe(false) - expect(StateSchema.guards.Active({ _tag: "Active", since: "2024-01-01" })).toBe(true) - }) -}) - -describe("generateGuardsFor", () => { - const StateSchema = S.TaggedUnion([ - S.TaggedStruct("Active", { since: S.String }), - S.TaggedStruct("Inactive", { reason: S.String }), - S.TaggedStruct("Pending", { eta: S.Finite }) - ]) - - type State = S.Schema.Type - type Entity = { readonly state: State; readonly name: string } - - const { isActive, isAnyOf } = StateSchema.generateGuardsFor()("state") - - test("isActive narrows to Active member", () => { - const entity: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - expect(isActive(entity)).toBe(true) - if (isActive(entity)) { - expectTypeOf(entity.state).toEqualTypeOf<{ readonly _tag: "Active"; readonly since: string }>() - } - }) - - test("isActive returns false for non-Active", () => { - const entity: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "foo" } - expect(isActive(entity)).toBe(false) - }) - - test("isAnyOf narrows to union of specified members", () => { - const isActiveOrPending = isAnyOf(["Active", "Pending"]) - const active: Entity = { state: { _tag: "Active", since: "2024-01-01" }, name: "foo" } - const inactive: Entity = { state: { _tag: "Inactive", reason: "expired" }, name: "baz" } - - expect(isActiveOrPending(active)).toBe(true) - expect(isActiveOrPending(inactive)).toBe(false) - }) - - test("ExtendTaggedUnion also exposes generateGuardsFor", () => { - const union = S.Union([ - S.TaggedStruct("X", { x: S.String }), - S.TaggedStruct("Y", { y: S.Finite }) - ]) - const extended = S.ExtendTaggedUnion(union) - type Obj = { readonly field: S.Schema.Type } - const { isX, isY } = extended.generateGuardsFor()("field") - - expect(isX({ field: { _tag: "X", x: "hi" } })).toBe(true) - expect(isX({ field: { _tag: "Y", y: 1 } })).toBe(false) - expect(isY({ field: { _tag: "Y", y: 1 } })).toBe(true) - }) -}) diff --git a/packages/effect-app/test/secretURL.test.ts b/packages/effect-app/test/secretURL.test.ts deleted file mode 100644 index af2ab186c..000000000 --- a/packages/effect-app/test/secretURL.test.ts +++ /dev/null @@ -1,160 +0,0 @@ -import * as Chunk from "effect/Chunk" -import * as Config from "effect/Config" -import * as ConfigProvider from "effect/ConfigProvider" -import * as Effect from "effect/Effect" -import { describe, expect, test } from "vitest" -import { fromChunk, fromString, isSecretURL, make, secretURL, unsafeWipe, value } from "../src/Config/SecretURL.js" - -const testUrls = [ - "https://example.com/path?key=secret123", - "http://user:password@host.com:8080/resource", - "postgres://admin:p4ssw0rd@db.internal:5432/mydb", - "redis://default:token@cache.example.com:6379", - "mongodb+srv://user:pass@cluster.mongodb.net/db", - "https://api.example.com/v2/endpoint?token=abc&format=json" -] - -describe("SecretURL", () => { - describe("fromString / value roundtrip", () => { - test.each(testUrls)("preserves %s", (url) => { - const secret = fromString(url) - expect(value(secret)).toBe(url) - }) - }) - - describe("make / value roundtrip", () => { - test.each(testUrls)("preserves %s via byte array", (url) => { - const bytes = url.split("").map((c) => c.charCodeAt(0)) - const secret = make(bytes) - expect(value(secret)).toBe(url) - }) - }) - - describe("fromChunk / value roundtrip", () => { - test.each(testUrls)("preserves %s via Chunk", (url) => { - const chunk = Chunk.fromIterable(url.split("")) - const secret = fromChunk(chunk) - expect(value(secret)).toBe(url) - }) - }) - - describe("toString", () => { - test("redacts the URL, showing only protocol", () => { - const secret = fromString("https://example.com/secret") - expect(String(secret)).toBe("SecretURL(https://)") - }) - - test("shows http protocol", () => { - const secret = fromString("http://example.com") - expect(String(secret)).toBe("SecretURL(http://)") - }) - - test("shows unknown for non-URL strings", () => { - const secret = fromString("not-a-url") - expect(String(secret)).toBe("SecretURL(unknown://)") - }) - }) - - describe("toJSON", () => { - test("returns tag and protocol only", () => { - const secret = fromString("https://example.com/secret") - expect(JSON.parse(JSON.stringify(secret))).toEqual({ _tag: "SecretURL", protocol: "https" }) - }) - - test("returns unknown protocol for non-URL", () => { - const secret = fromString("not-a-url") - expect(JSON.parse(JSON.stringify(secret))).toEqual({ _tag: "SecretURL", protocol: "unknown" }) - }) - }) - - describe("isSecretURL", () => { - test("returns true for SecretURL", () => { - const secret = fromString("https://example.com") - expect(isSecretURL(secret)).toBe(true) - }) - - test("returns false for plain string", () => { - expect(isSecretURL("https://example.com")).toBe(false) - }) - - test("returns false for plain object", () => { - expect(isSecretURL({ raw: [1, 2, 3] })).toBe(false) - }) - }) - - describe("unsafeWipe", () => { - test("zeroes out raw bytes", () => { - const secret = fromString("https://example.com") - unsafeWipe(secret) - expect(value(secret)).toBe("\0".repeat("https://example.com".length)) - }) - }) - - describe("non-URL strings", () => { - const nonUrls = [ - "just-a-secret-token", - "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.abc", - "sk-1234567890abcdef", - "some random string with spaces and symbols!@#$%" - ] - - test.each(nonUrls)("preserves non-URL value: %s", (input) => { - const secret = fromString(input) - expect(value(secret)).toBe(input) - expect(String(secret)).toBe("SecretURL(unknown://)") - expect(JSON.parse(JSON.stringify(secret))).toEqual({ _tag: "SecretURL", protocol: "unknown" }) - }) - }) - - describe("special characters", () => { - test("preserves URLs with encoded characters", () => { - const url = "https://example.com/path?q=hello%20world&name=%C3%A9" - expect(value(fromString(url))).toBe(url) - }) - - test("preserves URLs with unicode", () => { - const url = "https://example.com/café" - expect(value(fromString(url))).toBe(url) - }) - - test("preserves URLs with special query params", () => { - const url = "https://example.com?a=1&b=2&c=foo+bar" - expect(value(fromString(url))).toBe(url) - }) - }) - - describe("secretURL config", () => { - const run = (config: Config.Config, provider: ConfigProvider.ConfigProvider) => - Effect.runSync(config.parse(provider)) - - test("reads from env-style ConfigProvider (QUEUE_URL)", () => { - const url = "https://sqs.us-east-1.amazonaws.com/123456789/my-queue" - const provider = ConfigProvider.fromEnv({ env: { QUEUE_URL: url } }) - const result = run(secretURL("QUEUE_URL"), provider) - expect(value(result)).toBe(url) - expect(String(result)).toBe("SecretURL(https://)") - }) - - test("reads from fromUnknown with nested config ({ queue: { url } })", () => { - const url = "redis://default:token@cache.example.com:6379" - const provider = ConfigProvider.fromUnknown({ queue: { url } }) - const config = secretURL("url").pipe(Config.nested("queue")) - const result = run(config, provider) - expect(value(result)).toBe(url) - expect(String(result)).toBe("SecretURL(redis://)") - }) - - test("reads non-URL secret from env", () => { - const token = "sk-1234567890abcdef" - const provider = ConfigProvider.fromEnv({ env: { API_KEY: token } }) - const result = run(secretURL("API_KEY"), provider) - expect(value(result)).toBe(token) - expect(String(result)).toBe("SecretURL(unknown://)") - }) - - test("rejects empty string", () => { - const provider = ConfigProvider.fromEnv({ env: { QUEUE_URL: "" } }) - expect(() => run(secretURL("QUEUE_URL"), provider)).toThrow() - }) - }) -}) diff --git a/packages/effect-app/test/special.test.ts b/packages/effect-app/test/special.test.ts deleted file mode 100644 index 9931c7757..000000000 --- a/packages/effect-app/test/special.test.ts +++ /dev/null @@ -1,1261 +0,0 @@ -import { InvalidStateError, LoginError, NotFoundError, NotLoggedInError, OptimisticConcurrencyException, ServiceUnavailableError, UnauthorizedError, ValidationError } from "effect-app/client/errors" -import { RequestContext } from "effect-app/RequestContext" -import * as AppSchema from "effect-app/Schema" -import { Class } from "effect-app/Schema/Class" -import { flattenNestedAnyOf, flattenSimpleAllOf, specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema" -import { deduplicateOpenApiSchemas } from "effect-app/Schema/SpecialOpenApi" -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as S from "effect/Schema" -import * as SchemaGetter from "effect/SchemaGetter" -import { describe, expect, it } from "vitest" - -class _FacadeModel extends AppSchema.Class<_FacadeModel>("FacadeModel")({ value: S.String }) { - get upperValue() { - return this.value.toUpperCase() - } - - static describe(self: { readonly value: string }) { - return `value:${self.value}` - } -} - -interface FacadeModelEncoded { - readonly value: string -} - -interface FacadeModelMake { - readonly value: string -} - -class FacadeModel extends AppSchema.OpaqueFacade< - FacadeModel, - FacadeModelEncoded, - FacadeModelMake, - never, - never ->()(_FacadeModel) { - declare readonly value: string - declare readonly upperValue: string - - get decoratedValue() { - return `facade:${this.value}` - } - - label() { - return this.decoratedValue - } -} - -class _RenamedFacadeModel extends AppSchema.Class<_RenamedFacadeModel>("RenamedFacadeModel")({ - internalName: S.String -}) { - get upperName() { - return this.internalName.toUpperCase() - } -} - -interface RenamedFacadeModelEncoded { - readonly external_name: string -} - -interface RenamedFacadeModelMake { - readonly internalName: string -} - -class RenamedFacadeModel extends AppSchema.OpaqueFacade< - RenamedFacadeModel, - RenamedFacadeModelEncoded, - RenamedFacadeModelMake, - never, - never ->()(_RenamedFacadeModel.pipe(S.encodeKeys({ internalName: "external_name" }))) { - declare readonly internalName: string - declare readonly upperName: string - - get decoratedName() { - return `facade:${this.internalName}` - } -} - -const _StructFacadeModel = AppSchema.Struct({ value: S.String }) - -interface StructFacadeModelEncoded { - readonly value: string -} - -interface StructFacadeModelMake { - readonly value: string -} - -class StructFacadeModel extends AppSchema.OpaqueFacade< - StructFacadeModel, - StructFacadeModelEncoded, - StructFacadeModelMake, - never, - never ->()(_StructFacadeModel) { - declare readonly value: string -} - -const _RenamedStructFacadeModel = AppSchema.Struct({ internalName: S.String }).pipe( - S.encodeKeys({ internalName: "external_name" }) -) - -interface RenamedStructFacadeModelEncoded { - readonly external_name: string -} - -interface RenamedStructFacadeModelMake { - readonly internalName: string -} - -class RenamedStructFacadeModel extends AppSchema.OpaqueFacade< - RenamedStructFacadeModel, - RenamedStructFacadeModelEncoded, - RenamedStructFacadeModelMake, - never, - never ->()(_RenamedStructFacadeModel) { - declare readonly internalName: string -} - -const _RenamedOpaqueStructFacadeModel = AppSchema - .Opaque< - { readonly internalName: string }, - RenamedStructFacadeModelEncoded - >()(AppSchema.Struct({ internalName: S.String })) - .pipe(S.encodeKeys({ internalName: "external_name" })) - -class RenamedOpaqueStructFacadeModel extends AppSchema.OpaqueFacade< - RenamedOpaqueStructFacadeModel, - RenamedStructFacadeModelEncoded, - RenamedStructFacadeModelMake, - never, - never ->()(_RenamedOpaqueStructFacadeModel) { - declare readonly internalName: string -} - -describe("Class", () => { - describe("strict", () => { - it("encoding doesnt accepts plain objects matching the struct (Fields argument)", () => { - class A extends Class("A")({ a: S.String }) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(() => S.encodeUnknownSync(A)({ a: "world" })).toThrow() - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("encoding rejects plain objects matching the struct (Struct argument)", () => { - class A extends Class("A")(S.Struct({ a: S.String })) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(() => S.encodeUnknownSync(A)({ a: "world" })).toThrow() - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("decoding still works normally", () => { - class A extends Class("A")({ a: S.String }) {} - - const decoded = S.decodeUnknownSync(A)({ a: "hello" }) - expect(decoded).toBeInstanceOf(A) - expect(decoded.a).toBe("hello") - - expect(() => S.decodeUnknownSync(A)(null)).toThrow() - expect(() => S.decodeUnknownSync(A)({ a: 1 })).toThrow() - }) - - it("S.is accepts class instances and not matching plain objects", () => { - class A extends Class("A")({ a: S.String }) {} - - expect(S.is(A)(new A({ a: "hello" }))).toBe(true) - expect(S.is(A)({ a: "world" })).toBe(false) - expect(S.is(A)({ a: 1 })).toBe(false) - expect(S.is(A)(null)).toBe(false) - }) - - it("rejects values that don't match the struct", () => { - class A extends Class("A")({ a: S.String }) {} - - expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow() - expect(() => S.encodeUnknownSync(A)("not an object")).toThrow() - }) - - it("returns a class constructor — new and instanceof work", () => { - class A extends Class("A")({ a: S.String }) {} - - const instance = new A({ a: "hello" }) - expect(instance).toBeInstanceOf(A) - expect(instance.a).toBe("hello") - }) - - it("preserves fields and identifier", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - expect(A.identifier).toBe("A") - expect(Object.keys(A.fields)).toStrictEqual(["a", "b"]) - }) - }) - - describe("non strict", () => { - it("encoding accepts plain objects matching the struct (Fields argument)", () => { - class A extends Class("A")({ a: S.String }, undefined, { strict: false }) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" }) - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("encoding accepts plain objects matching the struct (Struct argument)", () => { - class A extends Class("A")(S.Struct({ a: S.String }), undefined, { strict: false }) {} - - expect(S.encodeUnknownSync(A)(new A({ a: "hello" }))).toStrictEqual({ a: "hello" }) - expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" }) - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - }) - - it("S.is accepts class instances and matching plain objects", () => { - class A extends Class("A")({ a: S.String }, undefined, { strict: false }) {} - - expect(S.is(A)(new A({ a: "hello" }))).toBe(true) - expect(S.is(A)({ a: "world" })).toBe(true) - expect(S.is(A)({ a: 1 })).toBe(false) - expect(S.is(A)(null)).toBe(false) - }) - }) -}) - -describe("Class constructor", () => { - describe("strict", () => { - it("works as a base class — new, instanceof, encoding plain objects", () => { - class A extends Class("A")({ a: S.String }) {} - - const instance = new A({ a: "hello" }) - expect(instance).toBeInstanceOf(A) - expect(instance.a).toBe("hello") - - expect(S.encodeUnknownSync(A)(instance)).toStrictEqual({ a: "hello" }) - expect(() => S.encodeUnknownSync(A)({ a: "world" })).toThrow() - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow() - }) - - it("decoding works normally", () => { - class A extends Class("A")({ a: S.String }) {} - - const decoded = S.decodeUnknownSync(A)({ a: "hello" }) - expect(decoded).toBeInstanceOf(A) - expect(decoded.a).toBe("hello") - - expect(() => S.decodeUnknownSync(A)({ a: 1 })).toThrow() - }) - - it("exposes fields, identifier", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - expect(A.identifier).toBe("A") - expect(Object.keys(A.fields)).toStrictEqual(["a", "b"]) - }) - }) - - describe("non strict", () => { - it("works as a base class — new, instanceof, encoding plain objects", () => { - class A extends Class("A")({ a: S.String }, undefined, { strict: false }) {} - - const instance = new A({ a: "hello" }) - expect(instance).toBeInstanceOf(A) - expect(instance.a).toBe("hello") - - expect(S.encodeUnknownSync(A)(instance)).toStrictEqual({ a: "hello" }) - expect(S.encodeUnknownSync(A)({ a: "world" })).toStrictEqual({ a: "world" }) - expect(() => S.encodeUnknownSync(A)(null)).toThrow() - expect(() => S.encodeUnknownSync(A)({ a: 123 })).toThrow() - }) - }) -}) - -describe("TaggedStruct constructor", () => { - it("works with _tag, make, and encoding plain objects", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - expect(instance).toEqual({ _tag: "Circle", radius: 5 }) - - expect(S.encodeUnknownSync(Circle)(instance)).toStrictEqual({ _tag: "Circle", radius: 5 }) - expect(S.encodeUnknownSync(Circle)({ _tag: "Circle", radius: 10 })).toStrictEqual({ _tag: "Circle", radius: 10 }) - - expect(() => S.encodeUnknownSync(Circle)(null)).toThrow() - expect(() => S.encodeUnknownSync(Circle)({ _tag: "Circle", radius: "nope" })).toThrow() - }) - - it("decoding works normally", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const decoded = S.decodeUnknownSync(Circle)({ _tag: "Circle", radius: 5 }) - expect(decoded).toEqual({ _tag: "Circle", radius: 5 }) - }) - - it("S.decodeSync(S.toType(X)) should report n length schema error", () => { - class X extends S.Opaque()(S.TaggedStruct("X", { n: S.String.pipe(S.check(S.isMinLength(3))) })) {} - - try { - S.decodeSync(S.toType(X))({ _tag: "X", n: "a" /* not length 3 */ }) - expect.fail("expected decode to fail with a SchemaError") - } catch (error) { - expect(S.isSchemaError(error)).toBe(true) - if (S.isSchemaError(error)) { - expect(error.message).toContain("n") - expect(error.message.toLowerCase()).toContain("length") - } - } - }) - - it("exposes fields", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - expect(Object.keys(Circle.fields)).toContain("_tag") - expect(Object.keys(Circle.fields)).toContain("radius") - }) -}) - -describe("strict declaration option", () => { - it("Class strict: true keeps class-level expected errors", () => { - class X extends Class("X")({ n: S.String.pipe(S.check(S.isMinLength(3))) }, undefined, { strict: true }) {} - - expect(() => S.decodeSync(S.toType(X))({ n: "a" })).toThrow("Expected X") - }) - - it("TaggedStruct preserves field-level expected errors", () => { - class X extends S.Opaque()(AppSchema.TaggedStruct("X", { n: S.String.pipe(S.check(S.isMinLength(3))) })) {} - - expect(() => S.decodeSync(S.toType(X))({ _tag: "X", n: "a" })).toThrow() - }) - - it("Class with encoded override strict: true keeps class-level expected errors", () => { - class X extends Class("X")({ n: S.String.pipe(S.check(S.isMinLength(3))) }, undefined, { - strict: true - }) {} - - expect(() => S.decodeSync(S.toType(X))({ n: "a" })).toThrow("Expected X") - }) - - it("TaggedStruct with opaque wrapper decodes tagged input", () => { - class X extends S.Opaque()(AppSchema.TaggedStruct("X", { n: S.String.pipe(S.check(S.isMinLength(3))) })) {} - - expect(() => S.decodeSync(S.toType(X))({ _tag: "X", n: "a" })).toThrow() - }) -}) - -describe("Class.copy", () => { - it("creates a new instance with updated fields", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - const instance = new A({ a: "hello", b: 1 }) - const copied: A = A.copy(instance, { b: 2 }) - expect(copied).toBeInstanceOf(A) - expect(copied.a).toBe("hello") - expect(copied.b).toBe(2) - }) - - it("accepts a function for updates", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - const instance = new A({ a: "hello", b: 1 }) - const copied: A = A.copy(instance, (a) => ({ b: a.b + 1 })) - expect(copied).toBeInstanceOf(A) - expect(copied.b).toBe(2) - }) - - it("is pipeable", () => { - class A extends Class("A")({ a: S.String, b: S.Number }) {} - - const instance = new A({ a: "hello", b: 1 }) - const copied: A = A.copy({ b: 2 })(instance) - expect(copied).toBeInstanceOf(A) - expect(copied.b).toBe(2) - }) -}) - -describe("TaggedStruct (opaque).copy", () => { - it("creates a new instance with updated fields", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - const copied = Circle.copy(instance, { radius: 10 }) - expect(copied).toEqual({ _tag: "Circle", radius: 10 }) - }) - - it("accepts a function for updates", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - const copied = Circle.copy(instance, (c) => ({ radius: c.radius * 2 })) - expect(copied).toEqual({ _tag: "Circle", radius: 10 }) - }) -}) - -describe("OpaqueFacade", () => { - it("keeps OpaqueFacade usable with struct schemas", () => { - const decoded = S.decodeSync(StructFacadeModel)({ value: "hello" }) - - expect(decoded).toStrictEqual({ value: "hello" }) - expect(S.encodeSync(StructFacadeModel)(decoded)).toStrictEqual({ value: "hello" }) - expect(S.is(StructFacadeModel)(decoded)).toBe(true) - expect(S.is(StructFacadeModel)({ value: 1 })).toBe(false) - }) - - it("keeps encoded-key struct facades on the struct path", () => { - const decoded = S.decodeSync(RenamedStructFacadeModel)({ external_name: "fran" }) - - expect(decoded).toStrictEqual({ internalName: "fran" }) - expect(decoded).not.toBeInstanceOf(RenamedStructFacadeModel) - expect(S.encodeSync(RenamedStructFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" }) - expect(S.is(RenamedStructFacadeModel)(decoded)).toBe(true) - expect(S.is(RenamedStructFacadeModel)({ external_name: "fran" })).toBe(false) - }) - - it("keeps encoded-key opaque struct facades on the struct path", () => { - const decoded = S.decodeSync(RenamedOpaqueStructFacadeModel)({ external_name: "fran" }) - - expect(decoded).toStrictEqual({ internalName: "fran" }) - expect(decoded).not.toBeInstanceOf(RenamedOpaqueStructFacadeModel) - expect(S.encodeSync(RenamedOpaqueStructFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" }) - expect(S.is(RenamedOpaqueStructFacadeModel)(decoded)).toBe(true) - expect(S.is(RenamedOpaqueStructFacadeModel)({ external_name: "fran" })).toBe(false) - }) - - it("decodes through the public facade class", () => { - const decoded = S.decodeSync(FacadeModel)({ value: "hello" }) - const decodedEffect = Effect.runSync(S.decodeUnknownEffect(FacadeModel)({ value: "effect" })) - - expect(decoded).toBeInstanceOf(FacadeModel) - expect(decodedEffect).toBeInstanceOf(FacadeModel) - expect(decoded).toBeInstanceOf(_FacadeModel) - expect(decoded.value).toBe("hello") - expect(decoded.upperValue).toBe("HELLO") - expect(decoded.decoratedValue).toBe("facade:hello") - expect(decoded.label()).toBe("facade:hello") - expect(FacadeModel.describe(decoded)).toBe("value:hello") - }) - - it("validates facade instances with S.is", () => { - const instance = new FacadeModel({ value: "hello" }) - - expect(instance).toBeInstanceOf(FacadeModel) - expect(instance.upperValue).toBe("HELLO") - expect(instance.label()).toBe("facade:hello") - expect(S.is(FacadeModel)(instance)).toBe(true) - expect(S.is(FacadeModel)(S.decodeSync(FacadeModel)({ value: "world" }))).toBe(true) - expect(S.is(FacadeModel)({ value: "plain" })).toBe(false) - expect(S.is(FacadeModel)({ value: 1 })).toBe(false) - }) - - it("decodes renamed keys through the public facade class", () => { - const decoded = S.decodeSync(RenamedFacadeModel)({ external_name: "fran" }) - - expect(decoded).toBeInstanceOf(RenamedFacadeModel) - expect(decoded).toBeInstanceOf(_RenamedFacadeModel) - expect(Object.getPrototypeOf(decoded)).toBe(RenamedFacadeModel.prototype) - expect(decoded.internalName).toBe("fran") - expect(decoded.upperName).toBe("FRAN") - expect(decoded.decoratedName).toBe("facade:fran") - expect(S.encodeSync(RenamedFacadeModel)(decoded)).toStrictEqual({ external_name: "fran" }) - expect(S.is(RenamedFacadeModel)(decoded)).toBe(true) - expect(S.is(RenamedFacadeModel)({ internalName: "plain" })).toBe(false) - expect(S.is(RenamedFacadeModel)({ external_name: "plain" })).toBe(false) - }) - - it("keeps generated struct facades decodable with public statics", () => { - const input: typeof RequestContext.Encoded = { - span: { traceId: "trace", spanId: "span", sampled: true }, - name: "operation", - locale: "en", - namespace: "tenant" - } - - const decoded = S.decodeSync(RequestContext)(input) - - expect(decoded).not.toBeInstanceOf(RequestContext) - expect(S.is(RequestContext)(decoded)).toBe(true) - expect(RequestContext.toMonitoring(decoded)).toStrictEqual({ operationName: "operation", locale: "en" }) - }) - - it("keeps error facades class-only", () => { - const makeStructErrorFacade = () => - AppSchema.OpaqueErrorFacadeClass< - { readonly value: string }, - { readonly value: string }, - { readonly value: string }, - never, - never - >()( - // Runtime guard: only class schemas are accepted (struct schemas throw). - AppSchema.Struct({ value: S.String }) as never - ) - - expect(makeStructErrorFacade).toThrow("OpaqueErrorFacadeClass requires a class schema") - }) -}) - -describe("Struct.copy", () => { - it("creates a new value with updated fields", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("accepts a function for updates", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, (a) => ({ b: a.b + 1 })) - - expect(copied).toEqual({ a: "hello", b: 2 }) - }) - - it("allows widening updates against the full struct type", () => { - const A = AppSchema.Struct({ - name: S.String, - state: S.Union([ - S.Struct({ _tag: S.tag("a"), a: S.String }), - S.Struct({ _tag: S.tag("b"), b: S.Number }) - ]) - }) - - const instance = A.make({ name: "x", state: { _tag: "a", a: "a" } }) - const copied = A.copy(instance, { state: { _tag: "b", b: 1 } }) - - expect(copied).toEqual({ name: "x", state: { _tag: "b", b: 1 } }) - }) - - it("copy function is preserved when using .annotate()", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }).annotate({ title: "A" }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("copy function is preserved when using .annotateKey()", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }).annotateKey({ title: "A" }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("copy function is preserved when using .mapFields()", () => { - const A = AppSchema.Struct({ a: S.String, b: S.Number }).mapFields((f) => ({ ...f })) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) - - it("copy function is preserved through chained calls", () => { - const A = AppSchema - .Struct({ a: S.String, b: S.Number }) - .annotate({ title: "A" }) - .annotateKey({ description: "test" }) - - const instance = A.make({ a: "hello", b: 1 }) - const copied = A.copy(instance, { b: 2 }) - - expect(copied).toEqual({ a: "hello", b: 2 }) - expect(copied).not.toBe(instance) - }) -}) - -describe("TaggedStruct.copy", () => { - it("creates a new tagged value with updated fields", () => { - const Circle = AppSchema.TaggedStruct("Circle", { radius: S.Number }) - - const instance = Circle.make({ radius: 5 }) - const copied = Circle.copy(instance, { radius: 10 }) - - expect(copied).toEqual({ _tag: "Circle", radius: 10 }) - expect(copied).not.toBe(instance) - }) -}) - -describe("TaggedError", () => { - it("InvalidStateError toString includes the message", () => { - const error = new InvalidStateError("something went wrong") - expect(error.toString()).toContain("something went wrong") - }) - - it("NotFoundError toString includes the message", () => { - const error = new NotFoundError({ type: "User", id: "123" }) - expect(error.toString()).toContain("Didn't find User") - expect(error.toString()).toContain("123") - }) - - it("ServiceUnavailableError toString includes the message", () => { - const error = new ServiceUnavailableError("service down") - expect(error.toString()).toContain("service down") - }) - - it("ValidationError toString includes the message", () => { - const error = new ValidationError({ errors: ["field required"] }) - expect(error.toString()).toContain("Validation failed") - expect(error.toString()).toContain("field required") - }) - - it("NotLoggedInError toString includes the message", () => { - const error = new NotLoggedInError("not logged in") - expect(error.toString()).toContain("not logged in") - }) - - it("LoginError toString includes the message", () => { - const error = new LoginError("login failed") - expect(error.toString()).toContain("login failed") - }) - - it("UnauthorizedError toString includes the message", () => { - const error = new UnauthorizedError("forbidden") - expect(error.toString()).toContain("forbidden") - }) - - it("OptimisticConcurrencyException toString includes the message", () => { - const error = new OptimisticConcurrencyException({ message: "conflict" }) - expect(error.toString()).toContain("conflict") - }) - - it("OptimisticConcurrencyException from details toString includes the message", () => { - const error = new OptimisticConcurrencyException({ type: "User", id: "123", code: 409 }) - expect(error.toString()).toContain("Existing User 123 record changed") - }) -}) - -describe("SpecialJsonSchema", () => { - it("nullable to optional — from NullOr", () => { - const nullableDecodedUndefinedEncoded = (schema: S.Top) => { - const isNullableSchema = "members" in schema - && globalThis.Array.isArray((schema as any).members) - && (schema as any).members.length === 2 - && (schema as any).members.some((member: any) => member.ast._tag === "Null") - - const nullableMembers = isNullableSchema ? (schema as any).members as ReadonlyArray : undefined - const innerSchema = nullableMembers - ? nullableMembers.find((member: any) => member.ast._tag !== "Null")! - : schema - - const nullableSchema = isNullableSchema ? schema : S.NullOr(schema) - - return nullableSchema.pipe( - S.encodeTo(S.optionalKey(innerSchema), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ) - } - - const fromNullOr = nullableDecodedUndefinedEncoded(S.NullOr(S.String)) - const structFromNullOr = S.Struct({ status: fromNullOr }) - - const encode = S.encodeUnknownSync(structFromNullOr as any) - const encodedNull = encode({ status: null }) - expect("status" in encodedNull).toBe(false) - expect(encode({ status: "test" })).toStrictEqual({ status: "test" }) - - const decode = S.decodeUnknownSync(structFromNullOr as any) - expect(decode({})).toStrictEqual({ status: null }) - expect(decode({ status: "test" })).toStrictEqual({ status: "test" }) - - const doc = specialJsonSchemaDocument(structFromNullOr) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { - "type": "object", - "additionalProperties": true, - "properties": { - "status": { "type": "string" } - } - }, - definitions: {} - }) - }) - - it("identifies X universally — deduplicates same-fingerprint references", () => { - const X = S.String.annotate({ title: "X", identifier: "X" }) - - const s = S.Struct({ - a: S.NullOr(X).pipe( - S.encodeTo(S.optionalKey(X), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ), - b: S.NullOr(X).pipe( - S.encodeTo(S.optionalKey(X), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ), - c: S.NullOr(X), - d: X, - e: X.pipe(S.optionalKey) - }) - - const doc = specialJsonSchemaDocument(s) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { - "type": "object", - "additionalProperties": true, - "properties": { - "a": { "$ref": "#/$defs/X" }, - "b": { "$ref": "#/$defs/X" }, - "c": { - "anyOf": [ - { "$ref": "#/$defs/X" }, - { "type": "null" } - ] - }, - "d": { "$ref": "#/$defs/X" }, - "e": { "$ref": "#/$defs/X" } - }, - "required": ["c", "d"] - }, - definitions: { - X: { - "type": "string", - "title": "X" - } - } - }) - }) - - it("shared annotated schema via helper — deduplicates", () => { - const X = S.String.annotate({ title: "X", identifier: "X" }) - - const cache = new WeakMap() - const nullableDecodedUndefinedEncoded = (schema: S.Top) => { - const isNullableSchema = "members" in schema - && globalThis.Array.isArray((schema as any).members) - && (schema as any).members.length === 2 - && (schema as any).members.some((member: any) => member.ast._tag === "Null") - - const nullableMembers = isNullableSchema ? (schema as any).members as ReadonlyArray : undefined - const innerSchema = nullableMembers - ? nullableMembers.find((member: any) => member.ast._tag !== "Null")! - : schema - - const cached = cache.get(innerSchema.ast) - if (cached !== undefined) return cached - - const nullableSchema = isNullableSchema ? schema : S.NullOr(schema) - const out = nullableSchema.pipe( - S.encodeTo(S.optionalKey(innerSchema), { - decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), - encode: SchemaGetter.transformOptional(Option.filter(Predicate.isNotNull)) - }) - ) - - cache.set(innerSchema.ast, out) - return out - } - - const structWithShared = S.Struct({ - a: nullableDecodedUndefinedEncoded(X), - b: nullableDecodedUndefinedEncoded(S.NullOr(X)), - c: S.NullOr(X), - d: X, - e: X.pipe(S.optionalKey) - }) - - const doc = specialJsonSchemaDocument(structWithShared) - expect(doc).toStrictEqual({ - dialect: "draft-2020-12", - schema: { - "type": "object", - "additionalProperties": true, - "properties": { - "a": { "$ref": "#/$defs/X" }, - "b": { "$ref": "#/$defs/X" }, - "c": { - "anyOf": [ - { "$ref": "#/$defs/X" }, - { "type": "null" } - ] - }, - "d": { "$ref": "#/$defs/X" }, - "e": { "$ref": "#/$defs/X" } - }, - "required": ["c", "d"] - }, - definitions: { - X: { - "type": "string", - "title": "X" - } - } - }) - }) -}) - -describe("SpecialOpenApi", () => { - it("deduplicates identical components.schemas entries with same base identifier", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: { - "/foo": { - get: { - responses: { - 200: { - content: { - "application/json": { - schema: { $ref: "#/components/schemas/X" } - } - } - } - } - } - }, - "/bar": { - get: { - responses: { - 200: { - content: { - "application/json": { - schema: { $ref: "#/components/schemas/X1" } - } - } - } - } - } - } - }, - components: { - schemas: { - X: { type: "string", title: "X" }, - X1: { type: "string", title: "X" } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - // X1 should be removed, and $ref to X1 rewritten to X - expect(result.components.schemas).toStrictEqual({ - X: { type: "string", title: "X" } - }) - expect( - result.paths["/bar"].get.responses[200].content["application/json"].schema - ) - .toStrictEqual({ $ref: "#/components/schemas/X" }) - }) - - it("does not deduplicate entries with different representations", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - X: { type: "string", title: "X" }, - X1: { type: "number", title: "X" } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - // Both should remain since they have different representations - expect(result.components.schemas).toStrictEqual({ - X: { type: "string", title: "X" }, - X1: { type: "number", title: "X" } - }) - }) - - it("returns spec unchanged when no duplicates exist", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Foo: { type: "string" }, - Bar: { type: "number" } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) - expect(result).toStrictEqual(spec) - }) - - it("rewrites nested $ref pointers in allOf/anyOf/oneOf", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: { - "/baz": { - post: { - requestBody: { - content: { - "application/json": { - schema: { - anyOf: [ - { $ref: "#/components/schemas/Y1" }, - { type: "null" } - ] - } - } - } - } - } - } - }, - components: { - schemas: { - Y: { type: "object", properties: { name: { type: "string" } } }, - Y1: { type: "object", properties: { name: { type: "string" } } } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas).toStrictEqual({ - Y: { type: "object", properties: { name: { type: "string" } } } - }) - expect( - result.paths["/baz"].post.requestBody.content["application/json"].schema.anyOf[0] - ) - .toStrictEqual({ $ref: "#/components/schemas/Y" }) - }) - - it("rewrites $ref pointers inside definitions themselves", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Inner: { type: "string" }, - Inner1: { type: "string" }, - Outer: { - type: "object", - properties: { - field: { $ref: "#/components/schemas/Inner1" } - } - } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(Object.keys(result.components.schemas)).toStrictEqual(["Inner", "Outer"]) - expect(result.components.schemas.Outer.properties.field).toStrictEqual({ - $ref: "#/components/schemas/Inner" - }) - }) - - it("handles spec without components gracefully", () => { - const spec = { openapi: "3.1.0", info: { title: "Test", version: "1.0" }, paths: {} } - const result = deduplicateOpenApiSchemas(spec) - expect(result).toStrictEqual(spec) - }) - - it("flattens allOf in components.schemas", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - PositiveInt: { - type: "integer", - allOf: [{ exclusiveMinimum: 0, title: "PositiveInt" }] - } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas.PositiveInt).toStrictEqual({ - type: "integer", - exclusiveMinimum: 0, - title: "PositiveInt" - }) - }) - - it("does not flatten allOf containing $ref entries", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Composed: { - type: "object", - allOf: [{ $ref: "#/components/schemas/Base" }] - }, - Base: { type: "object", properties: { id: { type: "string" } } } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas.Composed).toStrictEqual({ - type: "object", - allOf: [{ $ref: "#/components/schemas/Base" }] - }) - }) - - it("does not flatten allOf entries that define their own type", () => { - const spec = { - openapi: "3.1.0", - info: { title: "Test", version: "1.0" }, - paths: {}, - components: { - schemas: { - Mixed: { - type: "object", - allOf: [{ type: "string", minLength: 1 }] - } - } - } - } - - const result = deduplicateOpenApiSchemas(spec) as any - - expect(result.components.schemas.Mixed).toStrictEqual({ - type: "object", - allOf: [{ type: "string", minLength: 1 }] - }) - }) -}) - -describe("flattenSimpleAllOf", () => { - it("flattens constraint-only allOf into parent with type", () => { - const input = { - type: "integer", - allOf: [{ exclusiveMinimum: 0, title: "PositiveInt" }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual({ - type: "integer", - exclusiveMinimum: 0, - title: "PositiveInt" - }) - }) - - it("flattens string type with multiple constraints", () => { - const input = { - type: "string", - allOf: [ - { minLength: 1, maxLength: 255 }, - { title: "NonEmptyString255" } - ] - } - expect(flattenSimpleAllOf(input)).toStrictEqual({ - type: "string", - minLength: 1, - maxLength: 255, - title: "NonEmptyString255" - }) - }) - - it("does not flatten allOf with $ref", () => { - const input = { - type: "object", - allOf: [{ $ref: "#/components/schemas/Base" }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual(input) - }) - - it("does not flatten allOf entries with their own type", () => { - const input = { - type: "object", - allOf: [{ type: "string", minLength: 1 }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual(input) - }) - - it("allOf entry wins on property conflict", () => { - const input = { - type: "integer", - title: "OldTitle", - allOf: [{ title: "NewTitle", minimum: 0 }] - } - expect(flattenSimpleAllOf(input)).toStrictEqual({ - type: "integer", - title: "NewTitle", - minimum: 0 - }) - }) -}) - -describe("Post-processing integration — real Effect Schema types", () => { - it("PositiveInt — allOf flattened, no wrapping", () => { - const doc = specialJsonSchemaDocument(AppSchema.PositiveInt) - expect(doc.definitions["PositiveInt"]).toStrictEqual({ - type: "integer", - exclusiveMinimum: 0 - }) - }) - - it("NonEmptyString255 — multiple allOf constraints merged", () => { - const doc = specialJsonSchemaDocument(AppSchema.NonEmptyString255) - expect(doc.definitions["NonEmptyString255"]).toStrictEqual({ - type: "string", - minLength: 1, - maxLength: 255 - }) - }) - - it("NullOr(NonEmptyString64k) — null preserved in anyOf, allOf flattened in definition", () => { - const schema = S.Struct({ note: S.NullOr(AppSchema.NonEmptyString64k) }) - const doc = specialJsonSchemaDocument(schema) - - // null variant preserved (correct JSON Schema for NullOr) - expect(doc.schema).toStrictEqual({ - type: "object", - additionalProperties: true, - properties: { - note: { - anyOf: [ - { $ref: "#/$defs/NonEmptyString64k" }, - { type: "null" } - ] - } - }, - required: ["note"] - }) - - // allOf flattened in the referenced definition - expect(doc.definitions["NonEmptyString64k"]).toStrictEqual({ - type: "string", - minLength: 1, - maxLength: 65536 - }) - }) - - it("NonNegativeInt — allOf flattened", () => { - const doc = specialJsonSchemaDocument(AppSchema.NonNegativeInt) - expect(doc.definitions["NonNegativeInt"]).toStrictEqual({ - type: "integer", - minimum: 0 - }) - }) - - it("NullOr union flattens nested anyOf members", () => { - const A = S.String.annotate({ identifier: "A" }) - const B = S.Boolean.annotate({ identifier: "B" }) - const schema = S.Struct({ - value: S.NullOr(S.Union([A, B])) - }) - const doc = specialJsonSchemaDocument(schema) - const valueProp = (doc.schema as Record)["properties"]["value"] - - expect(valueProp).toStrictEqual({ - anyOf: [ - { $ref: "#/$defs/A" }, - { $ref: "#/$defs/B" }, - { type: "null" } - ] - }) - }) -}) - -describe("flattenNestedAnyOf", () => { - it("flattens nested anyOf with no sibling keys", () => { - const input = { - anyOf: [ - { anyOf: [{ type: "string" }, { type: "number" }] }, - { type: "null" } - ] - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ - anyOf: [ - { type: "string" }, - { type: "number" }, - { type: "null" } - ] - }) - }) - - it("does not flatten anyOf entry with sibling keys", () => { - const input = { - anyOf: [ - { anyOf: [{ type: "string" }], title: "X" }, - { type: "null" } - ] - } - // The inner anyOf is not flattened into the outer (sibling "title" prevents it), - // but the single-element inner anyOf is unwrapped within the entry itself - expect(flattenNestedAnyOf(input)).toStrictEqual({ - anyOf: [ - { type: "string", title: "X" }, - { type: "null" } - ] - }) - }) - - it("unwraps anyOf with single item after flattening", () => { - const input = { - anyOf: [ - { anyOf: [{ type: "string" }] } - ] - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ type: "string" }) - }) - - it("unwraps anyOf with single item, merging sibling properties", () => { - const input = { - title: "MyField", - anyOf: [{ type: "string" }] - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ - title: "MyField", - type: "string" - }) - }) - - it("recurses into nested objects", () => { - const input = { - properties: { - field: { - anyOf: [ - { anyOf: [{ $ref: "#/defs/A" }, { $ref: "#/defs/B" }] }, - { type: "null" } - ] - } - } - } - expect(flattenNestedAnyOf(input)).toStrictEqual({ - properties: { - field: { - anyOf: [ - { $ref: "#/defs/A" }, - { $ref: "#/defs/B" }, - { type: "null" } - ] - } - } - }) - }) - - it("passes through non-objects unchanged", () => { - expect(flattenNestedAnyOf(null)).toBe(null) - expect(flattenNestedAnyOf(42)).toBe(42) - expect(flattenNestedAnyOf("hello")).toBe("hello") - }) -}) diff --git a/packages/effect-app/test/utils.test.ts b/packages/effect-app/test/utils.test.ts deleted file mode 100644 index 2fe525b99..000000000 --- a/packages/effect-app/test/utils.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import * as S from "effect-app/Schema" -import { test } from "vitest" -import { copyOrigin } from "../src/utils.js" - -test("works with class", () => { - class Banana { - name: string - state: { a: string; _tag: "a" } | { b: number; _tag: "b" } - - constructor(params: { name: string; state: { a: string; _tag: "a" } | { b: number; _tag: "b" } }) { - this.name = params.name - this.state = params.state - } - } - - const copyBanana = copyOrigin(Banana) - - const original = new Banana({ name: "banana", state: { a: "a", _tag: "a" } }) - - const res1 = copyBanana( - original, - (_) => ({ state: { b: 1, _tag: "b" } }) - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res1).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res1).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res1)).toEqual(Banana.prototype) - - const res2 = copyBanana( - original, - { state: { b: 1, _tag: "b" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res2).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res2).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res2)).toEqual(Banana.prototype) - - const res3 = copyBanana( - res2, - { state: { a: "a", _tag: "a" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res3).toEqualTypeOf<{ - name: string - state: { - a: string - _tag: "a" - } - }>() - - expect(res3).toEqual({ - name: "banana", - state: { a: "a", _tag: "a" } - }) - - expect(Object.getPrototypeOf(res3)).toEqual(Banana.prototype) - - copyBanana( - original, - // @ts-expect-error extraProp is not a valid property of Banana - { name: "string", extraProp: "whatever" } - ) - - copyBanana( - // @ts-expect-error extraProp is not a valid property of Banana - original, - { extraProp: "whatever" } - ) - - const res4 = copyBanana( - original, - (o) => o - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res4).toEqualTypeOf<{ - name: string - state: { - a: string - _tag: "a" - } | { - b: number - _tag: "b" - } - }>() -}) - -test("works with schema class", () => { - class Banana extends S.Class("Banana")({ - name: S.String, - state: S.Union([ - S.Struct({ a: S.String, _tag: S.tag("a") }), - S.Struct({ b: S.Finite, _tag: S.tag("b") }) - ]) - }) {} - - const copyBanana = copyOrigin(Banana) - - const original = new Banana({ name: "banana", state: { a: "a", _tag: "a" } }) - - const res1 = copyBanana( - original, - (_) => ({ state: { b: 1, _tag: "b" } }) - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res1).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res1).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res1)).toEqual(Banana.prototype) - - const res2 = copyBanana( - original, - { state: { b: 1, _tag: "b" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res2).toEqualTypeOf<{ - name: string - state: { - b: number - _tag: "b" - } - }>() - - expect(res2).toEqual({ - name: "banana", - state: { b: 1, _tag: "b" } - }) - - expect(Object.getPrototypeOf(res2)).toEqual(Banana.prototype) - - const res3 = copyBanana( - res2, - { state: { a: "a", _tag: "a" } } - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res3).toEqualTypeOf<{ - name: string - state: { - a: string - _tag: "a" - } - }>() - - expect(res3).toEqual({ - name: "banana", - state: { a: "a", _tag: "a" } - }) - - expect(Object.getPrototypeOf(res3)).toEqual(Banana.prototype) - - copyBanana( - original, - // @ts-expect-error extraProp is not a valid property of Banana - { name: "string", extraProp: "whatever" } - ) - - copyBanana( - // @ts-expect-error extraProp is not a valid property of Banana - original, - { extraProp: "whatever" } - ) - - const res4 = copyBanana( - original, - (o) => o - ) satisfies Banana // must be assignable to Banana - - expectTypeOf(res4).toEqualTypeOf<{ - name: string - state: { - readonly a: string - readonly _tag: "a" - } | { - readonly b: number - readonly _tag: "b" - } - }>() -}) diff --git a/packages/effect-app/tsconfig.base.json b/packages/effect-app/tsconfig.base.json deleted file mode 100644 index 832135d45..000000000 --- a/packages/effect-app/tsconfig.base.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "extends": "@tsconfig/strictest/tsconfig.json", - "compilerOptions": { - "module": "Node16", - "lib": [ - "ES2023" - ], - "target": "ES2022", - // inline source maps stay better in sync with the source, which should aid with watchers. - // "sourceMap": true, - "inlineSourceMap": true, - "incremental": true, - "composite": true, - "declarationMap": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "noImplicitAny": true, - "useUnknownInCatchVariables": true, - // This is not desirable, as it's useful in generators. - "noImplicitReturns": false, - "noImplicitThis": true, - "resolveJsonModule": true, - "moduleResolution": "Node16", - "noErrorTruncation": true, - "forceConsistentCasingInFileNames": true, - "rewriteRelativeImportExtensions": true, - "allowImportingTsExtensions": true - }, - "watchOptions": { - // Use native file system events for files and directories - // This actually detect changes in parent directories ... not great! - // however it seems that the response is very fast on the non-related projects figuring out their files are unchanged... - "watchFile": "useFsEvents", - "watchDirectory": "useFsEvents", - // // Poll files for updates more frequently - // // when they're updated a lot. - "fallbackPolling": "dynamicPriority", - "excludeDirectories": [ - "**/node_modules", - "**/dist", - "**/.build", - "**/.git", - "**/.data", - "**/.logs", - "**/.*" - ], - "excludeFiles": [ - "**/*.tmp", - "openapi.json", - "*.json" - ] - }, - "files": [], - "include": [], - "exclude": [ - "**/node_modules", - "**/build", - "**/dist", - "**/.*" - ] -} diff --git a/packages/effect-app/tsconfig.json b/packages/effect-app/tsconfig.json deleted file mode 100644 index 8565116ac..000000000 --- a/packages/effect-app/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": {}, - "extends": "../../tsconfig.base.json", - "references": [ - { - "path": "./tsconfig.src.json" - }, - { - "path": "./tsconfig.test.json" - } - ] -} diff --git a/packages/effect-app/tsconfig.src.json b/packages/effect-app/tsconfig.src.json deleted file mode 100644 index 872c35832..000000000 --- a/packages/effect-app/tsconfig.src.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": [ - "esnext" - ], - "tsBuildInfoFile": "./dist/.tsbuildinfo", - "esModuleInterop": true, - "rootDir": "./src", - // keep in here, cause madge can't detect it from extended tsconfig - "moduleResolution": "Node16", - "outDir": "./dist" - }, - "include": [ - "./src/**/*.ts" - ], - "exclude": [ - "./dist", - "*.test.ts", - "node_modules", - "build", - "lib", - "dist", - "**/*.d.ts.map", - ".git", - ".data", - "**/.*", - "**/*.tmp" - ], - "references": [] -} diff --git a/packages/effect-app/tsconfig.test.json b/packages/effect-app/tsconfig.test.json deleted file mode 100644 index c071d42d0..000000000 --- a/packages/effect-app/tsconfig.test.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "extends": "./tsconfig.src.json", - "compilerOptions": { - "rootDir": "./test", - "outDir": "./test/dist", - "tsBuildInfoFile": "./test/dist/.tsbuildinfo", - "types": [ - "node", - "vitest/globals" - ] - }, - "include": [ - "./test/**/*.ts" - ], - "exclude": [ - "./dist", - "./test/dist", - "node_modules", - "build", - "lib", - "dist", - "**/*.d.ts.map", - ".git", - ".data", - "**/.*", - "**/*.tmp" - ], - "references": [ - { - "path": "./tsconfig.src.json" - } - ] -} diff --git a/packages/effect-app/vitest.config.ts b/packages/effect-app/vitest.config.ts deleted file mode 100644 index 85903fa64..000000000 --- a/packages/effect-app/vitest.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -import { defineConfig } from "vitest/config" -import makeConfig from "../../vite.config.base" - -export default defineConfig(makeConfig(__dirname)) diff --git a/repos/effect/packages/effect/ARBITRARY.md b/packages/effect/ARBITRARY.md similarity index 100% rename from repos/effect/packages/effect/ARBITRARY.md rename to packages/effect/ARBITRARY.md diff --git a/repos/effect/packages/effect/CHANGELOG.md b/packages/effect/CHANGELOG.md similarity index 93% rename from repos/effect/packages/effect/CHANGELOG.md rename to packages/effect/CHANGELOG.md index 8a345116a..fb6f29ad0 100644 --- a/repos/effect/packages/effect/CHANGELOG.md +++ b/packages/effect/CHANGELOG.md @@ -1,5 +1,257 @@ # effect +## 4.0.5 + +### Patch Changes + +- [#9055](https://github.com/Effect-TS/effect/pull/9055) [`f665607`](https://github.com/Effect-TS/effect/commit/f665607ee81bee248af7c8129a75692a7ad464e1) Thanks @Tyagiquamar! - Pass a `NonEmptyArray` to the `onNonEmpty` callback of `Array.match` when the input is a mutable array. + +- [#9110](https://github.com/Effect-TS/effect/pull/9110) [`dbf442b`](https://github.com/Effect-TS/effect/commit/dbf442b914b0ab755b42fabb8d29675508dff8d2) Thanks @petar-omni! - Fix `AsyncResult.all` failures carrying a single input's `previousSuccess` instead of the combined record or tuple. + +- [#9105](https://github.com/Effect-TS/effect/pull/9105) [`74d1930`](https://github.com/Effect-TS/effect/commit/74d19304da077d98cc6dcb294c71e253365f272f) Thanks @tim-smart! - Fix infinite atom rebuilds caused by late reads of uninitialized dependencies. + +- [#9145](https://github.com/Effect-TS/effect/pull/9145) [`a03afb6`](https://github.com/Effect-TS/effect/commit/a03afb6147651b8813f6f6d7218e1e7e2c27d93c) Thanks @jayhemnani9910! - Add `Flag.withOptionalValue` for GNU-style flags with an optional value, like `--color[=WHEN]`. A bare flag uses the given value; an explicit value must be attached with `=`. + +- [#9131](https://github.com/Effect-TS/effect/pull/9131) [`f159284`](https://github.com/Effect-TS/effect/commit/f1592843755ec2c41c5b65a0dbe0eb38c96dba82) Thanks @tim-smart! - Fix lost deferred callback wakes in cluster workflows when an activity suspends while a parallel activity is still running. + +- [#9094](https://github.com/Effect-TS/effect/pull/9094) [`5d301cc`](https://github.com/Effect-TS/effect/commit/5d301cc7aaf23e40cc8b59feea9389f747048d02) Thanks @okxint! - Name cluster entity RPC server spans by entity type only (e.g. `WorkflowEntity.Run`) so span names stay low-cardinality. The entity ID is still recorded in the `entity.id` span attribute. + +- [#9176](https://github.com/Effect-TS/effect/pull/9176) [`cd74ea2`](https://github.com/Effect-TS/effect/commit/cd74ea211980de0989ec45f0632cbb0bf07e96ba) Thanks @effect-bot! - Recover persisted local cluster replies from storage to prevent requests hanging when replies arrive before handler registration. Retain local reply notifications as storage-reader wakeups for low-latency streaming. + + Defer terminal in-memory reply publication until the successful outer transaction completes, preserving the commit boundary for reply readers. + +- [#9095](https://github.com/Effect-TS/effect/pull/9095) [`925cf64`](https://github.com/Effect-TS/effect/commit/925cf641c7070140541aef0d6a7fe6b545f9da23) Thanks @jayhemnani9910! - Fix `ConfigProvider.fromDotEnv` and `fromDotEnvContents` rescanning expanded values when `expandVariables` is enabled. Self and circular references no longer overflow the stack, escaped `\$NAME` stays literal, and defaults apply when a reference expands to an empty value. + +- [#9109](https://github.com/Effect-TS/effect/pull/9109) [`54c51d9`](https://github.com/Effect-TS/effect/commit/54c51d9e96134fe20394336fa7f6fd980b299741) Thanks @devangpratap! - Fix `Duration` conversions throwing for very large millisecond values, and make those durations equal to their nanosecond equivalents. + +- [#9161](https://github.com/Effect-TS/effect/pull/9161) [`995c104`](https://github.com/Effect-TS/effect/commit/995c104f7bfd9fb298d6f921fa21b94fbe53b227) Thanks @devangpratap! - Fix `Duration` conversions losing nanosecond precision for large integer millisecond values. + +- [#9112](https://github.com/Effect-TS/effect/pull/9112) [`816575e`](https://github.com/Effect-TS/effect/commit/816575e11cb8c6d9bc9101f20c16a512b88829d0) Thanks @effect-bot! - Use `bigint` for 64-bit `FileSystem.File.Info` metadata. `dev` is now a `bigint`, and `ino`, `rdev`, `nlink`, and `blocks` are `Option`, so file stats no longer fail or drop values outside the safe integer range. Node and Bun read `dev`, `ino`, `rdev`, and `nlink` as unsigned 64-bit integers. Deno reports these fields as numbers, so values above `Number.MAX_SAFE_INTEGER` are rounded by Deno before conversion. + +- [#9150](https://github.com/Effect-TS/effect/pull/9150) [`0df6e06`](https://github.com/Effect-TS/effect/commit/0df6e068b6df2093cefbb1d7ef208d762c5f5b1e) Thanks @tim-smart! - Support per-request URLs in `GraphQLProtocol.layerHttp` and `makeHttp`, as a function of the context and operation or as an `Effect`. `GraphQLResolver` batches lookups by URL. + +- [#9137](https://github.com/Effect-TS/effect/pull/9137) [`52a7d3c`](https://github.com/Effect-TS/effect/commit/52a7d3c8d3ec78caff07e85e7eb88e7c2d67755d) Thanks @tim-smart! - Add HttpClientRequest.compressBody for gzip and deflate request body compression. + +- [#9160](https://github.com/Effect-TS/effect/pull/9160) [`ec831c3`](https://github.com/Effect-TS/effect/commit/ec831c3795b060ca6644cabdc0a8f62365f67af7) Thanks @fubhy! - Accept `Redacted` values as URL parameters in `UrlParams` and `HttpClientRequest`. + +- [#9138](https://github.com/Effect-TS/effect/pull/9138) [`3abc63e`](https://github.com/Effect-TS/effect/commit/3abc63eae45675ad90e52026695366ea0e548c80) Thanks @tim-smart! - Add `HttpMiddleware.decompression`, which decompresses `gzip` and `deflate` request bodies, limits the decompressed size, and rejects other codings with `415`. + +- [#9113](https://github.com/Effect-TS/effect/pull/9113) [`a841b8b`](https://github.com/Effect-TS/effect/commit/a841b8b144607d3dadddd08c5e6807526d24f584) Thanks @effect-bot! - Fix interruption calling `Math.random` and hashing defects, which made deterministic hosts that forbid randomness loop forever or overflow the stack. + +- [#9098](https://github.com/Effect-TS/effect/pull/9098) [`91efea7`](https://github.com/Effect-TS/effect/commit/91efea7860bbb42bd53cd76059538c44fada3217) Thanks @armancharan! - Narrow only the matched prefix when a `Match` tuple pattern is applied to an array or variadic tuple, so the remaining elements keep their original type. + +- [#9151](https://github.com/Effect-TS/effect/pull/9151) [`e271997`](https://github.com/Effect-TS/effect/commit/e271997a14a6ae5201c70217f86537818abbe664) Thanks @tim-smart! - Fix `McpServer` types to require schema services for resource templates and encoding services for prompt parameters with completion handlers. Missing services are now caught at compile time. + +- [#9117](https://github.com/Effect-TS/effect/pull/9117) [`7719bda`](https://github.com/Effect-TS/effect/commit/7719bda8a5bddd561c5332984b827f02f610be3f) Thanks @jayhemnani9910! - Pad 64-bit trace ids to 128 bits in OTLP exports and `traceparent` headers. + +- [#9139](https://github.com/Effect-TS/effect/pull/9139) [`563397f`](https://github.com/Effect-TS/effect/commit/563397fbb8484f41eca5ee3e9fee01596a1c6220) Thanks @tim-smart! - Add opt-in gzip compression to built-in OTLP JSON and protobuf exporters via the compression option or OTEL_EXPORTER_OTLP_COMPRESSION and per-signal environment variables. Compress batches once before retries using HttpClientRequest.compressBody, retaining its default 1KB threshold and uncompressed defaults. + +- [#9127](https://github.com/Effect-TS/effect/pull/9127) [`65b04f8`](https://github.com/Effect-TS/effect/commit/65b04f87b7f0179e6402a3d0e51f039897fdad9b) Thanks @effect-bot! - Add `Path.resolveWithin(root, input)`, which resolves a relative path beneath a root and returns `Option.none()` for traversal escapes and rooted inputs, using the platform's path rules. + +- [#9143](https://github.com/Effect-TS/effect/pull/9143) [`c2859bf`](https://github.com/Effect-TS/effect/commit/c2859bf0176a671154e578653dfb3ba2578a79ff) Thanks @danieljvdm! - Collect tool approvals once per `LanguageModel` request instead of twice. + +- [#9134](https://github.com/Effect-TS/effect/pull/9134) [`c462214`](https://github.com/Effect-TS/effect/commit/c462214483775e57f32cd4854346f49328043ed2) Thanks @effect-bot! - Reduce SQLite statement overhead by borrowing the shared connection while preserving semaphore exclusion and failed-COMMIT recovery. + +- [#9169](https://github.com/Effect-TS/effect/pull/9169) [`d1ad2c7`](https://github.com/Effect-TS/effect/commit/d1ad2c71a0dace15d14bc6f158ea8de2ccbed866) Thanks @danieljvdm! - Cache the last response decoder for each language model response kind, keyed by validation mode and ordered tool schema identities. + +- [#9136](https://github.com/Effect-TS/effect/pull/9136) [`12be65e`](https://github.com/Effect-TS/effect/commit/12be65e5e3a0b2f3231d9ae47665727851d9b7b1) Thanks @tim-smart! - Redact sensitive query values in HTTP spans, request inspection and error messages. Also redact URL credentials in client spans, request inspection and errors. Request URLs sent over the wire are unchanged. + + Move `AWSAccessKeyId`, `Signature`, `sig` and `X-Goog-Signature` from `Headers.CurrentRedactedNames` to the new `UrlParams.CurrentRedactedNames`. A custom list replaces these defaults. Add `UrlParams.isRedactedName`, `UrlParams.redact` and `UrlParams.redactUrl` for explicit redaction. + +- [#9088](https://github.com/Effect-TS/effect/pull/9088) [`352618a`](https://github.com/Effect-TS/effect/commit/352618a562d6f5693e6255a3dcc0c15929c01d88) Thanks @armancharan! - `Match.when`, `Match.whenOr`, `Match.whenAnd`, and `Match.not` now reject object patterns with keys the input type does not have. + +- [#9166](https://github.com/Effect-TS/effect/pull/9166) [`2a8ec8b`](https://github.com/Effect-TS/effect/commit/2a8ec8be832a91292a4c378ac0f94b8dfd547f69) Thanks @danieljvdm! - Keep `Schema` decoding synchronous when a decoding default is an already-successful effect, such as `Effect.succeed(...)`. + +- [#9135](https://github.com/Effect-TS/effect/pull/9135) [`8dec89d`](https://github.com/Effect-TS/effect/commit/8dec89d318297fb27916330ea01b8ff17c1faacb) Thanks @tim-smart! - Preserve completion-handler service requirements in `McpServer.registerResource` and `McpServer.resource` URI templates when handlers take a context argument. + +- [#9118](https://github.com/Effect-TS/effect/pull/9118) [`f05642f`](https://github.com/Effect-TS/effect/commit/f05642f77734e192c6a382e5cfe225cf48a19c97) Thanks @jayhemnani9910! - Fix handler-local `request.url` for parameterized and percent-encoded `HttpRouter` prefixes, respecting duplicate-slash configuration. + +- [#9187](https://github.com/Effect-TS/effect/pull/9187) [`3ad9a0c`](https://github.com/Effect-TS/effect/commit/3ad9a0c6334f579ee38650060f2690c4273dea87) Thanks @effect-bot! - Fix RPC socket clients closing healthy connections before sending a ping when a ping tick runs late. Slow requests now stay connected while the server responds to pings. + +- [#9103](https://github.com/Effect-TS/effect/pull/9103) [`dba4e52`](https://github.com/Effect-TS/effect/commit/dba4e52f505e10730623a7610413156d82fee6fe) Thanks @gcanti! - Reject duplicate discriminants at compile time in `Schema.toTaggedUnion`, including duplicates in nested unions and numeric/string property-key collisions such as `1` and `"1"`. + +- [#9096](https://github.com/Effect-TS/effect/pull/9096) [`7ca8224`](https://github.com/Effect-TS/effect/commit/7ca8224ebb87a603f7a3eaee7470537067fe2bc0) Thanks @jayhemnani9910! - Fix `Sse.encoder` dropping event data after a lone `\r`. CR and CRLF line endings in event data now start a new `data:` line, like LF. + +- [#9133](https://github.com/Effect-TS/effect/pull/9133) [`65d20dd`](https://github.com/Effect-TS/effect/commit/65d20dd6e85a4d1af2ef964625832ae38fa60d8d) Thanks @tim-smart! - Add filesystem watcher installation notifications and wait for all GraphQL watch subscriptions, including nested directories, before generating. This prevents edits during startup from being missed. + +- [#9106](https://github.com/Effect-TS/effect/pull/9106) [`e3ff963`](https://github.com/Effect-TS/effect/commit/e3ff963eb59eb6a71316b3581c35bcffaaa2c263) Thanks @armancharan! - Fix `Stream.timeout` and `Stream.timeoutOrElse` hanging when an async iterator's pending `next()` never settles. + +- [#9183](https://github.com/Effect-TS/effect/pull/9183) [`bc454a3`](https://github.com/Effect-TS/effect/commit/bc454a377dda3b92a24359c7b92810e47694d97a) Thanks @Marve10s! - Fix `String.snakeToCamel` and `String.snakeToPascal` throwing on a trailing underscore or an even-length run of underscores, such as `"foo_"` or `"__"`. A trailing underscore is now dropped and an even-length run of underscores returns `""`; inputs that converted before give the same result. + + `String.noCase` no longer calls a custom `transform` for input without words, such as `""` or `"---"`. + +- [#9124](https://github.com/Effect-TS/effect/pull/9124) [`793d89e`](https://github.com/Effect-TS/effect/commit/793d89efa9201730ba52282a6c53ab12ad50b59e) Thanks @jem-computer! - Support structured decision instructions, descriptions, and score legends in TypeSafe, Cloudflare, and OpenRouter. `Decision.rate` accepts labeled descriptions; `Rate.criteria` entries are now `{ label, description }` objects instead of strings. + +- [#9173](https://github.com/Effect-TS/effect/pull/9173) [`ec9332b`](https://github.com/Effect-TS/effect/commit/ec9332ba2b79a3539579e3241da7b053a5e1e11a) Thanks @effect-bot! - Remove interrupted sleeps from `TestClock` and insert new sleeps in order instead of re-sorting the queue. + +- [#9172](https://github.com/Effect-TS/effect/pull/9172) [`f2b3d8a`](https://github.com/Effect-TS/effect/commit/f2b3d8a573d9189d46cf8ab2c209512dabc89331) Thanks @effect-bot! - Fix `TestClock.adjust` and `TestClock.setTime` skipping sleeps that a woken fiber registers after more than one scheduler hop, such as after `Effect.yieldNow` or forking and joining a child. + +- [#8166](https://github.com/Effect-TS/effect/pull/8166) [`cb2cf5f`](https://github.com/Effect-TS/effect/commit/cb2cf5f19dd23cc65a5cd826844fe7b11e95f4f6) Thanks @KhraksMamtsov! - `Effect.Transaction` now stores attempt metadata, analogous to `Schedule.CurrentMetadata`. Yield it inside a transaction body to read `attempt`, `retryReason`, and the same timing fields as a schedule (`start`, `now`, `elapsed`, `elapsedSincePrevious`). + +- [#9092](https://github.com/Effect-TS/effect/pull/9092) [`b8c201d`](https://github.com/Effect-TS/effect/commit/b8c201d0f492f669bbdbdf5d6f1834abe25ed1fc) Thanks @effect-bot! - Add a `detached` option for discarded child workflows. Detached children neither wake their parent on completion nor get interrupted with it. + +- [#9091](https://github.com/Effect-TS/effect/pull/9091) [`118c9c4`](https://github.com/Effect-TS/effect/commit/118c9c4fe5fa5bac292bed88c01a7c10fe6ed2e6) Thanks @effect-bot! - Keep workflows suspended by `Workflow.SuspendOnFailure` from retrying failed activities until explicitly resumed. Interrupting a failure-suspended workflow now completes it as interrupted without re-running the failed activity. + +## 4.0.4 + +### Patch Changes + +- [#9030](https://github.com/Effect-TS/effect/pull/9030) [`7547a99`](https://github.com/Effect-TS/effect/commit/7547a99f81fa6f7e651d52f3d42081f399bfbca8) Thanks @effect-bot! - Bound sparse array indices in `SchemaGetter.makeTreeRecord`, used by `Schema.fromURLSearchParams` and `Schema.fromFormData`. An indexed container with more than 20 holes is now returned as an object keyed by its index strings instead of a sparse array, and appending after the maximum array index no longer throws. + +- [#9083](https://github.com/Effect-TS/effect/pull/9083) [`711719d`](https://github.com/Effect-TS/effect/commit/711719d28d78dddda4d1685b916d1edf1eae9e98) Thanks @effect-bot! - Bound import-time stack detection probes to the frames they inspect, restoring the caller's stack trace limit afterward. + +- [#9041](https://github.com/Effect-TS/effect/pull/9041) [`8f1ea52`](https://github.com/Effect-TS/effect/commit/8f1ea52f0c073daf445adb987d23860dd76a147c) Thanks @tim-smart! - Stop grouped RequestResolvers from retaining batch keys for the lifetime of the resolver. Equal keys are now matched by the pending batch map, so a key is dropped as soon as its batch stops pending, including after interruption. + +- [#9010](https://github.com/Effect-TS/effect/pull/9010) [`c93485e`](https://github.com/Effect-TS/effect/commit/c93485e365555763b8cf043d2e2a1399ac5bb236) Thanks @effect-bot! - Support an explicit client `servername` for TLS socket upgrades without changing hostname behavior when omitted. + +- [#8990](https://github.com/Effect-TS/effect/pull/8990) [`37e088f`](https://github.com/Effect-TS/effect/commit/37e088f2af8dc03271bb9e2ca2874b750487b0f5) Thanks @bastiankistner! - Use decoded response aliases in generated HTTP clients. Render bare numbers with a standard leading finite check as `Schema.Finite`, preserving annotations and other checks. + +- [#8998](https://github.com/Effect-TS/effect/pull/8998) [`9314666`](https://github.com/Effect-TS/effect/commit/93146666ec49cd6831e9c9d5fa0f9bc72730aaa7) Thanks @gchen0002! - Optimize `Context.pick` for zero or one service. + +- [#9048](https://github.com/Effect-TS/effect/pull/9048) [`a0a26b7`](https://github.com/Effect-TS/effect/commit/a0a26b789c5e865a2f2baf0b29b7b369b100d8be) Thanks @jayhemnani9910! - Fix zoned `DateTime.add` and `DateTime.subtract` to apply hours, minutes, seconds and milliseconds as elapsed time across DST transitions. + +- [#9008](https://github.com/Effect-TS/effect/pull/9008) [`cc843ea`](https://github.com/Effect-TS/effect/commit/cc843ea091e68a57d91f39fc51baff094c50e41d) Thanks @effect-bot! - Add `toolCallValidation: "deferred"` for callers using `disableToolCallResolution: true`. Model-generated tool calls return with `unknown` parameters, including invalid parameters and undeclared tool names, even without a toolkit. Supported by the Anthropic, OpenAI, and OpenAI-compatible providers. Strict validation remains the default; provider-executed calls remain validated. + +- [#8979](https://github.com/Effect-TS/effect/pull/8979) [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3) Thanks @tim-smart! - Add experimental `effect/graphql` with typed operations, middleware, HTTP and subscription transports, partial results, serializable errors and cursor pagination. + +- [#9052](https://github.com/Effect-TS/effect/pull/9052) [`a59b971`](https://github.com/Effect-TS/effect/commit/a59b9711aff7b1af8ab3838b23d0981fb53d7a9a) Thanks @Tarekkharsa! - Fix `HttpClient.withRateLimiter` reading a `Retry-After` HTTP date as a number of seconds. + +- [#9031](https://github.com/Effect-TS/effect/pull/9031) [`fccf660`](https://github.com/Effect-TS/effect/commit/fccf660d83f1db48087cc32da767c43d8e385f64) Thanks @effect-bot! - Add `McpServer.layerHttpRequestScoped` for serverless hosts such as Cloudflare Workers. Each POST is served by an MCP server owned by that request, so no fiber or timer outlives the request. The new layer accepts only sessionless protocol adapters such as `v2026_07_28`, with no subscription or session termination options. `McpServer.layerHttp` retains its long-lived server behavior. + +- [#9081](https://github.com/Effect-TS/effect/pull/9081) [`1bfd8fd`](https://github.com/Effect-TS/effect/commit/1bfd8fddebc978643b7a2077a837b82dc8998177) Thanks @hsyntax! - Tighten colliding bounds in place when exporting JSON Schema, instead of moving them into `allOf`. This applies to numeric, length, item-count and property-count bounds, and an inclusive or exclusive bound implied by the other on the same side is dropped. For example, `Schema.Natural` now exports `minimum: 0`, and `Schema.Int.check(Schema.isGreaterThan(0))` exports `exclusiveMinimum: 0` without the safe-integer `minimum`. + +- [#9013](https://github.com/Effect-TS/effect/pull/9013) [`78e4c43`](https://github.com/Effect-TS/effect/commit/78e4c435ede00c7757c3507e60feb821edc85b5f) Thanks @jayhemnani9910! - Fix `Multipart.toPersisted` failing for uploads named `.` or `..` and for long multi-byte file names. + +- [#8991](https://github.com/Effect-TS/effect/pull/8991) [`fde67a1`](https://github.com/Effect-TS/effect/commit/fde67a10e9245aa3974ecdca00239de8dfd2cc9f) Thanks @fubhy! - Add `NetAddress.loopbackAddresses`. + +- [#8992](https://github.com/Effect-TS/effect/pull/8992) [`118deef`](https://github.com/Effect-TS/effect/commit/118deefe7c02089b1bc5d202ce3225f05a98cd04) Thanks @fubhy! - Add `Host.toFullyQualified`, `Host.toRelative`, and `Host.parseHostsFile`. + +- [#9046](https://github.com/Effect-TS/effect/pull/9046) [`cea74c9`](https://github.com/Effect-TS/effect/commit/cea74c92ac839009c6ba112504689d8726f648d7) Thanks @effect-bot! - Persist outgoing cluster messages while the locally assigned shard is waiting for its lock. + +- [#9056](https://github.com/Effect-TS/effect/pull/9056) [`b7c1ac6`](https://github.com/Effect-TS/effect/commit/b7c1ac683775f37c78061da3304d37a800d227d6) Thanks @tim-smart! - `Prompt.fromResponseParts` now keeps streamed text and reasoning in stream order, and keeps partial text from unended text blocks. + +- [#8997](https://github.com/Effect-TS/effect/pull/8997) [`f837966`](https://github.com/Effect-TS/effect/commit/f8379667c914cc5bbc5acfd41eee0090ee247925) Thanks @effect-bot! - Release the durable waits of losing `DurableDeferred.raceAll` branches once the race settles, so a late completion such as a losing `DurableClock.sleep` no longer preempts and replays the continuing workflow + +- [#9053](https://github.com/Effect-TS/effect/pull/9053) [`09738f4`](https://github.com/Effect-TS/effect/commit/09738f4aa61f4e188d0b1db76b405618f0e31d94) Thanks @Tarekkharsa! - Honor `Retry-After` in `HttpClient.retryTransient`. + +- [#9019](https://github.com/Effect-TS/effect/pull/9019) [`22e7129`](https://github.com/Effect-TS/effect/commit/22e7129023f15aea70ec3bb5ab92c93aad605b4e) Thanks @effect-bot! - Allow configuring Scalar's preferred authentication security scheme through HttpApiScalar without a TypeScript cast. + +- [#9069](https://github.com/Effect-TS/effect/pull/9069) [`fdcb47e`](https://github.com/Effect-TS/effect/commit/fdcb47e64304b52299bad350aa57722fa9e3c013) Thanks @effect-bot! - Fix cluster Sharding startup accessing storage-loop state before initialization, preventing a scheduler-dependent ReferenceError and delayed initial storage reads. + +- [#9049](https://github.com/Effect-TS/effect/pull/9049) [`fc349a1`](https://github.com/Effect-TS/effect/commit/fc349a13f77c5cc4c94752546913b90be8d0bfaa) Thanks @jayhemnani9910! - Fix `Sink.takeUntil` and `Sink.takeUntilEffect` dropping the element after a match. They now stop at the match and return the remaining input as leftovers. + +- [#8979](https://github.com/Effect-TS/effect/pull/8979) [`ab90be9`](https://github.com/Effect-TS/effect/commit/ab90be98ea693df4c0d647cf29a4b913ecbef8f3) Thanks @tim-smart! - Add `dispatchEmptyNamedEvents` to `Sse` decode options. It also dispatches an event that sets an `event:` name but has no `data:` line, which protocols such as graphql-sse use for `complete`. + +- [#9065](https://github.com/Effect-TS/effect/pull/9065) [`892d14d`](https://github.com/Effect-TS/effect/commit/892d14d88b84f842457494792bf702eb15d5b491) Thanks @Abdullah1738! - Fix quadratic parsing time in `Sse.makeParser` and `Sse.decode` for long lines split across many chunks. + +- [#9066](https://github.com/Effect-TS/effect/pull/9066) [`077c557`](https://github.com/Effect-TS/effect/commit/077c55770db747f04213af82bb4651376ea04447) Thanks @effect-bot! - Complete HTTP server spans synchronously before the request fiber exits, preventing completed spans from being lost during immediate runtime shutdown. + +- [#9029](https://github.com/Effect-TS/effect/pull/9029) [`4a999a3`](https://github.com/Effect-TS/effect/commit/4a999a34e81ef754b959f6ded75b272503bc1163) Thanks @effect-bot! - Clean up unused Anthropic bindings and make documentation-only imports type-only, preserving server-tool inputs and documentation links. + +- [#9062](https://github.com/Effect-TS/effect/pull/9062) [`797696a`](https://github.com/Effect-TS/effect/commit/797696a2b7b5b3c73e01b8d72d9a327e49a39708) Thanks @adam-r-kowalski! - Merge outer headers and cookies into a copy of a raw `Response` in `HttpServerResponse.toWeb`, leaving the original headers unchanged. This supports immutable headers and prevents cookies from accumulating across requests that reuse a response. Responses with nothing to merge are returned unchanged. + +- [#9007](https://github.com/Effect-TS/effect/pull/9007) [`cf5179b`](https://github.com/Effect-TS/effect/commit/cf5179b48867597342ccc9912252ac75637aec06) Thanks @effect-bot! - Wake a suspended `WorkflowEngine.execute` caller as soon as its workflow has a final result, instead of waiting for the next `suspendedRetrySchedule` step. A replay that suspends again does not spend a step of the schedule. With `ClusterWorkflowEngine`, only callers on the runner that owns the execution are woken early; other callers keep relying on `suspendedRetrySchedule`. + +- [#9037](https://github.com/Effect-TS/effect/pull/9037) [`e1c6053`](https://github.com/Effect-TS/effect/commit/e1c6053c5e813596a24793c90cbf732b63c98852) Thanks @effect-bot! - `HttpEffect.toWebHandler` now interrupts a streamed response body when the request is aborted after its headers were sent, even if the body is no longer being read, which releases the request scope. + +## 4.0.3 + +### Patch Changes + +- [#8977](https://github.com/Effect-TS/effect/pull/8977) [`b4e5af8`](https://github.com/Effect-TS/effect/commit/b4e5af825ac4c869d09c008b2d1b58cdd0ce4c02) Thanks @kitlangton! - Prevent crashes and premature interruption when `Effect.cached`, `Effect.cachedWithTTL`, and `Effect.cachedInvalidateWithTTL` are synchronously re-entered. + +- [#8982](https://github.com/Effect-TS/effect/pull/8982) [`b8d6e7a`](https://github.com/Effect-TS/effect/commit/b8d6e7a89b8c2283aebbfa154963efccd7addbd7) Thanks @Yi-111-a! - Export `CliOutput.displayWidth` to measure terminal-cell widths using the same logic as built-in CLI help and tables. + +- [#8933](https://github.com/Effect-TS/effect/pull/8933) [`89d5c88`](https://github.com/Effect-TS/effect/commit/89d5c8881024e31b08344bf9f992be0e5d024e4f) Thanks @tim-smart! - Respect `NO_COLOR` in CLI prompts and the command wizard, suppressing color and style escapes while preserving cursor and redraw controls. + +- [#8941](https://github.com/Effect-TS/effect/pull/8941) [`66257d2`](https://github.com/Effect-TS/effect/commit/66257d29224e949f7b300ff33416098519c7e86a) Thanks @Tanner-Scadden! - Hand cluster shards off one at a time when `Sharding` shuts down, after their entities and singletons have stopped, so live runners can take them over sooner. The handoff is best effort: its wait is bounded, and if it times out, `releaseAll` attempts to release the remaining locks. A runner that is shutting down no longer acquires new shards, and releasing a shard no longer waits forever on an entity whose id is also active on another shard. + +- [#8940](https://github.com/Effect-TS/effect/pull/8940) [`cd2ab19`](https://github.com/Effect-TS/effect/commit/cd2ab19600d00e1e9c84028f29dcb22978e8c681) Thanks @Tanner-Scadden! - Fix cluster shard handoffs. Interrupts and stream acknowledgements now reach requests still draining on the previous owner, and `RpcServer` accepts acknowledgements for active streams after a client's EOF. Volatile requests sent during a handoff wait, for at most `shardLockExpiration`, until the shard can be served instead of repeatedly failing with `EntityNotAssignedToRunner`. + +- [#8897](https://github.com/Effect-TS/effect/pull/8897) [`ca068ce`](https://github.com/Effect-TS/effect/commit/ca068cef445101cbb2710c24360ca3a7ab9b94d5) Thanks @effect-bot! - Add separate migration and storage layers to cluster SQL storage so applications can use tables migrated by an owner connection. + +- [#8895](https://github.com/Effect-TS/effect/pull/8895) [`f686000`](https://github.com/Effect-TS/effect/commit/f686000760c2d3ff27aea5767e73c1618c5d21c0) Thanks @tim-smart! - Serialize SchemaError as a compact object containing its tag and formatted message in JSON logs and Node inspection, without dumping schema AST nodes. + +- [#8968](https://github.com/Effect-TS/effect/pull/8968) [`e052f1b`](https://github.com/Effect-TS/effect/commit/e052f1b30b379f120854f9b2394efa7ac44fc01f) Thanks @sledorze! - Avoid a redundant map copy when `Context.add` rebases an overlay chain. + +- [#8487](https://github.com/Effect-TS/effect/pull/8487) [`c755b69`](https://github.com/Effect-TS/effect/commit/c755b69701e29e055ef5c4c87ced83e15b3faa81) Thanks @fubhy! - Add `TestCrypto.layer`, which provides deterministic random operations derived from a seed while preserving a platform crypto service's digest implementation. + +- [#8935](https://github.com/Effect-TS/effect/pull/8935) [`ebe487f`](https://github.com/Effect-TS/effect/commit/ebe487f88846a131faa0c61a5f65f80187782e52) Thanks @tim-smart! - Turn `Crypto.randomUUIDv4` and `Crypto.randomUUIDv7` into functions accepting an optional `{ format: "hex" | "bytes" }` option. Call them with `()` for the default lowercase, hyphenated UUID string, or with `{ format: "bytes" }` for a 16-byte `Uint8Array`. + +- [#8926](https://github.com/Effect-TS/effect/pull/8926) [`63bfad8`](https://github.com/Effect-TS/effect/commit/63bfad88fc60de2d8c21dc7f02a62f48a0b5281e) Thanks @effect-bot! - Fix `DurableDeferred.raceAll` and `DurableDeferred.into` so a workflow branch continues after its result is recorded instead of stalling until a concurrently parked sibling is woken. The effect passed to `DurableDeferred.into` now counts as in-flight work, so a parked sibling no longer suspends the run while it runs. + +- [#8921](https://github.com/Effect-TS/effect/pull/8921) [`8598157`](https://github.com/Effect-TS/effect/commit/8598157653e46fb12af342cb8bd6d30122afa4fa) Thanks @devangpratap! - Fix `Duration.fromInputUnsafe` throwing on infinite nanosecond totals. It now returns an infinite duration, and `Duration.fromInput` returns `Some` instead of `None`. + +- [#8972](https://github.com/Effect-TS/effect/pull/8972) [`76a03f7`](https://github.com/Effect-TS/effect/commit/76a03f759980de88cdcb7c906fca92a6212b760e) Thanks @devangpratap! - Fix `Duration.times` throwing when a nanosecond-backed duration is multiplied by a fractional or non-finite number. Fractional results round to the nearest nanosecond. + +- [#8900](https://github.com/Effect-TS/effect/pull/8900) [`182e26d`](https://github.com/Effect-TS/effect/commit/182e26dae1c8d16adf0372b6b106babb87d0f7b0) Thanks @effect-bot! - Add a `noFollow` option to `FileSystem.open` to reject symlinks at the final path component. Supported on Node and Bun on POSIX; Windows and Deno return `BadArgument`. Add `{ position }` to `File.read` and `File.readAlloc` for concurrent reads that leave the cursor unchanged. + +- [#8885](https://github.com/Effect-TS/effect/pull/8885) [`a46592a`](https://github.com/Effect-TS/effect/commit/a46592aed944b046e562815083b367ad28c8eaee) Thanks @effect-bot! - Preserve duplicate slashes in query values when FindMyWay normalizes request paths. + +- [#8986](https://github.com/Effect-TS/effect/pull/8986) [`1d3419e`](https://github.com/Effect-TS/effect/commit/1d3419e76ca8ad7c2504b9ec75b281ef3dff6b60) Thanks @jayhemnani9910! - Fix histogram exporters to retain all finite boundaries and overflow observations in OTLP, and emit a single `+Inf` bucket in Prometheus. + +- [#8932](https://github.com/Effect-TS/effect/pull/8932) [`e00eaae`](https://github.com/Effect-TS/effect/commit/e00eaae8d9d503bea1871031670c28f82935c303) Thanks @tim-smart! - Defer terminal input acquisition until a prompt needs user input, so constant fallback prompts leave piped stdin untouched. + +- [#8888](https://github.com/Effect-TS/effect/pull/8888) [`133ceb2`](https://github.com/Effect-TS/effect/commit/133ceb2b77331742f381c09552478a86975826fa) Thanks @RhysSullivan! - Return empty 200 text/event-stream responses for MCP HTTP request POSTs when cancellation withholds every reply. Return 500 for other request POSTs that end without a response. + +- [#8924](https://github.com/Effect-TS/effect/pull/8924) [`a494a64`](https://github.com/Effect-TS/effect/commit/a494a647885d8eacff4fce0ebde1263e8b261be6) Thanks @effect-bot! - Add `McpServer.layerHttp({ allowSubscriptions: false })` to disable `v2026_07_28` subscriptions while keeping request-scoped progress and log notifications. + +- [#8890](https://github.com/Effect-TS/effect/pull/8890) [`d6082a2`](https://github.com/Effect-TS/effect/commit/d6082a25a3779f7e2abba2787323d5d9c7e1d24a) Thanks @tim-smart! - Send MCP cancellation notifications for interrupted server-to-client requests instead of internal RPC control messages. + +- [#8925](https://github.com/Effect-TS/effect/pull/8925) [`ee3a350`](https://github.com/Effect-TS/effect/commit/ee3a35076854eb6a49183bf8d5ac610e46393d02) Thanks @effect-bot! - Prevent MCP subscriptions from receiving deferred list-change notifications emitted before they subscribed, while preserving coalesced runtime changes. + +- [#8955](https://github.com/Effect-TS/effect/pull/8955) [`7ba976c`](https://github.com/Effect-TS/effect/commit/7ba976c620d62125ecc7924be379308cf9b87cd7) Thanks @effect-bot! - Allow interrupted activities to run again when the in-memory workflow engine replays a preempted execution. + +- [#8973](https://github.com/Effect-TS/effect/pull/8973) [`9dbd07e`](https://github.com/Effect-TS/effect/commit/9dbd07e0063301b2361e04dd9b3697d5469ab85e) Thanks @fubhy! - Accept address inputs in `Dns.reverse` and name server inputs in the platform `Dns` services, and add `Dns.nameServerFromInput`. + +- [#8953](https://github.com/Effect-TS/effect/pull/8953) [`d4f7dc3`](https://github.com/Effect-TS/effect/commit/d4f7dc3300a04b45800bd5331bd8282ce1dd0ea0) Thanks @fubhy! - Add TLSA records to `effect/net/Dns`, with a `Schema.DnsTlsaRecord` schema whose JSON codec encodes the certificate association data as hex. The Node.js `Dns` service queries them with `resolveTlsa` (Node.js 22.15 or 23.9 and later); Bun, Deno, and older Node.js versions fail TLSA queries with `Unsupported`. + +- [#8880](https://github.com/Effect-TS/effect/pull/8880) [`a9b5148`](https://github.com/Effect-TS/effect/commit/a9b514822573c0e27452d80bcf35a6fa036d32fb) Thanks @fubhy! - Add experimental `Host`, `Dns`, and `AddressResolver` modules to `effect/net` for host parsing, DNS queries, and endpoint resolution, with Node, Bun, and Deno services. DNS operations accept and normalize domain-name strings; endpoint resolution also accepts `host:port` strings. Add scoped IPv6 literals and host and DNS record schemas, including JSON codecs for records. + + Provide the services explicitly; they are not included in the platform `*Services` layers. Use `NodeAddressResolver.layer.pipe(Layer.provideMerge(NodeDns.layer))` to provide both Node services. + +- [#8970](https://github.com/Effect-TS/effect/pull/8970) [`e7d84e4`](https://github.com/Effect-TS/effect/commit/e7d84e449a0e75d1d04486ea81dfb58531fd6503) Thanks @fubhy! - Add `fromInput` constructors to the `effect/net` data types. + +- [#8899](https://github.com/Effect-TS/effect/pull/8899) [`0e2b988`](https://github.com/Effect-TS/effect/commit/0e2b988eec37873bf37f056e2b1b1e52193c4a7a) Thanks @tim-smart! - Answer orphaned cluster workflow activity requests with Suspended instead of waiting indefinitely for replay registration. + +- [#8987](https://github.com/Effect-TS/effect/pull/8987) [`84f48df`](https://github.com/Effect-TS/effect/commit/84f48df586ef1be9290910e3f37b5bb2811b9545) Thanks @jayhemnani9910! - Omit `min` and `max` from subsequent delta histogram exports in `OtlpMetrics`: cumulative extrema do not describe the reported interval. + +- [#8907](https://github.com/Effect-TS/effect/pull/8907) [`48ed32f`](https://github.com/Effect-TS/effect/commit/48ed32f846c11ab702b22c84ed103c767d0dd21c) Thanks @tim-smart! - Add a `delay` option to `PersistedQueue.offer` that postpones an element's first delivery in the memory, Redis and SQL stores. + +- [#8904](https://github.com/Effect-TS/effect/pull/8904) [`59e58a6`](https://github.com/Effect-TS/effect/commit/59e58a6b9839b475e02f2f551dd4c1328b548911) Thanks @effect-bot! - Add PowerShell CLI completions with `--completions powershell` (alias `pwsh`). + +- [#8409](https://github.com/Effect-TS/effect/pull/8409) [`b9e7821`](https://github.com/Effect-TS/effect/commit/b9e7821be3efd61b5ac01f8ced0818e50ed7f0e5) Thanks @tim-smart! - Add `DatagramSocket` for UDP on Node, Bun, and Deno, and `NetAddress.inetAddressFromNativeUnsafe` for runtime-reported addresses. + +- [#8943](https://github.com/Effect-TS/effect/pull/8943) [`aa811e4`](https://github.com/Effect-TS/effect/commit/aa811e454b61033ec150204d959bbf09e44e2112) Thanks @Tanner-Scadden! - Report RPC response encoding failures to ErrorReporter and include the RPC tag in client defects. + +- [#8958](https://github.com/Effect-TS/effect/pull/8958) [`bfc5742`](https://github.com/Effect-TS/effect/commit/bfc5742e18945026e4360e928e311042d8e47ab7) Thanks @tim-smart! - End the RPC server span with the encoding defect when a response fails to encode, instead of recording it as a client interruption. + +- [#8976](https://github.com/Effect-TS/effect/pull/8976) [`f9f7a5a`](https://github.com/Effect-TS/effect/commit/f9f7a5a16e1f7685b6653a71cd0265ad027c69df) Thanks @juliusmarminge! - Add `onPingTimeout` to `RpcClient.makeProtocolSocket` and `RpcClient.layerProtocolSocket` to distinguish ping timeouts from other socket failures. It runs only when a ping timeout drops an open connection, before `onDisconnect` and in-flight call failures; defects are logged and ignored. + +- [#8956](https://github.com/Effect-TS/effect/pull/8956) [`73fa287`](https://github.com/Effect-TS/effect/commit/73fa2873cb1269874eec8998d2f5ce9d44b0bb0b) Thanks @schickling-assistant! - Export `Schema.isInt` and `Schema.Int` as exact JSON Schema integers bounded by `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`. Composed bounds retain the tighter constraint, and exact integer branches preserve `oneOf`. + +- [#8937](https://github.com/Effect-TS/effect/pull/8937) [`7ff5048`](https://github.com/Effect-TS/effect/commit/7ff5048db51daba7c31ee90d5e3d17861a9e6b50) Thanks @effect-bot! - Skip startup stack probing when `Error.stackTraceLimit` is zero, avoiding unnecessary stack formatting during module loading. + +- [#8927](https://github.com/Effect-TS/effect/pull/8927) [`7803e51`](https://github.com/Effect-TS/effect/commit/7803e51e940727382c8af049f4744955fa21f53a) Thanks @effect-bot! - Hold the shared connection for the whole statement or stream on single-connection SQL clients, so another fiber's transaction can no longer roll back an acknowledged write or expose uncommitted rows to a plain read. + +- [#8938](https://github.com/Effect-TS/effect/pull/8938) [`93410da`](https://github.com/Effect-TS/effect/commit/93410da528b46607caf006f8c8d37dc6cee94530) Thanks @effect-bot! - Add `Stdio.stderrIsTerminal` with an overridable false default and runtime detection for Node.js, Bun, and Deno. + +- [#8963](https://github.com/Effect-TS/effect/pull/8963) [`aef1d1f`](https://github.com/Effect-TS/effect/commit/aef1d1f2e59cdfd85766a2b168bb3ec1b64538a1) Thanks @tim-smart! - Add `ClusterSchema.InterruptOnTermination` to interrupt opted-in RPCs as soon as entity termination starts. Persisted requests resume under the next owner without saving an interrupt reply, and server-side `Uninterruptible` takes precedence. + +- [#8917](https://github.com/Effect-TS/effect/pull/8917) [`d6d515b`](https://github.com/Effect-TS/effect/commit/d6d515bafc83e02bee055231b52fdce2ea24d88d) Thanks @Brendonovich! - Make `HttpClient.withRateLimiter` wait for the reported reset once a response's remaining budget is exhausted, including when a limit header is also present. + +- [#8892](https://github.com/Effect-TS/effect/pull/8892) [`ea52c24`](https://github.com/Effect-TS/effect/commit/ea52c245f64b378628ca290bf99bdb6cd033809c) Thanks @effect-bot! - Propagate SQL transaction COMMIT errors as typed SqlError failures when cleanup succeeds. Preserve both COMMIT and cleanup errors as defects when cleanup fails, so typed recovery cannot hide an unsafe connection state. + +- [#8913](https://github.com/Effect-TS/effect/pull/8913) [`7e44402`](https://github.com/Effect-TS/effect/commit/7e444029d3101640ea2edbffafa882097b78bb68) Thanks @grzegorz-naileditgames! - Reject B3 single-header and multi-header trace contexts with invalid trace or span identifiers. + +- [#8978](https://github.com/Effect-TS/effect/pull/8978) [`0b17b9e`](https://github.com/Effect-TS/effect/commit/0b17b9e2e43c2f95726470aee188fd9e2bf1baf6) Thanks @tim-smart! - Allow WebSocket clients on Node and Bun to send handshake headers alongside subprotocols through socket constructors, channels, and layers. Preserve browser-compatible constructor calls when headers are not supplied. + +- [#8891](https://github.com/Effect-TS/effect/pull/8891) [`9753be9`](https://github.com/Effect-TS/effect/commit/9753be9ecf8dfa9c58169f41a4b876d638b9cc08) Thanks @tim-smart! - Deliver terminal replies for persisted `ClusterSchema.WithTransaction` requests only after commit or clean rollback. On transaction failure, recover a committed reply or retry the request. + +- [#8886](https://github.com/Effect-TS/effect/pull/8886) [`977096b`](https://github.com/Effect-TS/effect/commit/977096bcea55317ef0e845218c818430cc6e5318) Thanks @effect-bot! - Fix lost failure replies for persisted cluster RPCs using `ClusterSchema.WithTransaction`. Save typed failures and non-fatal defects after rollback so retries receive the stored reply. Successful replies still commit with the handler's writes. + +- [#8882](https://github.com/Effect-TS/effect/pull/8882) [`cf58cb0`](https://github.com/Effect-TS/effect/commit/cf58cb0362972b4b46b762d8c9062ec7349b438e) Thanks @devangpratap! - Fix generated Zsh completions for flag descriptions containing `]`. + ## 4.0.2 ### Patch Changes diff --git a/repos/effect/packages/effect/CONFIG.md b/packages/effect/CONFIG.md similarity index 100% rename from repos/effect/packages/effect/CONFIG.md rename to packages/effect/CONFIG.md diff --git a/repos/effect/packages/effect/HTTPAPI.md b/packages/effect/HTTPAPI.md similarity index 100% rename from repos/effect/packages/effect/HTTPAPI.md rename to packages/effect/HTTPAPI.md diff --git a/repos/effect/packages/effect/LICENSE b/packages/effect/LICENSE similarity index 100% rename from repos/effect/packages/effect/LICENSE rename to packages/effect/LICENSE diff --git a/repos/effect/packages/effect/MCP.md b/packages/effect/MCP.md similarity index 100% rename from repos/effect/packages/effect/MCP.md rename to packages/effect/MCP.md diff --git a/repos/effect/packages/effect/OPTIC.md b/packages/effect/OPTIC.md similarity index 100% rename from repos/effect/packages/effect/OPTIC.md rename to packages/effect/OPTIC.md diff --git a/repos/effect/packages/effect/README.md b/packages/effect/README.md similarity index 100% rename from repos/effect/packages/effect/README.md rename to packages/effect/README.md diff --git a/repos/effect/packages/effect/SCHEMA.md b/packages/effect/SCHEMA.md similarity index 99% rename from repos/effect/packages/effect/SCHEMA.md rename to packages/effect/SCHEMA.md index 5168151ca..ac4a29956 100644 --- a/repos/effect/packages/effect/SCHEMA.md +++ b/packages/effect/SCHEMA.md @@ -1049,6 +1049,20 @@ An index signature lets a struct accept any string key in addition to its fixed Filters applied to either the struct or the record are preserved when combined. +The base schema must have an object-shaped encoded type. Whole-object transformations are not supported, even when the rest array is empty. For JSON-string encoding, wrap the completed schema with `Schema.fromJsonString` rather than passing a JSON-string schema to `Schema.StructWithRest`: + +```ts +import { Schema } from "effect" + +const base = Schema.Record(Schema.Literals(["bash", "sh"]), Schema.NonEmptyString) +const records = [Schema.Record(Schema.NonEmptyString, Schema.NonEmptyString)] as const +const schema = Schema.fromJsonString(Schema.StructWithRest(base, records), { space: 2 }) + +type Encoded = typeof schema.Encoded // string +``` + +Object-shaped encoded types alone do not guarantee compatibility: unsupported whole-object encodings are still rejected at runtime. Transformations on individual fields remain supported. + **Example** (Combining fixed properties with an index signature) ```ts @@ -2160,7 +2174,7 @@ This helper has some advantages over a dedicated constructor: - You can choose among multiple possible tag fields if present. - It supports unions that include nested unions. -Each member must have a unique discriminant property key. `toTaggedUnion` throws when it encounters a duplicate. Numeric and string values that resolve to the same property key, such as `1` and `"1"`, are considered duplicates. +Each member must have a unique discriminant property key. `toTaggedUnion` rejects overlapping discriminant types in known member tuples at compile time, including nested unions. It also throws when it encounters a duplicate at runtime. Numeric and string values that resolve to the same property key, such as `1` and `"1"`, are considered duplicates. **Note**. If the tag is the standard `_tag` field, you can use `Schema.TaggedUnion` instead. @@ -4393,6 +4407,8 @@ console.log(A.ast.annotations?.title) Use `extend` to create a subclass that adds fields to the base schema. Instance fields declared on the base class are also available on the subclass. +Fields with the same name replace inherited fields. Use this to refine validation or change encoding. Effect does not check compatibility; the caller must ensure the replacement preserves the base class contract. + **Example** (Extending a class with new fields) ```ts diff --git a/repos/effect/packages/effect/benchmark/Pool.ts b/packages/effect/benchmark/Pool.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/Pool.ts rename to packages/effect/benchmark/Pool.ts diff --git a/repos/effect/packages/effect/benchmark/http/multipart.ts b/packages/effect/benchmark/http/multipart.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/http/multipart.ts rename to packages/effect/benchmark/http/multipart.ts diff --git a/repos/effect/packages/effect/benchmark/http/serverAllocations.ts b/packages/effect/benchmark/http/serverAllocations.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/http/serverAllocations.ts rename to packages/effect/benchmark/http/serverAllocations.ts diff --git a/repos/effect/packages/effect/benchmark/http/tracer.ts b/packages/effect/benchmark/http/tracer.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/http/tracer.ts rename to packages/effect/benchmark/http/tracer.ts diff --git a/repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts b/packages/effect/benchmark/httpapi/sseClientDecoder.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/httpapi/sseClientDecoder.ts rename to packages/effect/benchmark/httpapi/sseClientDecoder.ts diff --git a/repos/effect/packages/effect/benchmark/net/NetAddress.ts b/packages/effect/benchmark/net/NetAddress.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/net/NetAddress.ts rename to packages/effect/benchmark/net/NetAddress.ts diff --git a/repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts b/packages/effect/benchmark/rpc/RpcSerialization.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/rpc/RpcSerialization.ts rename to packages/effect/benchmark/rpc/RpcSerialization.ts diff --git a/repos/effect/packages/effect/benchmark/schema/Optic.ts b/packages/effect/benchmark/schema/Optic.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/Optic.ts rename to packages/effect/benchmark/schema/Optic.ts diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaBinary.md b/packages/effect/benchmark/schema/SchemaBinary.md similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/SchemaBinary.md rename to packages/effect/benchmark/schema/SchemaBinary.md diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts b/packages/effect/benchmark/schema/SchemaBinary.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/SchemaBinary.ts rename to packages/effect/benchmark/schema/SchemaBinary.ts diff --git a/repos/effect/packages/effect/benchmark/schema/SchemaError.ts b/packages/effect/benchmark/schema/SchemaError.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/schema/SchemaError.ts rename to packages/effect/benchmark/schema/SchemaError.ts diff --git a/repos/effect/packages/effect/benchmark/stream/splitLines.ts b/packages/effect/benchmark/stream/splitLines.ts similarity index 100% rename from repos/effect/packages/effect/benchmark/stream/splitLines.ts rename to packages/effect/benchmark/stream/splitLines.ts diff --git a/repos/effect/packages/effect/package.json b/packages/effect/package.json similarity index 94% rename from repos/effect/packages/effect/package.json rename to packages/effect/package.json index 48e82a448..da512a378 100644 --- a/repos/effect/packages/effect/package.json +++ b/packages/effect/package.json @@ -1,7 +1,7 @@ { "name": "effect", "type": "module", - "version": "4.0.2", + "version": "4.0.5", "license": "MIT", "description": "The missing standard library for TypeScript, for writing production-grade software.", "homepage": "https://effect.website", @@ -46,6 +46,7 @@ "./persistence": "./src/persistence/index.ts", "./process": "./src/process/index.ts", "./reactivity": "./src/reactivity/index.ts", + "./graphql": "./src/graphql/index.ts", "./rpc": "./src/rpc/index.ts", "./schema": "./src/schema/index.ts", "./socket": "./src/socket/index.ts", @@ -89,6 +90,7 @@ "./persistence": "./dist/persistence/index.js", "./process": "./dist/process/index.js", "./reactivity": "./dist/reactivity/index.js", + "./graphql": "./dist/graphql/index.js", "./rpc": "./dist/rpc/index.js", "./schema": "./dist/schema/index.js", "./socket": "./dist/socket/index.js", @@ -110,13 +112,15 @@ "check": "tsc -b tsconfig.json" }, "devDependencies": { - "@types/node": "^26.6.4", + "@types/node": "^26.6.5", "ajv": "^8.20.0", "ajv-draft-04": "^1.0.0", "ast-types": "^0.16.3", + "esbuild": "^0.28.2", "immer": "^11.1.21", + "miniflare": "5.20260925.0-alpha", "protobufjs": "^8.8.0", - "tinybench": "^6.2.0", + "tinybench": "^6.2.1", "valibot": "^1.5.0" } } diff --git a/repos/effect/packages/effect/runtimeperf/README.md b/packages/effect/runtimeperf/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/README.md rename to packages/effect/runtimeperf/README.md diff --git a/repos/effect/packages/effect/runtimeperf/compare.mts b/packages/effect/runtimeperf/compare.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/compare.mts rename to packages/effect/runtimeperf/compare.mts diff --git a/repos/effect/packages/effect/runtimeperf/config.json b/packages/effect/runtimeperf/config.json similarity index 100% rename from repos/effect/packages/effect/runtimeperf/config.json rename to packages/effect/runtimeperf/config.json diff --git a/repos/effect/packages/effect/runtimeperf/materialize.mts b/packages/effect/runtimeperf/materialize.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/materialize.mts rename to packages/effect/runtimeperf/materialize.mts diff --git a/repos/effect/packages/effect/runtimeperf/run.mts b/packages/effect/runtimeperf/run.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/run.mts rename to packages/effect/runtimeperf/run.mts diff --git a/repos/effect/packages/effect/runtimeperf/stats.mts b/packages/effect/runtimeperf/stats.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/stats.mts rename to packages/effect/runtimeperf/stats.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts b/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts rename to packages/effect/runtimeperf/suites/arbitrary/fixtures/fast-check-v4.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts b/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts rename to packages/effect/runtimeperf/suites/arbitrary/fixtures/native.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts b/packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts rename to packages/effect/runtimeperf/suites/arbitrary/fixtures/schema.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md b/packages/effect/runtimeperf/suites/compiler-rebuild/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/README.md rename to packages/effect/runtimeperf/suites/compiler-rebuild/README.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/costs.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/aot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/generate.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/interpreted.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/jit.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/valibot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-compiled.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts b/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts rename to packages/effect/runtimeperf/suites/compiler-rebuild/fixtures/zod-jitless.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/report-resources.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/resources.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts b/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts rename to packages/effect/runtimeperf/suites/compiler-rebuild/run-resources.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/aot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/data.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/data.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts b/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts rename to packages/effect/runtimeperf/suites/moltar/fixtures/generate.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/interpreted.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/jit.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/valibot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts b/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts rename to packages/effect/runtimeperf/suites/moltar/fixtures/zod.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/data.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/effect-beta.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/valibot.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts b/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts rename to packages/effect/runtimeperf/suites/schema-benchmarks/fixtures/zod.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md b/packages/effect/runtimeperf/suites/schema-checked-sources/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/README.md rename to packages/effect/runtimeperf/suites/schema-checked-sources/README.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts b/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/schema-checked-sources/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md b/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md rename to packages/effect/runtimeperf/suites/schema-construction/AST-LAYOUT.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md b/packages/effect/runtimeperf/suites/schema-construction/README.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/README.md rename to packages/effect/runtimeperf/suites/schema-construction/README.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md b/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/RESULTS.md rename to packages/effect/runtimeperf/suites/schema-construction/RESULTS.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md b/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md rename to packages/effect/runtimeperf/suites/schema-construction/TO-TYPE.md diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts b/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/fixtures.mts rename to packages/effect/runtimeperf/suites/schema-construction/fixtures.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts b/packages/effect/runtimeperf/suites/schema-construction/profile.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/profile.mts rename to packages/effect/runtimeperf/suites/schema-construction/profile.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts b/packages/effect/runtimeperf/suites/schema-construction/run.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/run.mts rename to packages/effect/runtimeperf/suites/schema-construction/run.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts b/packages/effect/runtimeperf/suites/schema-construction/worker.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-construction/worker.mts rename to packages/effect/runtimeperf/suites/schema-construction/worker.mts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts b/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts rename to packages/effect/runtimeperf/suites/schema-excess-properties/fixtures/cases.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts b/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/adapters.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts b/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/behavior.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts b/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/cold.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/cold.ts diff --git a/repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts b/packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts rename to packages/effect/runtimeperf/suites/schema/fixtures/comparison.ts diff --git a/repos/effect/packages/effect/runtimeperf/test/materialize.test.mts b/packages/effect/runtimeperf/test/materialize.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/materialize.test.mts rename to packages/effect/runtimeperf/test/materialize.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/registry.test.mts b/packages/effect/runtimeperf/test/registry.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/registry.test.mts rename to packages/effect/runtimeperf/test/registry.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts b/packages/effect/runtimeperf/test/schema-construction.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/schema-construction.test.mts rename to packages/effect/runtimeperf/test/schema-construction.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/stats.test.mts b/packages/effect/runtimeperf/test/stats.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/stats.test.mts rename to packages/effect/runtimeperf/test/stats.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/worker.test.mts b/packages/effect/runtimeperf/test/worker.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/worker.test.mts rename to packages/effect/runtimeperf/test/worker.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts b/packages/effect/runtimeperf/test/worktree-diff.test.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/test/worktree-diff.test.mts rename to packages/effect/runtimeperf/test/worktree-diff.test.mts diff --git a/repos/effect/packages/effect/runtimeperf/utils.mts b/packages/effect/runtimeperf/utils.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/utils.mts rename to packages/effect/runtimeperf/utils.mts diff --git a/repos/effect/packages/effect/runtimeperf/worker.mts b/packages/effect/runtimeperf/worker.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/worker.mts rename to packages/effect/runtimeperf/worker.mts diff --git a/repos/effect/packages/effect/runtimeperf/worktree-diff.mts b/packages/effect/runtimeperf/worktree-diff.mts similarity index 100% rename from repos/effect/packages/effect/runtimeperf/worktree-diff.mts rename to packages/effect/runtimeperf/worktree-diff.mts diff --git a/repos/effect/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts similarity index 100% rename from repos/effect/packages/effect/src/Arbitrary.ts rename to packages/effect/src/Arbitrary.ts diff --git a/repos/effect/packages/effect/src/Array.ts b/packages/effect/src/Array.ts similarity index 99% rename from repos/effect/packages/effect/src/Array.ts rename to packages/effect/src/Array.ts index 402966e7f..31f0b052d 100644 --- a/repos/effect/packages/effect/src/Array.ts +++ b/packages/effect/src/Array.ts @@ -411,8 +411,10 @@ export const fromOption: (self: Option.Option) => Array = Option.toArra * * **Details** * - * `onNonEmpty` receives a `NonEmptyReadonlyArray`. Supports both data-first and - * data-last usage. + * `onNonEmpty` receives a `NonEmptyArray` for mutable arrays and a + * `NonEmptyReadonlyArray` otherwise. Data-last calls default to the readonly + * callback, so the returned function still accepts `ReadonlyArray`. Supports + * both data-first and data-last usage. * * **Example** (Branching on emptiness) * @@ -442,6 +444,19 @@ export const match: { readonly onNonEmpty: (self: NonEmptyReadonlyArray) => C } ): (self: ReadonlyArray) => B | C + ( + options: { + readonly onEmpty: LazyArg + readonly onNonEmpty: (self: NonEmptyArray) => C + } + ): (self: Array) => B | C + ( + self: Array, + options: { + readonly onEmpty: LazyArg + readonly onNonEmpty: (self: NonEmptyArray) => C + } + ): B | C ( self: ReadonlyArray, options: { diff --git a/repos/effect/packages/effect/src/BigDecimal.ts b/packages/effect/src/BigDecimal.ts similarity index 100% rename from repos/effect/packages/effect/src/BigDecimal.ts rename to packages/effect/src/BigDecimal.ts diff --git a/repos/effect/packages/effect/src/BigInt.ts b/packages/effect/src/BigInt.ts similarity index 100% rename from repos/effect/packages/effect/src/BigInt.ts rename to packages/effect/src/BigInt.ts diff --git a/repos/effect/packages/effect/src/Boolean.ts b/packages/effect/src/Boolean.ts similarity index 100% rename from repos/effect/packages/effect/src/Boolean.ts rename to packages/effect/src/Boolean.ts diff --git a/repos/effect/packages/effect/src/Brand.ts b/packages/effect/src/Brand.ts similarity index 100% rename from repos/effect/packages/effect/src/Brand.ts rename to packages/effect/src/Brand.ts diff --git a/repos/effect/packages/effect/src/ByteSize.ts b/packages/effect/src/ByteSize.ts similarity index 100% rename from repos/effect/packages/effect/src/ByteSize.ts rename to packages/effect/src/ByteSize.ts diff --git a/repos/effect/packages/effect/src/Cache.ts b/packages/effect/src/Cache.ts similarity index 100% rename from repos/effect/packages/effect/src/Cache.ts rename to packages/effect/src/Cache.ts diff --git a/repos/effect/packages/effect/src/Cause.ts b/packages/effect/src/Cause.ts similarity index 100% rename from repos/effect/packages/effect/src/Cause.ts rename to packages/effect/src/Cause.ts diff --git a/repos/effect/packages/effect/src/Channel.ts b/packages/effect/src/Channel.ts similarity index 99% rename from repos/effect/packages/effect/src/Channel.ts rename to packages/effect/src/Channel.ts index c6f3a68a1..35f3d6955 100644 --- a/repos/effect/packages/effect/src/Channel.ts +++ b/packages/effect/src/Channel.ts @@ -1932,7 +1932,9 @@ const readableStreamToPullUnsafe = (options: { * Each yielded value is emitted as an output element. The iterator's return * value becomes the channel's done value. Thrown or rejected iterator errors * are converted with `onError`. If the channel scope closes early and the - * iterator has a `return` method, that method is called. + * iterator has a `return` method, that method is called. It is awaited unless + * a `next()` call is still pending, in which case its result is ignored and an + * async generator may not run its cleanup until that pull settles. * * @stability stable * @category constructors @@ -1944,12 +1946,28 @@ export const fromAsyncIterable = ( ): Channel => fromTransform(Effect.fnUntraced(function*(_, scope) { const iter = iterable[Symbol.asyncIterator]() + let pulling = false if (iter.return) { - yield* Scope.addFinalizer(scope, Effect.promise(() => iter.return!())) + // `return()` may not settle until a pending `next()` does, so only + // await it when the iterator is idle. + yield* Scope.addFinalizer( + scope, + Effect.suspend(() => + pulling + ? Effect.sync(() => void iter.return!().catch(constVoid)) + : Effect.promise(() => iter.return!()) + ) + ) } return Effect.flatMap( Effect.tryPromise({ - try: () => iter.next(), + try: () => { + const promise = iter.next() + pulling = true + return promise.finally(() => { + pulling = false + }) + }, catch: onError }), (result) => result.done ? Cause.done(result.value) : Effect.succeed(result.value) @@ -1965,6 +1983,9 @@ export const fromAsyncIterable = ( * The iterator's return value becomes the channel's done value. Thrown or * rejected iterator errors are converted with `onError`. If the channel scope * closes early and the iterator has a `return` method, that method is called. + * It is awaited unless a `next()` call is still pending, in which case its + * result is ignored and an async generator may not run its cleanup until that + * pull settles. * * @stability stable * @category constructors diff --git a/repos/effect/packages/effect/src/ChannelSchema.ts b/packages/effect/src/ChannelSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/ChannelSchema.ts rename to packages/effect/src/ChannelSchema.ts diff --git a/repos/effect/packages/effect/src/Chunk.ts b/packages/effect/src/Chunk.ts similarity index 100% rename from repos/effect/packages/effect/src/Chunk.ts rename to packages/effect/src/Chunk.ts diff --git a/repos/effect/packages/effect/src/Clock.ts b/packages/effect/src/Clock.ts similarity index 100% rename from repos/effect/packages/effect/src/Clock.ts rename to packages/effect/src/Clock.ts diff --git a/repos/effect/packages/effect/src/Combiner.ts b/packages/effect/src/Combiner.ts similarity index 100% rename from repos/effect/packages/effect/src/Combiner.ts rename to packages/effect/src/Combiner.ts diff --git a/repos/effect/packages/effect/src/Config.ts b/packages/effect/src/Config.ts similarity index 100% rename from repos/effect/packages/effect/src/Config.ts rename to packages/effect/src/Config.ts diff --git a/repos/effect/packages/effect/src/ConfigProvider.ts b/packages/effect/src/ConfigProvider.ts similarity index 96% rename from repos/effect/packages/effect/src/ConfigProvider.ts rename to packages/effect/src/ConfigProvider.ts index 4250c4bc2..b07eef48f 100644 --- a/repos/effect/packages/effect/src/ConfigProvider.ts +++ b/packages/effect/src/ConfigProvider.ts @@ -1116,56 +1116,52 @@ function dotEnvExpand(parsed: Record): Record { for (const configKey of Object.keys(parsed)) { // resolve escape sequences - newParsed[configKey] = interpolate(parsed[configKey], parsed).replace(/\\\$/g, "$") + newParsed[configKey] = interpolate(parsed[configKey], parsed, new Set([configKey])).replace(/\\\$/g, "$") } return newParsed } -function interpolate(envValue: string, parsed: Record): string { - // find the last unescaped dollar sign in the - // value so that we can evaluate it - const lastUnescapedDollarSignIndex = searchLast(envValue, /(?!(?<=\\))\$/g) - - // If we couldn't match any unescaped dollar sign - // let's return the string as is - if (lastUnescapedDollarSignIndex === -1) return envValue - - // This is the right-most group of variables in the string - const rightMostGroup = envValue.slice(lastUnescapedDollarSignIndex) - - /** - * This finds the inner most variable/group divided - * by variable name and default value (if present) - * ( - * (?!(?<=\\))\$ // only match dollar signs that are not escaped - * {? // optional opening curly brace - * ([\w]+) // match the variable name - * (?::-([^}\\]*))? // match an optional default value - * }? // optional closing curly brace - * ) - */ - const matchGroup = /((?!(?<=\\))\${?([\w]+)(?::-([^}\\]*))?}?)/ - const match = rightMostGroup.match(matchGroup) - - if (match !== null) { - const [_, group, variableName, defaultValue] = match - const value = Object.hasOwn(parsed, variableName) && parsed[variableName] !== "" - ? parsed[variableName] - : defaultValue ?? "" - - return interpolate( - envValue.replace(group, () => value), - parsed - ) +function interpolate( + envValue: string, + parsed: Record, + seen: ReadonlySet +): string { + const reference = /(? 0 ? matches.slice(-1)[0].index : -1 + return result + envValue.slice(cursor) } /** diff --git a/repos/effect/packages/effect/src/Console.ts b/packages/effect/src/Console.ts similarity index 100% rename from repos/effect/packages/effect/src/Console.ts rename to packages/effect/src/Console.ts diff --git a/repos/effect/packages/effect/src/Context.ts b/packages/effect/src/Context.ts similarity index 98% rename from repos/effect/packages/effect/src/Context.ts rename to packages/effect/src/Context.ts index 90e6f0d6c..c943df47f 100644 --- a/repos/effect/packages/effect/src/Context.ts +++ b/packages/effect/src/Context.ts @@ -813,9 +813,9 @@ export const addUnsafe = ( const impl = self as ContextImpl const cacheRoot = cacheKeys.has(key) ? undefined : impl.cacheRoot if (impl.depth >= MaxDepth) { - // Rebase the overlay chain into a flat map, keeping the cacheRoot so a - // rebase on an ordinary key does not invalidate fiber caches - const map = new Map(impl.mapUnsafe) + // Avoid mapUnsafe: it would flatten the parent before copying. + const map = new Map(impl._flat ?? impl.base) + if (!impl._flat) applyOverlays(map, impl.overlay) map.set(key, service) return makeImpl(cacheRoot, map, undefined, 0) } @@ -1258,6 +1258,15 @@ export const pick = >>( ...services: S ) => (self: Context): Context> => { + if (services.length <= 1) { + const map = new Map() + if (services.length === 1) { + const key = services[0].key + const value = lookup(self, key) + if (value !== notFound) map.set(key, value) + } + return makeUnsafe(map) + } const keep = new Set(services.map((key) => key.key)) return withFlat(self, (map) => map.forEach((_, key) => { diff --git a/repos/effect/packages/effect/src/Cron.ts b/packages/effect/src/Cron.ts similarity index 100% rename from repos/effect/packages/effect/src/Cron.ts rename to packages/effect/src/Cron.ts diff --git a/repos/effect/packages/effect/src/Crypto.ts b/packages/effect/src/Crypto.ts similarity index 80% rename from repos/effect/packages/effect/src/Crypto.ts rename to packages/effect/src/Crypto.ts index 43daf4330..482c3be06 100644 --- a/repos/effect/packages/effect/src/Crypto.ts +++ b/packages/effect/src/Crypto.ts @@ -66,7 +66,7 @@ export type DigestAlgorithm = "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512" * const program = Effect.gen(function*() { * const crypto = yield* Crypto.Crypto * const bytes = yield* crypto.randomBytes(16) - * const uuidv4 = yield* crypto.randomUUIDv4 + * const uuidv4 = yield* crypto.randomUUIDv4() * const hash = yield* crypto.digest("SHA-256", bytes) * return [bytes.length, uuidv4.length, hash.length] * }) @@ -148,14 +148,42 @@ export interface Crypto { randomShuffle(elements: Iterable): Effect.Effect> /** - * Generates a cryptographically secure UUIDv4 string. + * Generates a cryptographically secure UUIDv4. + * + * **Details** + * + * The default `"hex"` format returns a lowercase, hyphenated UUID string + * (36 characters), not a bare hexadecimal string. The `"bytes"` format + * returns the 16 UUID bytes, including the version and variant bits. */ - readonly randomUUIDv4: Effect.Effect + randomUUIDv4(options?: { + readonly format?: "hex" | undefined + }): Effect.Effect + randomUUIDv4(options: { + readonly format: Format + }): Effect.Effect + randomUUIDv4(options?: { + readonly format?: "hex" | "bytes" | undefined + }): Effect.Effect /** - * Generates a cryptographically secure UUIDv7 string. + * Generates a cryptographically secure UUIDv7 using the `Clock` timestamp. + * + * **Details** + * + * The default `"hex"` format returns a lowercase, hyphenated UUID string + * (36 characters), not a bare hexadecimal string. The `"bytes"` format + * returns the 16 UUID bytes, including the timestamp, version and variant bits. */ - readonly randomUUIDv7: Effect.Effect + randomUUIDv7(options?: { + readonly format?: "hex" | undefined + }): Effect.Effect + randomUUIDv7(options: { + readonly format: Format + }): Effect.Effect + randomUUIDv7(options?: { + readonly format?: "hex" | "bytes" | undefined + }): Effect.Effect /** * Generates a cryptographically secure ULID string. @@ -293,10 +321,16 @@ export const make = ( } return buffer }), - randomUUIDv4: Effect.sync(() => Uuid.v4String(randomBytesUnsafe(16))), - randomUUIDv7: Effect.clockWith((clock) => - Effect.succeed(Uuid.v7String(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(16))) - ), + randomUUIDv4: ((options?: { readonly format?: "hex" | "bytes" | undefined }) => + Effect.sync(() => { + const bytes = Uuid.v4Bytes(randomBytesUnsafe(16)) + return options?.format === "bytes" ? bytes : Uuid.stringify(bytes) + })) as Crypto["randomUUIDv4"], + randomUUIDv7: ((options?: { readonly format?: "hex" | "bytes" | undefined }) => + Effect.clockWith((clock) => { + const bytes = Uuid.v7Bytes(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(16)) + return Effect.succeed(options?.format === "bytes" ? bytes : Uuid.stringify(bytes)) + })) as Crypto["randomUUIDv7"], randomULID: Effect.clockWith((clock) => Effect.succeed(Ulid.ulidString(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(10))) ) diff --git a/repos/effect/packages/effect/src/Data.ts b/packages/effect/src/Data.ts similarity index 100% rename from repos/effect/packages/effect/src/Data.ts rename to packages/effect/src/Data.ts diff --git a/repos/effect/packages/effect/src/DateTime.ts b/packages/effect/src/DateTime.ts similarity index 99% rename from repos/effect/packages/effect/src/DateTime.ts rename to packages/effect/src/DateTime.ts index 38a7e456a..b86d4f6a9 100644 --- a/repos/effect/packages/effect/src/DateTime.ts +++ b/packages/effect/src/DateTime.ts @@ -1551,7 +1551,7 @@ export const isPastUnsafe: (self: DateTime) => boolean = Internal.isPastUnsafe * * **Details** * - * This always returns the UTC representation, ignoring any time zone information. + * This preserves the original instant, regardless of any time zone information. * * **Example** (Converting DateTime values to UTC Dates) * @@ -1576,7 +1576,9 @@ export const toDateUtc: (self: DateTime) => Date = Internal.toDateUtc * * **Details** * - * For `DateTime.Zoned`, this adjusts for the time zone before converting. + * For `DateTime.Zoned`, this stores the zoned wall-clock time in the returned + * `Date`'s UTC fields, rather than preserving the original instant. Use + * `toDateUtc` to preserve the instant. * For `DateTime.Utc`, this is equivalent to `toDateUtc`. * * **Example** (Converting DateTime values to Dates) @@ -1584,13 +1586,14 @@ export const toDateUtc: (self: DateTime) => Date = Internal.toDateUtc * ```ts import.meta.vitest * import { DateTime } from "effect" * - * const utc = DateTime.makeUnsafe("2024-01-01T12:00:00Z") - * const zoned = DateTime.makeZonedUnsafe("2024-01-01T12:00:00Z", { + * const utc = DateTime.makeUnsafe("2024-07-01T12:00:00Z") + * const zoned = DateTime.makeZonedUnsafe("2024-07-01T12:00:00Z", { * timeZone: "Europe/London" * }) * - * DateTime.toDate(utc).toISOString() // => "2024-01-01T12:00:00.000Z" - * DateTime.toDate(zoned).toISOString() // => "2024-01-01T12:00:00.000Z" + * DateTime.toDate(utc).toISOString() // => "2024-07-01T12:00:00.000Z" + * DateTime.toDate(zoned).toISOString() // => "2024-07-01T13:00:00.000Z" + * DateTime.toDateUtc(zoned).toISOString() // => "2024-07-01T12:00:00.000Z" * ``` * * @stability stable diff --git a/repos/effect/packages/effect/src/Deferred.ts b/packages/effect/src/Deferred.ts similarity index 100% rename from repos/effect/packages/effect/src/Deferred.ts rename to packages/effect/src/Deferred.ts diff --git a/repos/effect/packages/effect/src/Differ.ts b/packages/effect/src/Differ.ts similarity index 100% rename from repos/effect/packages/effect/src/Differ.ts rename to packages/effect/src/Differ.ts diff --git a/repos/effect/packages/effect/src/Duration.ts b/packages/effect/src/Duration.ts similarity index 96% rename from repos/effect/packages/effect/src/Duration.ts rename to packages/effect/src/Duration.ts index e93556fa9..5d4a3a402 100644 --- a/repos/effect/packages/effect/src/Duration.ts +++ b/packages/effect/src/Duration.ts @@ -36,10 +36,12 @@ const bigint1e3 = BigInt(1_000) const bigint1e6 = BigInt(1_000_000) const bigint1e9 = BigInt(1_000_000_000) -const roundTiesAwayFromZero = (input: number): bigint => - BigInt(input < 0 ? Math.ceil(input - 0.5) : Math.floor(input + 0.5)) +const roundTiesAwayFromZero = (input: number): bigint => BigInt(Math.sign(input) * Math.round(Math.abs(input))) -const roundMillisToNanos = (millis: number): bigint => roundTiesAwayFromZero(millis * 1_000_000) +const roundMillisToNanos = (millis: number): bigint => + Number.isInteger(millis) ? BigInt(millis) * bigint1e6 : roundTiesAwayFromZero(millis * 1_000_000) + +const fromNanosNumber = (nanos: number): Duration => make(Number.isFinite(nanos) ? roundTiesAwayFromZero(nanos) : nanos) const parseNanos = (input: string, scale: bigint): bigint => { const decimalIndex = input.indexOf(".") @@ -51,9 +53,13 @@ const parseNanos = (input: string, scale: bigint): bigint => { const scaled = ( BigInt(input.slice(isNegative ? 1 : 0, decimalIndex)) * fractionalScale + BigInt(fractional) ) * scale - const rounded = scaled / fractionalScale + - (scaled % fractionalScale * bigint2 >= fractionalScale ? bigint1 : bigint0) - return isNegative ? -rounded : rounded + return divideTiesAwayFromZero(isNegative ? -scaled : scaled, fractionalScale) +} + +const divideTiesAwayFromZero = (numerator: bigint, denominator: bigint): bigint => { + const absolute = numerator < bigint0 ? -numerator : numerator + const rounded = absolute / denominator + (absolute % denominator * bigint2 >= denominator ? bigint1 : bigint0) + return numerator < bigint0 ? -rounded : rounded } const nanosToHrTime = (nanos: bigint): [seconds: number, nanos: number] => { @@ -307,7 +313,7 @@ export const fromInputUnsafe = (input: Input): Duration => { if (input[0] === Infinity || input[1] === Infinity) { return infinity } - return make(roundTiesAwayFromZero(input[0] * 1_000_000_000 + input[1])) + return fromNanosNumber(input[0] * 1_000_000_000 + input[1]) } const obj = input as DurationObject let millis = 0 @@ -319,9 +325,7 @@ export const fromInputUnsafe = (input: Input): Duration => { if (obj.seconds) millis += obj.seconds * 1_000 if (obj.milliseconds) millis += obj.milliseconds if (!obj.microseconds && !obj.nanoseconds) return make(millis) - return make(roundTiesAwayFromZero( - millis * 1_000_000 + (obj.microseconds ?? 0) * 1_000 + (obj.nanoseconds ?? 0) - )) + return fromNanosNumber(millis * 1_000_000 + (obj.microseconds ?? 0) * 1_000 + (obj.nanoseconds ?? 0)) } } return invalid(input) @@ -362,10 +366,8 @@ const DurationProto: Omit = { // Hash equal finite durations using the same canonical nanoseconds // representation used by `equals`. switch (this.value._tag) { - case "Millis": { - const nanos = this.value.millis * 1_000_000 - return Number.isFinite(nanos) ? Hash.hash(roundTiesAwayFromZero(nanos)) : Hash.number(this.value.millis) - } + case "Millis": + return Hash.hash(roundMillisToNanos(this.value.millis)) case "Nanos": return Hash.hash(this.value.nanos) default: @@ -1434,8 +1436,8 @@ export const divideUnsafe: { * * **Details** * - * For nanosecond-backed durations, the multiplier must be convertible to a - * `bigint`; fractional or non-finite multipliers can throw. Infinite + * Fractional multipliers on nanosecond-backed durations round the result to + * the nearest nanosecond, with ties rounding away from zero. Infinite * durations return positive infinity, negative infinity, or zero depending on * the multiplier sign. * @@ -1459,7 +1461,24 @@ export const times: { (self: Duration, times: number): Duration => match(self, { onMillis: (millis) => make(millis * times), - onNanos: (nanos) => make(nanos * BigInt(times)), + onNanos: (nanos) => { + if (Number.isInteger(times)) return make(nanos * BigInt(times)) + const nanosNumber = Number(nanos) + const scaled = nanosNumber * times + if ( + !Number.isFinite(times) || + (Number.isSafeInteger(nanosNumber) && Math.abs(scaled) <= Number.MAX_SAFE_INTEGER) + ) { + return fromNanosNumber(scaled) + } + let numerator = times + let denominator = bigint1 + while (!Number.isInteger(numerator)) { + numerator *= 2 + denominator *= bigint2 + } + return make(divideTiesAwayFromZero(nanos * BigInt(numerator), denominator)) + }, onInfinity: () => times > 0 ? infinity : times < 0 ? negativeInfinity : zero, onNegativeInfinity: () => times > 0 ? negativeInfinity : times < 0 ? infinity : zero }) diff --git a/repos/effect/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts similarity index 99% rename from repos/effect/packages/effect/src/Effect.ts rename to packages/effect/src/Effect.ts index ac90f3f94..94a25e42f 100644 --- a/repos/effect/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -20,7 +20,7 @@ import type * as ExecutionPlan from "./ExecutionPlan.ts" import * as Exit from "./Exit.ts" import type { Fiber } from "./Fiber.ts" import type * as Filter from "./Filter.ts" -import { constant, dual, type LazyArg } from "./Function.ts" +import { dual, type LazyArg } from "./Function.ts" import type { TypeLambda } from "./HKT.ts" import type { Inspectable } from "./Inspectable.ts" import * as core from "./internal/core.ts" @@ -14770,50 +14770,113 @@ export const trackDuration: { // Transactions // ----------------------------------------------------------------------------- +/** + * Why the current transaction attempt started after a previous attempt did not commit. + * + * **Details** + * + * `"retry"` means the previous attempt called {@link txRetry}. `"conflict"` + * means an accessed `TxRef` changed before commit. The first attempt has no + * retry reason. + * + * @stability stable + * @category models + * @since 4.0.0 + */ +export type TransactionRetryReason = "retry" | "conflict" + +/** + * Journal and attempt metadata for an Effect transaction. + * + * **When to use** + * + * Use to type the value provided by {@link Transaction}. + * + * **Details** + * + * `attempt` starts at `1` and counts each run of the outermost transaction body. + * `retryReason` is `"retry"` after {@link txRetry} and `"conflict"` after an + * accessed `TxRef` changes before commit; it is unset on the first attempt. + * `start`, `now`, `elapsed`, and `elapsedSincePrevious` are millisecond + * timestamps and durations for the current attempt, matching {@link Schedule} + * metadata. Each journal entry stores the observed version and value of a + * `TxRef`. `written` is true when this transaction has updated that ref. + * + * @see {@link Transaction} for the context service that provides this metadata + * @see {@link TransactionRetryReason} for the retry-reason tags recorded on later attempts + * + * @stability stable + * @category models + * @since 4.0.0 + */ +export interface TransactionMeta { + readonly journal: ReadonlyMap< + TxRef, + { + readonly version: number + readonly value: any + readonly written: boolean + } + > + readonly attempt: number + readonly retryReason: TransactionRetryReason | undefined + readonly start: number + readonly now: number + readonly elapsed: number + readonly elapsedSincePrevious: number +} + +/** @internal */ +export interface TransactionMetaInner { + retry: boolean + journal: Map< + TxRef, + { + readonly version: number + value: any + written: boolean + } + > + attempt: number + retryReason: TransactionRetryReason | undefined + start: number + now: number + elapsed: number + elapsedSincePrevious: number +} + /** * Service that holds the current transaction state. * * **Details** * - * It includes a journal that stores non-committed changes to `TxRef` values and - * a retry flag that records whether the transaction should be retried. + * Nested {@link tx} calls reuse this same service instance. Yield it inside a + * transaction body to read {@link TransactionMeta} fields such as `attempt` + * and `retryReason`. * - * **Example** (Building transactions) + * **Example** (Reading transaction metadata) * * ```ts import.meta.vitest * import { Effect } from "effect" * - * // Transaction class for software transactional memory operations - * const txEffect = Effect.gen(function*() { + * const program = Effect.tx(Effect.gen(function*() { * const tx = yield* Effect.Transaction - * // Use transaction for coordinated state changes - * return "Transaction complete" - * }) + * return [tx.attempt, tx.retryReason, tx.elapsed, tx.elapsedSincePrevious] + * })) * - * const runnable = Effect.provideService(txEffect, Effect.Transaction, { - * retry: false, - * journal: new Map() - * }) - * Effect.runSync(runnable) // => "Transaction complete" + * Effect.runSync(program) // => [1, undefined, 0, 0] * ``` * + * @see {@link tx} for the outermost transaction boundary that creates this service + * @see {@link TransactionMeta} for the journal and attempt metadata + * * @stability stable * @category transactions * @since 4.0.0 */ export class Transaction extends Context.Service< Transaction, - { - retry: boolean - readonly journal: Map< - TxRef, - { - readonly version: number - value: any - written: boolean - } - > - } + TransactionMeta >()("effect/Effect/Transaction") {} /** @@ -14867,52 +14930,75 @@ export const tx = ( effect: Effect ): Effect> => withFiber((fiber) => { - let state = Context.getOrUndefined(fiber.context, Transaction) + const state = Context.getOrUndefined(fiber.context, Transaction) if (state && !completedTransactions.has(state)) { return effect as Effect> } + const clock = fiber.getRef(internal.ClockRef) + const now = clock.currentTimeMillisUnsafe() // Create transaction state only at the outermost boundary - state = { journal: new Map(), retry: false } + const mutable: TransactionMetaInner = { + journal: new Map(), + retry: false, + attempt: 1, + retryReason: undefined, + start: now, + now, + elapsed: 0, + elapsedSincePrevious: 0 + } let result: Exit.Exit | undefined - return uninterruptibleMask((restore) => - flatMap( + return uninterruptibleMask((restore) => { + const run = restore(effect).pipe( + provideService(Transaction, mutable), + tapCause((cause) => { + if (!mutable.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(mutable)) + return restore(awaitPendingTransaction(mutable)) + }), + exit + ) + return flatMap( whileLoop({ while: () => !result, - body: constant( - restore(effect).pipe( - provideService(Transaction, state), - 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 - ) - ), + body: () => { + if (mutable.attempt > 1) { + stampTransaction(mutable, clock.currentTimeMillisUnsafe()) + } + return run + }, step(exit: Exit.Exit) { - if (state.retry || !isTransactionConsistent(state)) { - return clearTransaction(state) + if (mutable.retry || !isTransactionConsistent(mutable)) { + mutable.attempt++ + mutable.retryReason = mutable.retry ? "retry" : "conflict" + return clearTransaction(mutable) } if (Exit.isSuccess(exit)) { - commitTransaction(fiber, state) + commitTransaction(fiber, mutable) } - clearTransaction(state) - completedTransactions.add(state) + clearTransaction(mutable) + completedTransactions.add(mutable) result = exit } }), () => result! ) - ) + }) }) // 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"]) => { +const stampTransaction = (state: TransactionMetaInner, now: number) => { + state.elapsedSincePrevious = now - state.now + state.now = now + state.elapsed = now - state.start +} + +const isTransactionConsistent = (state: TransactionMeta) => { for (const [ref, { version }] of state.journal) { if (ref.version !== version) { return false @@ -14921,7 +15007,7 @@ const isTransactionConsistent = (state: Transaction["Service"]) => { return true } -const awaitPendingTransaction = (state: Transaction["Service"]) => +const awaitPendingTransaction = (state: TransactionMeta) => callback((resume) => { // Validate the read set and register the waiter in one synchronous step. // A commit that landed after the reads has already signalled its waiters @@ -14946,7 +15032,7 @@ const awaitPendingTransaction = (state: Transaction["Service"]) => return sync(clearPending) }) -function commitTransaction(fiber: Fiber, state: Transaction["Service"]) { +function commitTransaction(fiber: Fiber, state: TransactionMeta) { for (const [ref, { value, written }] of state.journal) { if (!written) continue if (!Object.is(value, ref.value)) { @@ -14960,7 +15046,7 @@ function commitTransaction(fiber: Fiber, state: Transaction["S } } -function clearTransaction(state: Transaction["Service"]) { +function clearTransaction(state: TransactionMetaInner) { state.retry = false state.journal.clear() } @@ -15005,7 +15091,8 @@ function clearTransaction(state: Transaction["Service"]) { export const txRetry: Effect = flatMap( Transaction, (state) => { - state.retry = true + const mutable = state as TransactionMetaInner + mutable.retry = true return interrupt } ) diff --git a/repos/effect/packages/effect/src/Effectable.ts b/packages/effect/src/Effectable.ts similarity index 100% rename from repos/effect/packages/effect/src/Effectable.ts rename to packages/effect/src/Effectable.ts diff --git a/repos/effect/packages/effect/src/Equal.ts b/packages/effect/src/Equal.ts similarity index 100% rename from repos/effect/packages/effect/src/Equal.ts rename to packages/effect/src/Equal.ts diff --git a/repos/effect/packages/effect/src/Equivalence.ts b/packages/effect/src/Equivalence.ts similarity index 100% rename from repos/effect/packages/effect/src/Equivalence.ts rename to packages/effect/src/Equivalence.ts diff --git a/repos/effect/packages/effect/src/ErrorReporter.ts b/packages/effect/src/ErrorReporter.ts similarity index 100% rename from repos/effect/packages/effect/src/ErrorReporter.ts rename to packages/effect/src/ErrorReporter.ts diff --git a/repos/effect/packages/effect/src/ExecutionPlan.ts b/packages/effect/src/ExecutionPlan.ts similarity index 100% rename from repos/effect/packages/effect/src/ExecutionPlan.ts rename to packages/effect/src/ExecutionPlan.ts diff --git a/repos/effect/packages/effect/src/Exit.ts b/packages/effect/src/Exit.ts similarity index 100% rename from repos/effect/packages/effect/src/Exit.ts rename to packages/effect/src/Exit.ts diff --git a/repos/effect/packages/effect/src/Fiber.ts b/packages/effect/src/Fiber.ts similarity index 100% rename from repos/effect/packages/effect/src/Fiber.ts rename to packages/effect/src/Fiber.ts diff --git a/repos/effect/packages/effect/src/FiberHandle.ts b/packages/effect/src/FiberHandle.ts similarity index 100% rename from repos/effect/packages/effect/src/FiberHandle.ts rename to packages/effect/src/FiberHandle.ts diff --git a/repos/effect/packages/effect/src/FiberMap.ts b/packages/effect/src/FiberMap.ts similarity index 100% rename from repos/effect/packages/effect/src/FiberMap.ts rename to packages/effect/src/FiberMap.ts diff --git a/repos/effect/packages/effect/src/FiberSet.ts b/packages/effect/src/FiberSet.ts similarity index 100% rename from repos/effect/packages/effect/src/FiberSet.ts rename to packages/effect/src/FiberSet.ts diff --git a/repos/effect/packages/effect/src/FileSystem.ts b/packages/effect/src/FileSystem.ts similarity index 91% rename from repos/effect/packages/effect/src/FileSystem.ts rename to packages/effect/src/FileSystem.ts index e633dd873..6ef524101 100644 --- a/repos/effect/packages/effect/src/FileSystem.ts +++ b/packages/effect/src/FileSystem.ts @@ -220,12 +220,17 @@ export interface FileSystem { * **Details** * * The file handle will be automatically closed when the scope is closed. + * + * `noFollow` rejects symlinks at the final path component, not in parent + * directories. Node and Bun support it on POSIX; unsupported platforms, + * including Windows and Deno, fail with `BadArgument`. */ readonly open: ( path: string, options?: { readonly flag?: OpenFlag | undefined readonly mode?: number | undefined + readonly noFollow?: boolean | undefined } ) => Effect.Effect /** @@ -827,6 +832,10 @@ export const isFile = (u: unknown): u is File => hasProperty(u, FileTypeId) * cursor. Outside append mode, `truncate` clamps a cursor past the new length * to that length. * + * Positional reads leave the cursor unchanged and may run concurrently on + * one handle. Cursor operations (`seek`, reads without a position, and writes) + * must not run concurrently with each other. + * * **Example** (Working with file handles) * * ```ts import.meta.vitest @@ -877,8 +886,20 @@ export interface File { */ readonly seek: (offset: bigint, from: SeekMode) => Effect.Effect readonly sync: Effect.Effect - readonly read: (buffer: Uint8Array) => Effect.Effect - readonly readAlloc: (size: number) => Effect.Effect, PlatformError> + /** + * Reads into `buffer`, returning the byte count or `0` at EOF. Advances the + * cursor unless `options.position` is set (see `File.ReadOptions`). + */ + readonly read: (buffer: Uint8Array, options?: File.ReadOptions) => Effect.Effect + /** + * Reads up to `size` bytes into a new buffer, returning `Option.none()` at + * EOF. Advances the cursor unless `options.position` is set (see + * `File.ReadOptions`). + */ + readonly readAlloc: ( + size: number, + options?: File.ReadOptions + ) => Effect.Effect, PlatformError> readonly truncate: (length?: number) => Effect.Effect readonly write: (buffer: Uint8Array) => Effect.Effect readonly writeAll: (buffer: Uint8Array) => Effect.Effect @@ -892,6 +913,31 @@ export interface File { * @since 4.0.0 */ export declare namespace File { + /** + * Options for `File.read` and `File.readAlloc`. + * + * **Details** + * + * `position` is a byte offset from the start of the file. It leaves the + * cursor unchanged, fails with `BadArgument` if negative, and reads nothing + * at or past EOF. + * + * **Example** (Reading a byte range without moving the cursor) + * + * ```ts import.meta.vitest + * import type { FileSystem } from "effect" + * + * const options: FileSystem.File.ReadOptions = { position: BigInt(1024) } + * options.position // => 1024n + * ``` + * + * @category models + * @since 4.1.0 + */ + export interface ReadOptions { + readonly position?: bigint | undefined + } + /** * Enumeration of possible file system entry types. * @@ -922,9 +968,17 @@ export declare namespace File { * permissions, and size information. This structure is returned by file * stat operations. * - * Node and Bun preserve `size` and `blksize` exactly. Optional `number` - * metadata is `Option.none()` when absent or outside the safe integer range. - * Unsafe `dev` or `mode` values fail the stat operation with `BadArgument`. + * `dev`, `ino`, `rdev`, `nlink`, and `blocks` are `bigint` values, so + * 64-bit device identifiers, inode numbers, and counts are not limited to + * the safe integer range. `mode`, `uid`, and `gid` are 32-bit values and + * remain `number`. Optional metadata is `Option.none()` only when the + * platform does not report it. + * + * Node and Bun report all fields exactly, with `dev`, `ino`, `rdev`, and + * `nlink` interpreted as unsigned 64-bit integers. Unsafe `mode` values fail + * the stat operation with `BadArgument`. Deno reports these fields as + * `number`, so on Deno values above `Number.MAX_SAFE_INTEGER` are already + * rounded before they are converted to `bigint`. * * **Example** (Inspecting file information) * @@ -936,7 +990,7 @@ export declare namespace File { * mtime: Option.none(), * atime: Option.none(), * birthtime: Option.none(), - * dev: 1, + * dev: 1n, * ino: Option.none(), * mode: 0o644, * nlink: Option.none(), @@ -968,16 +1022,16 @@ export declare namespace File { readonly mtime: Option.Option readonly atime: Option.Option readonly birthtime: Option.Option - readonly dev: number - readonly ino: Option.Option + readonly dev: bigint + readonly ino: Option.Option readonly mode: number - readonly nlink: Option.Option + readonly nlink: Option.Option readonly uid: Option.Option readonly gid: Option.Option - readonly rdev: Option.Option + readonly rdev: Option.Option readonly size: ByteSize.ByteSize readonly blksize: Option.Option - readonly blocks: Option.Option + readonly blocks: Option.Option } } @@ -1015,6 +1069,12 @@ export interface WatchOptions { * When `true`, changes in subdirectories are also reported. */ readonly recursive?: boolean | undefined + /** + * Called once the underlying watcher is installed and can buffer events. + * Stream construction alone does not install a watcher. Custom watch + * backends must also call this callback after installation, when supplied. + */ + readonly onReady?: (() => void) | undefined } /** diff --git a/repos/effect/packages/effect/src/Filter.ts b/packages/effect/src/Filter.ts similarity index 100% rename from repos/effect/packages/effect/src/Filter.ts rename to packages/effect/src/Filter.ts diff --git a/repos/effect/packages/effect/src/Formatter.ts b/packages/effect/src/Formatter.ts similarity index 100% rename from repos/effect/packages/effect/src/Formatter.ts rename to packages/effect/src/Formatter.ts diff --git a/repos/effect/packages/effect/src/Function.ts b/packages/effect/src/Function.ts similarity index 100% rename from repos/effect/packages/effect/src/Function.ts rename to packages/effect/src/Function.ts diff --git a/repos/effect/packages/effect/src/Graph.ts b/packages/effect/src/Graph.ts similarity index 100% rename from repos/effect/packages/effect/src/Graph.ts rename to packages/effect/src/Graph.ts diff --git a/repos/effect/packages/effect/src/HKT.ts b/packages/effect/src/HKT.ts similarity index 100% rename from repos/effect/packages/effect/src/HKT.ts rename to packages/effect/src/HKT.ts diff --git a/repos/effect/packages/effect/src/Hash.ts b/packages/effect/src/Hash.ts similarity index 100% rename from repos/effect/packages/effect/src/Hash.ts rename to packages/effect/src/Hash.ts diff --git a/repos/effect/packages/effect/src/HashMap.ts b/packages/effect/src/HashMap.ts similarity index 100% rename from repos/effect/packages/effect/src/HashMap.ts rename to packages/effect/src/HashMap.ts diff --git a/repos/effect/packages/effect/src/HashRing.ts b/packages/effect/src/HashRing.ts similarity index 100% rename from repos/effect/packages/effect/src/HashRing.ts rename to packages/effect/src/HashRing.ts diff --git a/repos/effect/packages/effect/src/HashSet.ts b/packages/effect/src/HashSet.ts similarity index 100% rename from repos/effect/packages/effect/src/HashSet.ts rename to packages/effect/src/HashSet.ts diff --git a/repos/effect/packages/effect/src/Inspectable.ts b/packages/effect/src/Inspectable.ts similarity index 100% rename from repos/effect/packages/effect/src/Inspectable.ts rename to packages/effect/src/Inspectable.ts diff --git a/repos/effect/packages/effect/src/Iterable.ts b/packages/effect/src/Iterable.ts similarity index 100% rename from repos/effect/packages/effect/src/Iterable.ts rename to packages/effect/src/Iterable.ts diff --git a/repos/effect/packages/effect/src/JsonPatch.ts b/packages/effect/src/JsonPatch.ts similarity index 100% rename from repos/effect/packages/effect/src/JsonPatch.ts rename to packages/effect/src/JsonPatch.ts diff --git a/repos/effect/packages/effect/src/JsonPointer.ts b/packages/effect/src/JsonPointer.ts similarity index 100% rename from repos/effect/packages/effect/src/JsonPointer.ts rename to packages/effect/src/JsonPointer.ts diff --git a/repos/effect/packages/effect/src/JsonSchema.ts b/packages/effect/src/JsonSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/JsonSchema.ts rename to packages/effect/src/JsonSchema.ts diff --git a/repos/effect/packages/effect/src/Latch.ts b/packages/effect/src/Latch.ts similarity index 100% rename from repos/effect/packages/effect/src/Latch.ts rename to packages/effect/src/Latch.ts diff --git a/repos/effect/packages/effect/src/Layer.ts b/packages/effect/src/Layer.ts similarity index 100% rename from repos/effect/packages/effect/src/Layer.ts rename to packages/effect/src/Layer.ts diff --git a/repos/effect/packages/effect/src/LayerMap.ts b/packages/effect/src/LayerMap.ts similarity index 100% rename from repos/effect/packages/effect/src/LayerMap.ts rename to packages/effect/src/LayerMap.ts diff --git a/repos/effect/packages/effect/src/LayerRef.ts b/packages/effect/src/LayerRef.ts similarity index 100% rename from repos/effect/packages/effect/src/LayerRef.ts rename to packages/effect/src/LayerRef.ts diff --git a/repos/effect/packages/effect/src/LogLevel.ts b/packages/effect/src/LogLevel.ts similarity index 100% rename from repos/effect/packages/effect/src/LogLevel.ts rename to packages/effect/src/LogLevel.ts diff --git a/repos/effect/packages/effect/src/Logger.ts b/packages/effect/src/Logger.ts similarity index 100% rename from repos/effect/packages/effect/src/Logger.ts rename to packages/effect/src/Logger.ts diff --git a/repos/effect/packages/effect/src/ManagedRuntime.ts b/packages/effect/src/ManagedRuntime.ts similarity index 100% rename from repos/effect/packages/effect/src/ManagedRuntime.ts rename to packages/effect/src/ManagedRuntime.ts diff --git a/repos/effect/packages/effect/src/Match.ts b/packages/effect/src/Match.ts similarity index 96% rename from repos/effect/packages/effect/src/Match.ts rename to packages/effect/src/Match.ts index 4cd164619..8dd3b5f5e 100644 --- a/repos/effect/packages/effect/src/Match.ts +++ b/packages/effect/src/Match.ts @@ -569,6 +569,26 @@ export const withReturnType: () => [Ret] extends [[A] extends [never] ? any : A] ? Matcher : "withReturnType constraint does not extend Result type" = internal.withReturnType +// `P` is a const type parameter, so object literals skip excess-property checks. +// Reject pattern keys that no object variant of the input has. Functions, +// refinements, and array patterns are not checked. +type PatternKey = K extends number ? `${K}` : K + +type PatternKeys = object extends A ? PropertyKey + : A extends object ? PatternKey + : never + +type ObjectPatternGuard = [P] extends [Function | ReadonlyArray | SafeRefinement] ? unknown + : [P] extends [object] ? { readonly [K in Exclude, PatternKeys>]: never } + : unknown + +type ExactPattern = (Types.PatternPrimitive | Types.PatternBase) & ObjectPatternGuard + +type ExactPatterns = { + readonly [I in keyof P]: I extends number | `${number}` ? P[I] & ExactPattern + : P[I] +} + /** * Defines a condition for matching values. * @@ -620,7 +640,7 @@ export const withReturnType: () => | Types.PatternBase, + const P extends ExactPattern, Ret, Args extends Array, Fn extends (_: Types.WhenMatch, ...args: Args) => Ret @@ -688,7 +708,7 @@ export const whenOr: < Args extends Array, Fn extends (_: Types.WhenMatch, ...args: Args) => Ret >( - ...args: [...patterns: P, f: Fn] + ...args: [...patterns: ExactPatterns, f: Fn] ) => ( self: Matcher ) => Matcher< @@ -699,7 +719,13 @@ export const whenOr: < Pr, Ret, Args -> = internal.whenOr +> = < + R, + const P extends ReadonlyArray | Types.PatternBase>, + Ret, + Args extends Array, + Fn extends (_: Types.WhenMatch, ...args: Args) => Ret +>(...args: [...patterns: ExactPatterns, f: Fn]) => internal.whenOr(...args) /** * Matches a value that satisfies all provided patterns. @@ -747,7 +773,7 @@ export const whenAnd: < Args extends Array, Fn extends (_: Types.WhenMatch>, ...args: Args) => Ret >( - ...args: [...patterns: P, f: Fn] + ...args: [...patterns: ExactPatterns, f: Fn] ) => ( self: Matcher ) => Matcher< @@ -758,7 +784,13 @@ export const whenAnd: < Pr, Ret, Args -> = internal.whenAnd +> = < + R, + const P extends ReadonlyArray | Types.PatternBase>, + Ret, + Args extends Array, + Fn extends (_: Types.WhenMatch>, ...args: Args) => Ret +>(...args: [...patterns: ExactPatterns, f: Fn]) => internal.whenAnd(...args) /** * Matches values based on a specified discriminant field. @@ -1255,7 +1287,7 @@ export const tagsExhaustive: < */ export const not: < R, - const P extends Types.PatternPrimitive | Types.PatternBase, + const P extends ExactPattern, Ret, Args extends Array, Fn extends (_: Types.NotMatch, ...args: Args) => Ret @@ -2692,6 +2724,32 @@ export declare namespace Types { type Simplify = { [K in keyof A]: A[K] } & {} + // Tuple patterns match a prefix at runtime. For inputs with a variadic part, + // narrow each position the pattern requires and keep the rest of the input. + type NarrowArrayPattern, P extends ReadonlyArray> = number extends I["length"] + ? number extends P["length"] ? MapArrayPattern + : P["length"] extends Required

["length"] ? NarrowArrayPrefix + : MapArrayPattern + : MapArrayPattern + + type MapArrayPattern = { + readonly [K in keyof I]: K extends keyof P ? ExtractAndNarrow : I[K] + } extends infer R ? Fail extends R[keyof R] ? never + : R + : never + + type NarrowArrayPrefix< + I extends ReadonlyArray, + P extends ReadonlyArray, + Out extends ReadonlyArray = readonly [] + > = P extends readonly [infer PH, ...infer PT] ? + I extends readonly [infer IH, ...infer IT] ? + [ExtractAndNarrow] extends [infer N] ? [N] extends [never] ? never + : NarrowArrayPrefix + : never + : NarrowArrayPrefix], P, Out> + : readonly [...Out, ...I] + type ExtractAndNarrow = P extends Predicate.Refinement ? _Out extends Input ? Extract<_Out, Input> : Extract : @@ -2700,12 +2758,7 @@ export declare namespace Types { : Extract : P extends Predicate.Predicate ? Extract : Input extends infer I ? Exclude< - I extends ReadonlyArray ? P extends ReadonlyArray ? { - readonly [K in keyof I]: K extends keyof P ? ExtractAndNarrow - : I[K] - } extends infer R ? Fail extends R[keyof R] ? never - : R - : never + I extends ReadonlyArray ? P extends ReadonlyArray ? NarrowArrayPattern : never : IsPlainObject extends true ? string extends keyof I ? I extends P ? I : never diff --git a/repos/effect/packages/effect/src/Metric.ts b/packages/effect/src/Metric.ts similarity index 100% rename from repos/effect/packages/effect/src/Metric.ts rename to packages/effect/src/Metric.ts diff --git a/repos/effect/packages/effect/src/MutableHashMap.ts b/packages/effect/src/MutableHashMap.ts similarity index 100% rename from repos/effect/packages/effect/src/MutableHashMap.ts rename to packages/effect/src/MutableHashMap.ts diff --git a/repos/effect/packages/effect/src/MutableHashSet.ts b/packages/effect/src/MutableHashSet.ts similarity index 100% rename from repos/effect/packages/effect/src/MutableHashSet.ts rename to packages/effect/src/MutableHashSet.ts diff --git a/repos/effect/packages/effect/src/MutableList.ts b/packages/effect/src/MutableList.ts similarity index 100% rename from repos/effect/packages/effect/src/MutableList.ts rename to packages/effect/src/MutableList.ts diff --git a/repos/effect/packages/effect/src/MutableRef.ts b/packages/effect/src/MutableRef.ts similarity index 100% rename from repos/effect/packages/effect/src/MutableRef.ts rename to packages/effect/src/MutableRef.ts diff --git a/repos/effect/packages/effect/src/Newtype.ts b/packages/effect/src/Newtype.ts similarity index 100% rename from repos/effect/packages/effect/src/Newtype.ts rename to packages/effect/src/Newtype.ts diff --git a/repos/effect/packages/effect/src/NonEmptyIterable.ts b/packages/effect/src/NonEmptyIterable.ts similarity index 100% rename from repos/effect/packages/effect/src/NonEmptyIterable.ts rename to packages/effect/src/NonEmptyIterable.ts diff --git a/repos/effect/packages/effect/src/Number.ts b/packages/effect/src/Number.ts similarity index 100% rename from repos/effect/packages/effect/src/Number.ts rename to packages/effect/src/Number.ts diff --git a/repos/effect/packages/effect/src/Optic.ts b/packages/effect/src/Optic.ts similarity index 100% rename from repos/effect/packages/effect/src/Optic.ts rename to packages/effect/src/Optic.ts diff --git a/repos/effect/packages/effect/src/Option.ts b/packages/effect/src/Option.ts similarity index 100% rename from repos/effect/packages/effect/src/Option.ts rename to packages/effect/src/Option.ts diff --git a/repos/effect/packages/effect/src/Order.ts b/packages/effect/src/Order.ts similarity index 100% rename from repos/effect/packages/effect/src/Order.ts rename to packages/effect/src/Order.ts diff --git a/repos/effect/packages/effect/src/Ordering.ts b/packages/effect/src/Ordering.ts similarity index 100% rename from repos/effect/packages/effect/src/Ordering.ts rename to packages/effect/src/Ordering.ts diff --git a/repos/effect/packages/effect/src/PartitionedSemaphore.ts b/packages/effect/src/PartitionedSemaphore.ts similarity index 100% rename from repos/effect/packages/effect/src/PartitionedSemaphore.ts rename to packages/effect/src/PartitionedSemaphore.ts diff --git a/repos/effect/packages/effect/src/Path.ts b/packages/effect/src/Path.ts similarity index 92% rename from repos/effect/packages/effect/src/Path.ts rename to packages/effect/src/Path.ts index aca7dfc35..61438f46a 100644 --- a/repos/effect/packages/effect/src/Path.ts +++ b/packages/effect/src/Path.ts @@ -14,6 +14,7 @@ import * as Context from "./Context.ts" import * as Effect from "./Effect.ts" import { identity } from "./Function.ts" import * as Layer from "./Layer.ts" +import * as Option from "./Option.ts" import { BadArgument } from "./PlatformError.ts" /** @@ -47,10 +48,17 @@ export const TypeId = "~effect/Path" * formatting, and converting file system paths. URL conversion methods return * `Effect`s because invalid file URLs or paths can fail with `BadArgument`. * + * `resolveWithin(root, input)` resolves a relative `input` beneath `root` and + * returns `Option.none()` when the result would leave the root or when `input` + * has its own root (an absolute path, or a Windows drive or UNC prefix). The + * root itself is allowed. The check is lexical: it does not follow symlinks, + * so it is not a sandbox on its own. A relative `root` is resolved against the + * current working directory, as with `resolve`. + * * **Example** (Using path operations) * * ```ts import.meta.vitest - * import { Effect, Path } from "effect" + * import { Effect, Option, Path } from "effect" * * const program = Effect.gen(function*() { * const path = yield* Path.Path @@ -64,7 +72,8 @@ export const TypeId = "~effect/Path" * isAbsolute: path.isAbsolute("/absolute/path"), * name: path.parse("/path/to/file.txt").name, * relative: path.relative("/from/path", "/to/path"), - * resolved: path.resolve("/base", "relative", "path") + * resolved: path.resolve("/base", "relative", "path"), + * within: path.resolveWithin("/base", "../outside") * } * }) * @@ -78,6 +87,7 @@ export const TypeId = "~effect/Path" * result.name // => "file" * result.relative // => "../../to/path" * result.resolved // => "/base/relative/path" + * result.within // => Option.none() * ``` * * @stability unstable @@ -98,6 +108,7 @@ export interface Path { readonly parse: (path: string) => Path.Parsed readonly relative: (from: string, to: string) => string readonly resolve: (...pathSegments: ReadonlyArray) => string + readonly resolveWithin: (root: string, input: string) => Option.Option readonly toFileUrl: (path: string) => Effect.Effect readonly toNamespacedPath: (path: string) => string } @@ -204,8 +215,7 @@ export declare namespace Path { * import { Effect, Layer, Path } from "effect" * * // Create a custom path implementation - * const customPath: Path.Path = { - * [Path.TypeId]: Path.TypeId, + * const customPath = Path.make({ * sep: "/", * basename: (path: string, suffix?: string) => { * const base = path.split("/").pop() || "" @@ -239,7 +249,7 @@ export declare namespace Path { * resolve: (...pathSegments: ReadonlyArray) => pathSegments.join("/"), * toFileUrl: (path: string) => Effect.succeed(new URL(`file://${path}`)), * toNamespacedPath: (path: string) => path - * } + * }) * * // Provide the path service * const customPathLayer = Layer.succeed(Path.Path)(customPath) @@ -259,6 +269,42 @@ export declare namespace Path { */ export const Path: Context.Service = Context.Service("effect/Path") +/** + * Creates a `Path` service from platform path operations. + * + * **When to use** + * + * Use to build a `Path` implementation, such as a platform layer or a test + * double, without implementing the operations that can be derived from the + * others. + * + * **Details** + * + * Adds the `Path` type marker and derives `resolveWithin` from the provided + * `parse`, `resolve`, `relative`, `isAbsolute`, and `sep`, so containment + * follows the same platform rules as those operations. + * + * @see {@link layer} for the built-in POSIX `Path` service layer + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make = (impl: Omit): Path => + Path.of({ + ...impl, + [TypeId]: TypeId, + resolveWithin(root, input) { + if (impl.parse(input).root !== "") return Option.none() + const base = impl.resolve(root) + const candidate = impl.resolve(base, input) + const relative = impl.relative(base, candidate) + return relative === ".." || relative.startsWith(`..${impl.sep}`) || impl.isAbsolute(relative) + ? Option.none() + : Option.some(candidate) + } + }) + /** * The following functions are adapted from the Node.js source code: * https://github.com/nodejs/node/blob/main/lib/internal/url.js @@ -479,8 +525,7 @@ function encodePathChars(filepath: string) { return filepath } -const posixImpl = Path.of({ - [TypeId]: TypeId, +const posixImpl = make({ resolve, normalize(path) { if (path.length === 0) return "." diff --git a/repos/effect/packages/effect/src/Pipeable.ts b/packages/effect/src/Pipeable.ts similarity index 100% rename from repos/effect/packages/effect/src/Pipeable.ts rename to packages/effect/src/Pipeable.ts diff --git a/repos/effect/packages/effect/src/PlatformError.ts b/packages/effect/src/PlatformError.ts similarity index 100% rename from repos/effect/packages/effect/src/PlatformError.ts rename to packages/effect/src/PlatformError.ts diff --git a/repos/effect/packages/effect/src/Pool.ts b/packages/effect/src/Pool.ts similarity index 100% rename from repos/effect/packages/effect/src/Pool.ts rename to packages/effect/src/Pool.ts diff --git a/repos/effect/packages/effect/src/Predicate.ts b/packages/effect/src/Predicate.ts similarity index 100% rename from repos/effect/packages/effect/src/Predicate.ts rename to packages/effect/src/Predicate.ts diff --git a/repos/effect/packages/effect/src/PrimaryKey.ts b/packages/effect/src/PrimaryKey.ts similarity index 100% rename from repos/effect/packages/effect/src/PrimaryKey.ts rename to packages/effect/src/PrimaryKey.ts diff --git a/repos/effect/packages/effect/src/PubSub.ts b/packages/effect/src/PubSub.ts similarity index 100% rename from repos/effect/packages/effect/src/PubSub.ts rename to packages/effect/src/PubSub.ts diff --git a/repos/effect/packages/effect/src/Pull.ts b/packages/effect/src/Pull.ts similarity index 100% rename from repos/effect/packages/effect/src/Pull.ts rename to packages/effect/src/Pull.ts diff --git a/repos/effect/packages/effect/src/Queue.ts b/packages/effect/src/Queue.ts similarity index 100% rename from repos/effect/packages/effect/src/Queue.ts rename to packages/effect/src/Queue.ts diff --git a/repos/effect/packages/effect/src/Random.ts b/packages/effect/src/Random.ts similarity index 100% rename from repos/effect/packages/effect/src/Random.ts rename to packages/effect/src/Random.ts diff --git a/repos/effect/packages/effect/src/RcMap.ts b/packages/effect/src/RcMap.ts similarity index 100% rename from repos/effect/packages/effect/src/RcMap.ts rename to packages/effect/src/RcMap.ts diff --git a/repos/effect/packages/effect/src/RcRef.ts b/packages/effect/src/RcRef.ts similarity index 100% rename from repos/effect/packages/effect/src/RcRef.ts rename to packages/effect/src/RcRef.ts diff --git a/repos/effect/packages/effect/src/Record.ts b/packages/effect/src/Record.ts similarity index 100% rename from repos/effect/packages/effect/src/Record.ts rename to packages/effect/src/Record.ts diff --git a/repos/effect/packages/effect/src/Redactable.ts b/packages/effect/src/Redactable.ts similarity index 100% rename from repos/effect/packages/effect/src/Redactable.ts rename to packages/effect/src/Redactable.ts diff --git a/repos/effect/packages/effect/src/Redacted.ts b/packages/effect/src/Redacted.ts similarity index 100% rename from repos/effect/packages/effect/src/Redacted.ts rename to packages/effect/src/Redacted.ts diff --git a/repos/effect/packages/effect/src/Reducer.ts b/packages/effect/src/Reducer.ts similarity index 100% rename from repos/effect/packages/effect/src/Reducer.ts rename to packages/effect/src/Reducer.ts diff --git a/repos/effect/packages/effect/src/Ref.ts b/packages/effect/src/Ref.ts similarity index 100% rename from repos/effect/packages/effect/src/Ref.ts rename to packages/effect/src/Ref.ts diff --git a/repos/effect/packages/effect/src/References.ts b/packages/effect/src/References.ts similarity index 100% rename from repos/effect/packages/effect/src/References.ts rename to packages/effect/src/References.ts diff --git a/repos/effect/packages/effect/src/RegExp.ts b/packages/effect/src/RegExp.ts similarity index 100% rename from repos/effect/packages/effect/src/RegExp.ts rename to packages/effect/src/RegExp.ts diff --git a/repos/effect/packages/effect/src/Request.ts b/packages/effect/src/Request.ts similarity index 100% rename from repos/effect/packages/effect/src/Request.ts rename to packages/effect/src/Request.ts diff --git a/repos/effect/packages/effect/src/RequestResolver.ts b/packages/effect/src/RequestResolver.ts similarity index 99% rename from repos/effect/packages/effect/src/RequestResolver.ts rename to packages/effect/src/RequestResolver.ts index 5df4843a8..32d6d22ad 100644 --- a/repos/effect/packages/effect/src/RequestResolver.ts +++ b/packages/effect/src/RequestResolver.ts @@ -311,16 +311,10 @@ export const makeGrouped = (options: { }) const hashGroupKey = (get: (entry: Request.Entry) => K) => { - const groupKeys = MutableHashMap.empty() - return (entry: Request.Entry): unknown => { - const key = get(entry) - const okey = MutableHashMap.get(groupKeys, key) - if (okey._tag === "Some") { - return okey.value - } - MutableHashMap.set(groupKeys, key, key) - return key - } + // Pending batches match equal keys, so they share one batch. + const batchKey = (entry: Request.Entry): unknown => get(entry) + internal.equalBatchKeys.add(batchKey) + return batchKey } /** diff --git a/repos/effect/packages/effect/src/Resource.ts b/packages/effect/src/Resource.ts similarity index 100% rename from repos/effect/packages/effect/src/Resource.ts rename to packages/effect/src/Resource.ts diff --git a/repos/effect/packages/effect/src/Result.ts b/packages/effect/src/Result.ts similarity index 100% rename from repos/effect/packages/effect/src/Result.ts rename to packages/effect/src/Result.ts diff --git a/repos/effect/packages/effect/src/Runtime.ts b/packages/effect/src/Runtime.ts similarity index 100% rename from repos/effect/packages/effect/src/Runtime.ts rename to packages/effect/src/Runtime.ts diff --git a/repos/effect/packages/effect/src/Schedule.ts b/packages/effect/src/Schedule.ts similarity index 100% rename from repos/effect/packages/effect/src/Schedule.ts rename to packages/effect/src/Schedule.ts diff --git a/repos/effect/packages/effect/src/Scheduler.ts b/packages/effect/src/Scheduler.ts similarity index 94% rename from repos/effect/packages/effect/src/Scheduler.ts rename to packages/effect/src/Scheduler.ts index 816b27084..9331d063f 100644 --- a/repos/effect/packages/effect/src/Scheduler.ts +++ b/packages/effect/src/Scheduler.ts @@ -160,6 +160,15 @@ class PriorityBuckets { * operation counts to decide when fibers should yield, and is the default * scheduler implementation. * + * **Gotchas** + * + * On Cloudflare Workers, request-owned scheduler timers can be discarded, + * leaving shared fibers suspended. Use `new MixedScheduler("sync")` for the + * whole application; see `HttpRouter.toWebHandler` for a recipe. + * Sync mode does not yield to the event loop: CPU-heavy work delays other + * requests, and I/O-polling loops can exhaust the CPU limit. It does not fix + * request-owned timers (such as `Effect.sleep`) or request-bound async contexts. + * * @stability stable * @category models * @since 2.0.0 diff --git a/repos/effect/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts similarity index 96% rename from repos/effect/packages/effect/src/Schema.ts rename to packages/effect/src/Schema.ts index 1c4a323ad..bfef2b661 100644 --- a/repos/effect/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -36,7 +36,9 @@ import * as HashMap_ from "./HashMap.ts" import * as HashSet_ from "./HashSet.ts" import * as Cookies_ from "./http/Cookies.ts" import * as Headers_ from "./http/Headers.ts" +import * as UrlParams_internal from "./http/internal/urlParams.ts" import * as UrlParams_ from "./http/UrlParams.ts" +import { NodeInspectSymbol } from "./Inspectable.ts" import * as core from "./internal/core.ts" import { effectIsExit } from "./internal/effect.ts" import * as InternalGraph from "./internal/graph.ts" @@ -56,6 +58,8 @@ import { isSchemaError as isSchemaErrorInternal, SchemaErrorTypeId } from "./int import { getStackTraceLimit, setStackTraceLimit } from "./internal/stackTraceLimit.ts" import type * as JsonPatch from "./JsonPatch.ts" import type * as JsonSchema from "./JsonSchema.ts" +import * as Dns_ from "./net/Dns.ts" +import * as Host_ from "./net/Host.ts" import * as IpInterface_ from "./net/IpInterface.ts" import * as IpNetwork_ from "./net/IpNetwork.ts" import * as NetAddress_ from "./net/NetAddress.ts" @@ -1217,6 +1221,15 @@ export class SchemaError extends Data.TaggedError("SchemaError")<{ override get message() { return SchemaIssue.defaultFormatter(this.issue) } + override toJSON() { + return { + _tag: this._tag, + message: this.message + } + } + override [NodeInspectSymbol]() { + return this.toJSON() + } override toString() { return `SchemaError(${this.message})` } @@ -4017,6 +4030,7 @@ export declare namespace StructWithRest { */ type Objects = Constraint & { readonly ast: SchemaAST.Objects + readonly Encoded: object } /** * Readonly list of record schemas that provide the additional index signatures @@ -4186,6 +4200,13 @@ export interface StructWithRest< * * **Gotchas** * + * The base schema must have an object-shaped encoded type. Whole-object + * transformations are not supported, even with an empty rest array. Apply + * transformations such as `Schema.fromJsonString` to the completed + * `StructWithRest` schema instead. Object-shaped encoded types alone do not + * guarantee compatibility: unsupported encodings are still rejected at runtime. + * Transformations on individual fields remain supported. + * * TypeScript index signatures also apply to fixed keys. `StructWithRest` does * not reject incompatible fixed fields at the call site; use * `StructWithRest.ValidateRecords` when you want an explicit type-level @@ -5946,6 +5967,9 @@ export type DecodingDefaultOptions = { function toIssueEffect( self: Effect.Effect ): Effect.Effect { + if (effectIsExit(self) && self._tag === "Success") { + return self as unknown as Exit_.Exit + } return Effect.catchCause(self, (cause) => Effect.failCauseSync(() => Cause_.map(cause, (error) => error.issue))) } /** @@ -6292,6 +6316,12 @@ type Flatten = Schemas extends readonly [infer Head, ...infer Tail] ? Head extends Union ? [...Flatten, ...Flatten] : [Head, ...Flatten] : [] +type DiscriminantKey = T extends number ? `${T}` : T +type EnsureUniqueDiscriminants, Seen = never> = Tags extends + readonly [infer Head extends PropertyKey, ...infer Tail extends ReadonlyArray] + ? [DiscriminantKey & Seen] extends [never] ? EnsureUniqueDiscriminants> + : never + : unknown type MatchCasesResult = { [K in keyof Cases]-?: NonNullable extends (...args: Array) => infer R ? R : never }[keyof Cases] @@ -6376,7 +6406,10 @@ export type toTaggedUnion< * * **Gotchas** * - * Throws if multiple members use the same discriminant property key. + * Rejects overlapping discriminant types in known member tuples at compile + * time, including nested unions. Numeric and string values such as `1` and + * `"1"` count as duplicates. Also throws at runtime if multiple members use the + * same discriminant property key. * * **Example** (Adding tagged-union utilities to an existing union) * @@ -6403,7 +6436,7 @@ export type toTaggedUnion< */ export function toTaggedUnion(tag: Tag) { return >( - self: Union + self: Union & NoInfer["discriminants"]>> ): toTaggedUnion => { const cases: Record = {} const discriminants: Array = [] @@ -7877,7 +7910,8 @@ export function isMultipleOf( * * JSON Schema: * - * This check corresponds to the `type: "integer"` constraint in JSON Schema. + * This check corresponds to `type: "integer"` with `minimum: Number.MIN_SAFE_INTEGER` + * and `maximum: Number.MAX_SAFE_INTEGER` in JSON Schema. * * Arbitrary: * @@ -7896,7 +7930,11 @@ export function isInt(annotations?: Annotations.Filter) { id: "effect/schema/isInt", payload: null }, - toJsonSchema: () => [{ type: "integer" }, true], + toJsonSchema: () => ({ + type: "integer", + minimum: globalThis.Number.MIN_SAFE_INTEGER, + maximum: globalThis.Number.MAX_SAFE_INTEGER + }), toCode: () => ({ runtime: "Schema.isInt()" }), arbitraryConstraint: { number: "integer" @@ -11978,10 +12016,10 @@ export const DateTimeZonedFromString: DateTimeZonedFromString = DateTimeZonedStr decodeTo(DateTimeZoned, dateTimeZonedFromString) ) -const netAddressFromString = , E extends { readonly message: string }>( +const netAddressFromString = ( declaration: S, - parse: (input: string) => Result_.Result, - encode: (value: S["Type"]) => string, + parse: (input: string) => Result_.Result, + encode: (value: S["Encoded"]) => string, identifier: string ) => String.pipe(decodeTo( @@ -13332,6 +13370,229 @@ export const SocketAddress: SocketAddress = declare(NetAddress_.isSocketAddress, identifier: "SocketAddress" }) +/** + * Type-level representation of {@link DomainName}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DomainName extends brand { + readonly "Rebuild": DomainName +} + +/** + * Schema for normalized DNS domain names. + * + * **Details** + * + * Only names already in normalized form (lowercase ASCII, with + * internationalized labels in `xn--` form) are accepted. Use + * {@link DomainNameFromString} to normalize arbitrary input. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DomainName: DomainName = String.check( + makeFilter(Host_.isDomainName, { expected: "a normalized domain name" }) +).pipe(brand("~effect/net/Host/DomainName")).annotate({ identifier: "DomainName" }) + +/** + * Type-level representation of {@link DomainNameFromString}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DomainNameFromString extends decodeTo { + readonly "Rebuild": DomainNameFromString +} + +/** + * Schema for DNS domain names decoded from strings, normalizing case and + * converting internationalized names to their ASCII form. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DomainNameFromString: DomainNameFromString = netAddressFromString( + DomainName, + Host_.domainNameFromString, + (name) => name, + "DomainNameFromString" +) + +/** + * Type-level representation of {@link ScopedIpv6Literal}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface ScopedIpv6Literal extends brand { + readonly "Rebuild": ScopedIpv6Literal +} + +/** + * Schema for canonical scoped IPv6 literals such as `fe80::1%eth0`. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const ScopedIpv6Literal: ScopedIpv6Literal = String.check( + makeFilter(NetAddress_.isScopedIpv6Literal, { expected: "a canonical scoped IPv6 literal" }) +).pipe(brand("~effect/net/NetAddress/ScopedIpv6Literal")).annotate({ identifier: "ScopedIpv6Literal" }) + +/** + * Type-level representation of {@link ScopedIpv6LiteralFromString}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface ScopedIpv6LiteralFromString extends decodeTo { + readonly "Rebuild": ScopedIpv6LiteralFromString +} + +/** + * Schema for scoped IPv6 literals decoded from strings, normalizing the address + * and numeric zones. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const ScopedIpv6LiteralFromString: ScopedIpv6LiteralFromString = netAddressFromString( + ScopedIpv6Literal, + NetAddress_.scopedIpv6LiteralFromString, + (literal) => literal, + "ScopedIpv6LiteralFromString" +) + +/** + * Type-level representation of {@link Host}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Host extends Union { + readonly "Rebuild": Host +} + +/** + * Schema for hosts: numeric IP addresses, scoped IPv6 literals, or domain names. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const Host: Host = Union([IpAddress, ScopedIpv6Literal, DomainName]).annotate({ identifier: "Host" }) + +/** + * Type-level representation of {@link HostFromString}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface HostFromString + extends Union +{ + readonly "Rebuild": HostFromString +} + +/** + * Schema for hosts decoded from strings. Numeric IP strings decode to IP + * addresses; other strings decode to scoped IPv6 literals or domain names. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const HostFromString: HostFromString = Union([ + IpAddressFromString, + ScopedIpv6LiteralFromString, + DomainNameFromString +]).annotate({ identifier: "HostFromString" }) + +/** + * Type-level representation of {@link HostPort}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface HostPort extends declare { + readonly "Rebuild": HostPort +} + +/** + * Schema for already-constructed unresolved host and port values. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const HostPort: HostPort = declare(Host_.isHostPort, { + identifier: "HostPort" +}) + +/** + * Type-level representation of {@link HostPortFromString}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface HostPortFromString extends decodeTo { + readonly "Rebuild": HostPortFromString +} + +/** + * Schema for unresolved endpoints encoded as `host:port` or `[IPv6]:port` + * strings. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const HostPortFromString: HostPortFromString = netAddressFromString( + HostPort, + Host_.hostPortFromString, + Host_.formatHostPort, + "HostPortFromString" +) + +/** + * Type-level representation of {@link Port}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Port extends Int { + readonly "Rebuild": Port +} + +/** + * Schema for TCP and UDP port numbers from 0 through 65535. + * + * **Details** + * + * Port 0 is accepted because it asks the operating system to choose a port + * when binding, and it matches the port range of `InetAddress` and `HostPort`. + * `Config.Port` accepts only 1 through 65535. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const Port: Port = Int.check(isBetween({ minimum: 0, maximum: 65535 }, { expected: "a port number" })) + // ----------------------------------------------------------------------------- // Duration schemas // ----------------------------------------------------------------------------- @@ -13549,6 +13810,426 @@ export const DurationFromMillis: DurationFromMillis = Number.pipe( decodeTo(Duration, durationFromMillis) ) +// ----------------------------------------------------------------------------- +// DNS schemas +// ----------------------------------------------------------------------------- + +/** + * Type-level representation of {@link DnsRecordType}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsRecordType extends Literals> {} + +/** + * Schema for supported DNS record types such as `"A"` or `"SRV"`. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsRecordType: DnsRecordType = Literals(Dns_.recordTypes) + +// Addresses in JSON records must already be canonical; normalizing input is +// left to the `*FromString` schemas. +const dnsCanonicalAddress = ( + schema: Codec, + parse: (input: string) => Result_.Result +) => + String.pipe(decodeTo( + schema, + SchemaTransformation.transformEffect({ + decode: (input, options) => { + const result = parse(input) + if (Result_.isFailure(result)) { + return Effect.fail(new SchemaIssue.InvalidValue({ message: result.failure.message }, input, options)) + } + return NetAddress_.formatIp(result.success) === input + ? Effect.succeed(result.success) + : Effect.fail(new SchemaIssue.InvalidValue({ message: "expected a canonical address" }, input, options)) + }, + encode: (address) => Effect.succeed(NetAddress_.formatIp(address)) + }) + )) + +const dnsUint8 = Int.check(isBetween({ minimum: 0, maximum: 0xff })) + +const dnsUint16 = Int.check(isBetween({ minimum: 0, maximum: 0xffff })) + +const dnsUint32 = Int.check(isBetween({ minimum: 0, maximum: 0xffffffff })) + +// SOA timers are written as whole seconds, as in zone files. +const dnsSeconds = dnsUint32.pipe(decodeTo( + Duration, + SchemaTransformation.transform({ decode: Duration_.seconds, encode: Duration_.toSeconds }) +)) + +// Each record type is an opaque value whose JSON form is a plain object tagged +// by record type, such as `{ "_tag": "MX", "exchange": "mail.example.com", "priority": 10 }`. +const dnsRecordOfType = ( + type: T, + fields: { readonly [K in keyof Dns_.RecordFields]-?: Codec[K], unknown> } +) => + declare((u): u is Dns_.RecordFor => Dns_.isDnsRecord(u) && u._tag === type, { + identifier: `Dns${type.charAt(0)}${type.slice(1).toLowerCase()}Record`, + toCodecJson: () => { + const json = TaggedStruct(type, fields) + return link>()( + json, + SchemaTransformation.transformEffect({ + decode: (input, options) => { + const { _tag, ...rest } = input as { readonly _tag: T } + const result = Dns_.makeRecord(type, rest as Dns_.RecordFields) + return Result_.isSuccess(result) + ? Effect.succeed(result.success) + : Effect.fail(new SchemaIssue.InvalidValue({ message: result.failure.message }, input, options)) + }, + // Records hold only their tag and fields as own properties. + encode: (record) => Effect.succeed({ ...record } as unknown as typeof json["Type"]) + }) + ) + } + }) + +/** + * Type-level representation of {@link DnsARecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsARecord extends declare { + readonly "Rebuild": DnsARecord +} + +/** + * Schema for already-constructed IPv4 address (`A`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsARecord: DnsARecord = dnsRecordOfType("A", { + address: dnsCanonicalAddress(Ipv4Address, NetAddress_.ipv4FromString) +}) + +/** + * Type-level representation of {@link DnsAaaaRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsAaaaRecord extends declare { + readonly "Rebuild": DnsAaaaRecord +} + +/** + * Schema for already-constructed IPv6 address (`AAAA`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsAaaaRecord: DnsAaaaRecord = dnsRecordOfType("AAAA", { + address: dnsCanonicalAddress(Ipv6Address, NetAddress_.ipv6FromString) +}) + +/** + * Type-level representation of {@link DnsCaaRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsCaaRecord extends declare { + readonly "Rebuild": DnsCaaRecord +} + +/** + * Schema for already-constructed certification authority authorization (`CAA`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsCaaRecord: DnsCaaRecord = dnsRecordOfType("CAA", { + critical: Boolean, + tag: String.check(isPattern(/^[a-z0-9]+$/i)), + value: String +}) + +/** + * Type-level representation of {@link DnsCnameRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsCnameRecord extends declare { + readonly "Rebuild": DnsCnameRecord +} + +/** + * Schema for already-constructed canonical name (`CNAME`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsCnameRecord: DnsCnameRecord = dnsRecordOfType("CNAME", { target: DomainName }) + +/** + * Type-level representation of {@link DnsMxRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsMxRecord extends declare { + readonly "Rebuild": DnsMxRecord +} + +/** + * Schema for already-constructed mail exchange (`MX`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsMxRecord: DnsMxRecord = dnsRecordOfType("MX", { exchange: DomainName, priority: dnsUint16 }) + +/** + * Type-level representation of {@link DnsNaptrRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsNaptrRecord extends declare { + readonly "Rebuild": DnsNaptrRecord +} + +/** + * Schema for already-constructed naming authority pointer (`NAPTR`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsNaptrRecord: DnsNaptrRecord = dnsRecordOfType("NAPTR", { + order: dnsUint16, + preference: dnsUint16, + flags: String, + service: String, + regexp: String, + replacement: DomainName +}) + +/** + * Type-level representation of {@link DnsNsRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsNsRecord extends declare { + readonly "Rebuild": DnsNsRecord +} + +/** + * Schema for already-constructed name server (`NS`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsNsRecord: DnsNsRecord = dnsRecordOfType("NS", { host: DomainName }) + +/** + * Type-level representation of {@link DnsPtrRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsPtrRecord extends declare { + readonly "Rebuild": DnsPtrRecord +} + +/** + * Schema for already-constructed pointer (`PTR`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsPtrRecord: DnsPtrRecord = dnsRecordOfType("PTR", { host: NonEmptyString }) + +/** + * Type-level representation of {@link DnsSoaRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsSoaRecord extends declare { + readonly "Rebuild": DnsSoaRecord +} + +/** + * Schema for already-constructed start of authority (`SOA`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsSoaRecord: DnsSoaRecord = dnsRecordOfType("SOA", { + primary: DomainName, + admin: NonEmptyString, + serial: dnsUint32, + refresh: dnsSeconds, + retry: dnsSeconds, + expire: dnsSeconds, + minimum: dnsSeconds +}) + +/** + * Type-level representation of {@link DnsSrvRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsSrvRecord extends declare { + readonly "Rebuild": DnsSrvRecord +} + +/** + * Schema for already-constructed service locator (`SRV`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsSrvRecord: DnsSrvRecord = dnsRecordOfType("SRV", { + target: DomainName, + port: dnsUint16, + priority: dnsUint16, + weight: dnsUint16 +}) + +/** + * Type-level representation of {@link DnsTlsaRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsTlsaRecord extends declare { + readonly "Rebuild": DnsTlsaRecord +} + +/** + * Schema for already-constructed TLS authentication (`TLSA`) DNS records. + * + * **Details** + * + * The default JSON serializer encodes the certificate association `data` as a + * hex string. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsTlsaRecord: DnsTlsaRecord = dnsRecordOfType("TLSA", { + certUsage: dnsUint8, + selector: dnsUint8, + matchingType: dnsUint8, + data: Uint8ArrayFromHex +}) + +/** + * Type-level representation of {@link DnsTxtRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsTxtRecord extends declare { + readonly "Rebuild": DnsTxtRecord +} + +/** + * Schema for already-constructed text (`TXT`) DNS records. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsTxtRecord: DnsTxtRecord = dnsRecordOfType("TXT", { chunks: NonEmptyArray(String) }) + +/** + * Type-level representation of {@link DnsRecord}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DnsRecord extends + Union< + readonly [ + DnsARecord, + DnsAaaaRecord, + DnsCaaRecord, + DnsCnameRecord, + DnsMxRecord, + DnsNaptrRecord, + DnsNsRecord, + DnsPtrRecord, + DnsSoaRecord, + DnsSrvRecord, + DnsTlsaRecord, + DnsTxtRecord + ] + > +{ + readonly "Rebuild": DnsRecord +} + +/** + * Schema for already-constructed DNS records of any supported record type. + * + * **Details** + * + * The default JSON serializer encodes records as objects tagged by record type, + * with addresses and names as canonical strings, SOA timers as whole seconds, + * and TLSA data as hex, such as + * `{ "_tag": "SRV", "target": "db.internal", "port": 5432, "priority": 10, "weight": 5 }`. + * Decoding accepts only canonical addresses and normalized domain names, and + * reports invalid fields with their path. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const DnsRecord: DnsRecord = Union([ + DnsARecord, + DnsAaaaRecord, + DnsCaaRecord, + DnsCnameRecord, + DnsMxRecord, + DnsNaptrRecord, + DnsNsRecord, + DnsPtrRecord, + DnsSoaRecord, + DnsSrvRecord, + DnsTlsaRecord, + DnsTxtRecord +]).annotate({ identifier: "DnsRecord" }) + // ----------------------------------------------------------------------------- // Exit schemas // ----------------------------------------------------------------------------- @@ -14553,9 +15234,9 @@ export const UrlParams: UrlParams = declare( toCodec: () => link()( ArraySchema(Tuple([String, String])), - SchemaTransformation.transform({ + SchemaTransformation.transform>({ decode: UrlParams_.make, - encode: (self) => self.params + encode: (self) => self.params.map(([key, value]) => [key, UrlParams_internal.unwrap(value)]) }) ) } @@ -15141,6 +15822,10 @@ export interface Class { .map((part) => (Arr.isCanonicalArrayIndex(part) ? globalThis.Number(part) : part)) } +const MAX_ARRAY_HOLES = 20 + /** * Builds a nested tree object from a list of bracket-path entries. * @@ -2120,6 +2122,10 @@ function bracketPathToTokens(bracketPath: string): Array { * - `""` → real empty key * - Numeric bracket segments become array indices only when they are valid * JavaScript array-index strings; otherwise they remain object keys. + * - An indexed container becomes an array only when it has at most 20 holes + * (missing indices below its highest index). Otherwise it is returned as an + * object keyed by the index strings, so untrusted input such as + * `a[4294967294]` cannot produce huge sparse arrays. * - Duplicate keys for the same path are merged into arrays. * - If a structural path conflicts with a previous leaf or a different container * type, the later structural path replaces the conflicting value. @@ -2152,43 +2158,31 @@ export function makeTreeRecord( const out: any = {} const containers = new WeakSet() const duplicates = new WeakSet() - - function getOrCreateContainer(self: any, key: PropertyKey, shouldBeArray: boolean): any { - const current = Object.hasOwn(self, key) ? self[key] : undefined - if (containers.has(current) && Array.isArray(current) === shouldBeArray) { - return current - } - const container = shouldBeArray ? [] : {} - containers.add(container) - InternalRecord.assignProperty(self, key, container) - return container - } + // Indexed containers are built as plain objects so that large indices never + // allocate sparse arrays, and are materialized once all entries are applied + const indexed = new Map() bracketPathEntries.forEach(([key, value]) => { const tokens = bracketPathToTokens(key) let cur: any = out - tokens.forEach((token, i) => { - const isLast = i === tokens.length - 1 - - // We are inside an array and see "[]" (empty token) => append - if (Array.isArray(cur) && token === "") { - if (isLast) { - cur.push(value) - } else { - // bracket path: "foo[][bar]" => push a new element and descend into it - const next = tokens[i + 1] - const shouldBeArray = typeof next === "number" || next === "" - const index = cur.length - cur = getOrCreateContainer(cur, index, shouldBeArray) - } - } else if (isLast) { - // If we're setting a value at a path that already exists - // convert it to an array to support multiple values for the same key - const hasOwn = Object.hasOwn(cur, token) - if (hasOwn && Array.isArray(cur[token]) && (containers.has(cur[token]) || duplicates.has(cur[token]))) { - cur[token].push(value) + for (let i = 0; i < tokens.length; i++) { + const state = indexed.get(cur) + // Resolve "[]" to the next index before handling leaves or containers + const token = tokens[i] === "" && state !== undefined ? state.length : tokens[i] + if (state !== undefined && typeof token === "number") { + state.length = Math.max(state.length, token + 1) + } + const hasOwn = Object.hasOwn(cur, token) + const current = hasOwn ? cur[token] : undefined + + if (i === tokens.length - 1) { + const currentState = indexed.get(current) + if (currentState !== undefined) { + InternalRecord.assignProperty(current, currentState.length++, value) + } else if (duplicates.has(current)) { + current.push(value) } else if (hasOwn) { - const values = [cur[token], value] + const values = [current, value] duplicates.add(values) InternalRecord.assignProperty(cur, token, values) } else { @@ -2196,12 +2190,30 @@ export function makeTreeRecord( } } else { const next = tokens[i + 1] - // if next is a number OR "" (from []), we are building an array const shouldBeArray = typeof next === "number" || next === "" - cur = getOrCreateContainer(cur, token, shouldBeArray) + if (containers.has(current) && indexed.has(current) === shouldBeArray) { + cur = current + } else { + const container = {} + containers.add(container) + if (shouldBeArray) { + indexed.set(container, { parent: cur, key: token, length: 0 }) + } + InternalRecord.assignProperty(cur, token, container) + cur = container + } } - }) + } }) + + // Children are created after their parents, so materializing in reverse + // creation order replaces every nested container before its parent is copied + for (const [builder, { key, length, parent }] of Array.from(indexed).reverse()) { + // A later conflicting path may have replaced the container in its parent + if (parent[key] === builder && length - Object.keys(builder).length <= MAX_ARRAY_HOLES) { + InternalRecord.assignProperty(parent, key, Object.assign(new Array(length), builder)) + } + } return out } diff --git a/repos/effect/packages/effect/src/SchemaIssue.ts b/packages/effect/src/SchemaIssue.ts similarity index 100% rename from repos/effect/packages/effect/src/SchemaIssue.ts rename to packages/effect/src/SchemaIssue.ts diff --git a/repos/effect/packages/effect/src/SchemaParser.ts b/packages/effect/src/SchemaParser.ts similarity index 100% rename from repos/effect/packages/effect/src/SchemaParser.ts rename to packages/effect/src/SchemaParser.ts diff --git a/repos/effect/packages/effect/src/SchemaRepresentation.ts b/packages/effect/src/SchemaRepresentation.ts similarity index 100% rename from repos/effect/packages/effect/src/SchemaRepresentation.ts rename to packages/effect/src/SchemaRepresentation.ts diff --git a/repos/effect/packages/effect/src/SchemaTransformation.ts b/packages/effect/src/SchemaTransformation.ts similarity index 100% rename from repos/effect/packages/effect/src/SchemaTransformation.ts rename to packages/effect/src/SchemaTransformation.ts diff --git a/repos/effect/packages/effect/src/Scope.ts b/packages/effect/src/Scope.ts similarity index 100% rename from repos/effect/packages/effect/src/Scope.ts rename to packages/effect/src/Scope.ts diff --git a/repos/effect/packages/effect/src/ScopedCache.ts b/packages/effect/src/ScopedCache.ts similarity index 100% rename from repos/effect/packages/effect/src/ScopedCache.ts rename to packages/effect/src/ScopedCache.ts diff --git a/repos/effect/packages/effect/src/ScopedRef.ts b/packages/effect/src/ScopedRef.ts similarity index 100% rename from repos/effect/packages/effect/src/ScopedRef.ts rename to packages/effect/src/ScopedRef.ts diff --git a/repos/effect/packages/effect/src/Semaphore.ts b/packages/effect/src/Semaphore.ts similarity index 100% rename from repos/effect/packages/effect/src/Semaphore.ts rename to packages/effect/src/Semaphore.ts diff --git a/repos/effect/packages/effect/src/Sink.ts b/packages/effect/src/Sink.ts similarity index 97% rename from repos/effect/packages/effect/src/Sink.ts rename to packages/effect/src/Sink.ts index 0074c1290..138e26d8e 100644 --- a/repos/effect/packages/effect/src/Sink.ts +++ b/packages/effect/src/Sink.ts @@ -1825,13 +1825,24 @@ export const takeWhileFilterEffect = ( * @since 4.0.0 */ export const takeUntil = (predicate: Predicate): Sink, In, In> => - suspend(() => { - let done = false - return takeWhile((i) => { - if (done) return false - done = predicate(i) - return true - }) + fromTransform((upstream) => { + const out = Arr.empty() + return upstream.pipe( + Effect.flatMap((arr) => { + for (let i = 0; i < arr.length; i++) { + out.push(arr[i]) + if (predicate(arr[i])) { + const leftover: NonEmptyReadonlyArray | undefined = (i + 1) < arr.length + ? arr.slice(i + 1) as Arr.NonEmptyArray + : undefined + return Cause.done([out, leftover] as const) + } + } + return Effect.void + }), + Effect.forever({ disableYield: true }), + Pull.catchDone((end) => Effect.succeed, In>>(end ?? [out])) + ) }) /** @@ -1849,17 +1860,33 @@ export const takeUntil = (predicate: Predicate): Sink, In, In> export const takeUntilEffect = ( predicate: (input: In) => Effect.Effect ): Sink, In, In, E, R> => - suspend(() => { - let done = false - return takeWhileEffect((input) => { - if (done) { - return Effect.succeed(false) - } - return Effect.map(predicate(input), (b) => { - done = b - return true - }) - }) + fromTransform((upstream) => { + const out = Arr.empty() + let leftover: NonEmptyReadonlyArray | undefined = undefined + return upstream.pipe( + Effect.flatMap((arr) => { + let i = 0 + return Effect.whileLoop({ + while: () => i < arr.length, + body: () => { + const input = arr[i++] + return Effect.flatMap(predicate(input), (matched) => { + out.push(input) + if (!matched) { + return Effect.void + } + if (i < arr.length) { + leftover = arr.slice(i) as Arr.NonEmptyArray + } + return Cause.done() + }) + }, + step: constVoid + }) + }), + Effect.forever({ disableYield: true }), + Pull.catchDone(() => Effect.succeed([out, leftover] as const)) + ) }) /** diff --git a/repos/effect/packages/effect/src/StandardSchema.ts b/packages/effect/src/StandardSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/StandardSchema.ts rename to packages/effect/src/StandardSchema.ts diff --git a/repos/effect/packages/effect/src/Stdio.ts b/packages/effect/src/Stdio.ts similarity index 95% rename from repos/effect/packages/effect/src/Stdio.ts rename to packages/effect/src/Stdio.ts index 858e533ea..448339061 100644 --- a/repos/effect/packages/effect/src/Stdio.ts +++ b/packages/effect/src/Stdio.ts @@ -79,6 +79,12 @@ export interface Stdio { * @since 4.0.0 */ readonly stdoutIsTerminal: Effect.Effect + /** + * Whether standard error is attached to a terminal. + * + * @since 4.0.0 + */ + readonly stderrIsTerminal: Effect.Effect stdout(options?: { readonly endOnDone?: boolean | undefined }): Sink.Sink @@ -127,12 +133,13 @@ export const Stdio: Context.Service = Context.Service(TypeI */ export const make = ( options: - & Omit - & Partial> + & Omit + & Partial> ): Stdio => ({ [TypeId]: TypeId, stdinIsTerminal: Effect.succeed(false), stdoutIsTerminal: Effect.succeed(false), + stderrIsTerminal: Effect.succeed(false), ...options }) diff --git a/repos/effect/packages/effect/src/Stream.ts b/packages/effect/src/Stream.ts similarity index 99% rename from repos/effect/packages/effect/src/Stream.ts rename to packages/effect/src/Stream.ts index a6d1bcdce..e102f5f57 100644 --- a/repos/effect/packages/effect/src/Stream.ts +++ b/packages/effect/src/Stream.ts @@ -11542,6 +11542,9 @@ export const mkUint8Array = (self: Stream): Effect.Effec * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * + * When `signal` aborts, the stream is interrupted even if the reader has + * stopped pulling, and the `ReadableStream` closes. + * * **Example** (Converting to a ReadableStream with services) * * ```ts import.meta.vitest @@ -11549,7 +11552,7 @@ export const mkUint8Array = (self: Stream): Effect.Effec * * const stream = Stream.make(1, 2, 3, 4, 5) * const readableStream = Stream.toReadableStreamWith(stream, Context.empty()) - * const values = await Array.fromAsync(readableStream) + * const values = await globalThis.Array.fromAsync(readableStream) * values // => [ 1, 2, 3, 4, 5 ] * ``` * @@ -11560,23 +11563,36 @@ export const mkUint8Array = (self: Stream): Effect.Effec export const toReadableStreamWith: { ( context: Context.Context, - options?: { readonly strategy?: QueuingStrategy | undefined } + options?: { + readonly strategy?: QueuingStrategy | undefined + readonly signal?: AbortSignal | undefined + } ): (self: Stream) => ReadableStream ( self: Stream, context: Context.Context, - options?: { readonly strategy?: QueuingStrategy | undefined } + options?: { + readonly strategy?: QueuingStrategy | undefined + readonly signal?: AbortSignal | undefined + } ): ReadableStream } = dual( (args) => isStream(args[0]), ( self: Stream, context: Context.Context, - options?: { readonly strategy?: QueuingStrategy | undefined } + options?: { + readonly strategy?: QueuingStrategy | undefined + readonly signal?: AbortSignal | undefined + } ): ReadableStream => { let currentResolve: (() => void) | undefined = undefined let fiber: Fiber.Fiber | undefined = undefined + let cancelled = false const latch = Latch.makeUnsafe(false) + const signal = options?.signal + // Interrupting the fiber directly also stops a consumer waiting for a pull. + const onAbort = () => fiber!.interruptUnsafe() return new ReadableStream({ start(controller) { @@ -11593,12 +11609,18 @@ export const toReadableStreamWith: { context )) fiber.addObserver((exit) => { - if (exit._tag === "Failure") { + signal?.removeEventListener("abort", onAbort) + if (cancelled) return + if (exit._tag === "Failure" && signal?.aborted !== true) { controller.error(Cause.squash(exit.cause)) } else { controller.close() } }) + if (signal !== undefined && fiber.pollUnsafe() === undefined) { + if (signal.aborted) onAbort() + else signal.addEventListener("abort", onAbort, { once: true }) + } }, pull() { return new Promise((resolve) => { @@ -11608,6 +11630,7 @@ export const toReadableStreamWith: { }, cancel() { if (!fiber) return + cancelled = true return Effect.runPromise(Effect.asVoid(Fiber.interrupt(fiber))) } }, options?.strategy) @@ -11627,7 +11650,7 @@ export const toReadableStreamWith: { * import { Stream } from "effect" * * const readableStream = Stream.toReadableStream(Stream.make(1, 2, 3)) - * const values = await Array.fromAsync(readableStream) + * const values = await globalThis.Array.fromAsync(readableStream) * values // => [ 1, 2, 3 ] * ``` * @@ -11722,7 +11745,7 @@ export const toReadableStreamEffect: { * const stream = Stream.make(1, 2, 3) * const iterable = Stream.toAsyncIterableWith(stream, Context.empty()) * - * await Array.fromAsync(iterable) // => [1, 2, 3] + * await globalThis.Array.fromAsync(iterable) // => [1, 2, 3] * ``` * * @stability stable @@ -11829,7 +11852,7 @@ export const toAsyncIterableWith: { * * const program = Effect.gen(function*() { * const iterable = yield* Stream.toAsyncIterableEffect(stream) - * return yield* Effect.promise(() => Array.fromAsync(iterable)) + * return yield* Effect.promise(() => globalThis.Array.fromAsync(iterable)) * }) * * await Effect.runPromise(program) // => [1, 2, 3] @@ -11855,7 +11878,7 @@ export const toAsyncIterableEffect = (self: Stream): Effect.Ef * * const stream = Stream.make(1, 2, 3) * - * await Array.fromAsync(Stream.toAsyncIterable(stream)) // => [1, 2, 3] + * await globalThis.Array.fromAsync(Stream.toAsyncIterable(stream)) // => [1, 2, 3] * ``` * * @stability stable diff --git a/repos/effect/packages/effect/src/String.ts b/packages/effect/src/String.ts similarity index 97% rename from repos/effect/packages/effect/src/String.ts rename to packages/effect/src/String.ts index 2391f1797..b4bb0117f 100644 --- a/repos/effect/packages/effect/src/String.ts +++ b/packages/effect/src/String.ts @@ -1079,6 +1079,12 @@ export const stripMargin = (self: string): string => stripMarginWith(self, "|") /** * Converts a snake_case string to camelCase. * + * **Details** + * + * Keeps the first character, then replaces each later underscore and the + * character after it with that character uppercased. A trailing underscore is + * dropped. Input made only of an even number of underscores returns `""`. + * * **Example** (Converting snake_case to camelCase) * * ```ts import.meta.vitest @@ -1093,10 +1099,10 @@ export const stripMargin = (self: string): string => stripMarginWith(self, "|") * @since 2.0.0 */ export const snakeToCamel = (self: string): string => { - if (self.length === 0) return self + if (self.length === 0 || (self.length % 2 === 0 && self[0] === "_" && /^_+$/.test(self))) return "" let str = self[0] for (let i = 1; i < self.length; i++) { - str += self[i] === "_" ? self[++i].toUpperCase() : self[i] + str += self[i] === "_" ? self.charAt(++i).toUpperCase() : self[i] } return str } @@ -1104,6 +1110,12 @@ export const snakeToCamel = (self: string): string => { /** * Converts a snake_case string to PascalCase. * + * **Details** + * + * Uppercases the first character, then replaces each later underscore and the + * character after it with that character uppercased. A trailing underscore is + * dropped. Input made only of an even number of underscores returns `""`. + * * **Example** (Converting snake_case to PascalCase) * * ```ts import.meta.vitest @@ -1118,10 +1130,10 @@ export const snakeToCamel = (self: string): string => { * @since 2.0.0 */ export const snakeToPascal = (self: string): string => { - if (self.length === 0) return self + if (self.length === 0 || (self.length % 2 === 0 && self[0] === "_" && /^_+$/.test(self))) return "" let str = self[0].toUpperCase() for (let i = 1; i < self.length; i++) { - str += self[i] === "_" ? self[++i].toUpperCase() : self[i] + str += self[i] === "_" ? self.charAt(++i).toUpperCase() : self[i] } return str } @@ -1334,6 +1346,7 @@ const normalizeCase = ( while (result.charAt(end - 1) === "\0") { end-- } + if (start >= end) return "" // Transform each token independently. return result.slice(start, end).split("\0").map(transform).join(delimiter) diff --git a/repos/effect/packages/effect/src/Struct.ts b/packages/effect/src/Struct.ts similarity index 100% rename from repos/effect/packages/effect/src/Struct.ts rename to packages/effect/src/Struct.ts diff --git a/repos/effect/packages/effect/src/SubscriptionRef.ts b/packages/effect/src/SubscriptionRef.ts similarity index 100% rename from repos/effect/packages/effect/src/SubscriptionRef.ts rename to packages/effect/src/SubscriptionRef.ts diff --git a/repos/effect/packages/effect/src/Symbol.ts b/packages/effect/src/Symbol.ts similarity index 100% rename from repos/effect/packages/effect/src/Symbol.ts rename to packages/effect/src/Symbol.ts diff --git a/repos/effect/packages/effect/src/SynchronizedRef.ts b/packages/effect/src/SynchronizedRef.ts similarity index 100% rename from repos/effect/packages/effect/src/SynchronizedRef.ts rename to packages/effect/src/SynchronizedRef.ts diff --git a/repos/effect/packages/effect/src/Take.ts b/packages/effect/src/Take.ts similarity index 100% rename from repos/effect/packages/effect/src/Take.ts rename to packages/effect/src/Take.ts diff --git a/repos/effect/packages/effect/src/Terminal.ts b/packages/effect/src/Terminal.ts similarity index 100% rename from repos/effect/packages/effect/src/Terminal.ts rename to packages/effect/src/Terminal.ts diff --git a/repos/effect/packages/effect/src/Tracer.ts b/packages/effect/src/Tracer.ts similarity index 100% rename from repos/effect/packages/effect/src/Tracer.ts rename to packages/effect/src/Tracer.ts diff --git a/repos/effect/packages/effect/src/Trie.ts b/packages/effect/src/Trie.ts similarity index 100% rename from repos/effect/packages/effect/src/Trie.ts rename to packages/effect/src/Trie.ts diff --git a/repos/effect/packages/effect/src/Tuple.ts b/packages/effect/src/Tuple.ts similarity index 100% rename from repos/effect/packages/effect/src/Tuple.ts rename to packages/effect/src/Tuple.ts diff --git a/repos/effect/packages/effect/src/TxChunk.ts b/packages/effect/src/TxChunk.ts similarity index 100% rename from repos/effect/packages/effect/src/TxChunk.ts rename to packages/effect/src/TxChunk.ts diff --git a/repos/effect/packages/effect/src/TxDeferred.ts b/packages/effect/src/TxDeferred.ts similarity index 100% rename from repos/effect/packages/effect/src/TxDeferred.ts rename to packages/effect/src/TxDeferred.ts diff --git a/repos/effect/packages/effect/src/TxHashMap.ts b/packages/effect/src/TxHashMap.ts similarity index 100% rename from repos/effect/packages/effect/src/TxHashMap.ts rename to packages/effect/src/TxHashMap.ts diff --git a/repos/effect/packages/effect/src/TxHashSet.ts b/packages/effect/src/TxHashSet.ts similarity index 100% rename from repos/effect/packages/effect/src/TxHashSet.ts rename to packages/effect/src/TxHashSet.ts diff --git a/repos/effect/packages/effect/src/TxPriorityQueue.ts b/packages/effect/src/TxPriorityQueue.ts similarity index 100% rename from repos/effect/packages/effect/src/TxPriorityQueue.ts rename to packages/effect/src/TxPriorityQueue.ts diff --git a/repos/effect/packages/effect/src/TxPubSub.ts b/packages/effect/src/TxPubSub.ts similarity index 100% rename from repos/effect/packages/effect/src/TxPubSub.ts rename to packages/effect/src/TxPubSub.ts diff --git a/repos/effect/packages/effect/src/TxQueue.ts b/packages/effect/src/TxQueue.ts similarity index 100% rename from repos/effect/packages/effect/src/TxQueue.ts rename to packages/effect/src/TxQueue.ts diff --git a/repos/effect/packages/effect/src/TxReentrantLock.ts b/packages/effect/src/TxReentrantLock.ts similarity index 100% rename from repos/effect/packages/effect/src/TxReentrantLock.ts rename to packages/effect/src/TxReentrantLock.ts diff --git a/repos/effect/packages/effect/src/TxRef.ts b/packages/effect/src/TxRef.ts similarity index 97% rename from repos/effect/packages/effect/src/TxRef.ts rename to packages/effect/src/TxRef.ts index 7f9b4c44e..2819ff57f 100644 --- a/repos/effect/packages/effect/src/TxRef.ts +++ b/packages/effect/src/TxRef.ts @@ -13,6 +13,7 @@ * @since 4.0.0 */ import * as Effect from "./Effect.ts" +import type { TransactionMetaInner } from "./Effect.ts" import { dual } from "./Function.ts" import { pipeArguments } from "./Pipeable.ts" import type { Pipeable } from "./Pipeable.ts" @@ -140,10 +141,11 @@ export const makeUnsafe = (initial: A): TxRef => ({ }) const journalEntry = (state: Effect.Transaction["Service"], self: TxRef) => { - let entry = state.journal.get(self) + const journal = (state as TransactionMetaInner).journal + let entry = journal.get(self) if (entry === undefined) { entry = { version: self.version, value: self.value, written: false } - state.journal.set(self, entry) + journal.set(self, entry) } return entry } diff --git a/repos/effect/packages/effect/src/TxSemaphore.ts b/packages/effect/src/TxSemaphore.ts similarity index 100% rename from repos/effect/packages/effect/src/TxSemaphore.ts rename to packages/effect/src/TxSemaphore.ts diff --git a/repos/effect/packages/effect/src/TxSubscriptionRef.ts b/packages/effect/src/TxSubscriptionRef.ts similarity index 100% rename from repos/effect/packages/effect/src/TxSubscriptionRef.ts rename to packages/effect/src/TxSubscriptionRef.ts diff --git a/repos/effect/packages/effect/src/Types.ts b/packages/effect/src/Types.ts similarity index 100% rename from repos/effect/packages/effect/src/Types.ts rename to packages/effect/src/Types.ts diff --git a/repos/effect/packages/effect/src/UndefinedOr.ts b/packages/effect/src/UndefinedOr.ts similarity index 100% rename from repos/effect/packages/effect/src/UndefinedOr.ts rename to packages/effect/src/UndefinedOr.ts diff --git a/repos/effect/packages/effect/src/Unify.ts b/packages/effect/src/Unify.ts similarity index 100% rename from repos/effect/packages/effect/src/Unify.ts rename to packages/effect/src/Unify.ts diff --git a/repos/effect/packages/effect/src/Utils.ts b/packages/effect/src/Utils.ts similarity index 92% rename from repos/effect/packages/effect/src/Utils.ts rename to packages/effect/src/Utils.ts index d35eff6c3..e9dd6d0c8 100644 --- a/repos/effect/packages/effect/src/Utils.ts +++ b/packages/effect/src/Utils.ts @@ -12,7 +12,7 @@ * @since 2.0.0 */ import type { Kind, TypeLambda } from "./HKT.ts" -import { getStackTraceLimit } from "./internal/stackTraceLimit.ts" +import { getStackTraceLimit, setStackTraceLimit } from "./internal/stackTraceLimit.ts" import type * as Types from "./Types.ts" /** @@ -209,10 +209,13 @@ const pickInternalCall = (): (body: () => A) => A => { } } - const isNotOptimizedAway = getStackTraceLimit() !== 0 && - standard[InternalTypeId](() => new Error().stack)?.includes(InternalTypeId) === true - - return isNotOptimizedAway ? standard[InternalTypeId] : forced[InternalTypeId] + const limit = getStackTraceLimit() + if (limit === 0) return forced[InternalTypeId] + // Only the callback and internal call frames are needed for detection. + setStackTraceLimit(2) + const error = standard[InternalTypeId](() => new Error()) + setStackTraceLimit(limit) + return error.stack?.includes(InternalTypeId) === true ? standard[InternalTypeId] : forced[InternalTypeId] } /** @internal */ diff --git a/repos/effect/packages/effect/src/Version.ts b/packages/effect/src/Version.ts similarity index 100% rename from repos/effect/packages/effect/src/Version.ts rename to packages/effect/src/Version.ts diff --git a/repos/effect/packages/effect/src/ai/AiError.ts b/packages/effect/src/ai/AiError.ts similarity index 99% rename from repos/effect/packages/effect/src/ai/AiError.ts rename to packages/effect/src/ai/AiError.ts index 495e10beb..8fbe89087 100644 --- a/repos/effect/packages/effect/src/ai/AiError.ts +++ b/packages/effect/src/ai/AiError.ts @@ -47,7 +47,9 @@ import * as Schema from "../Schema.ts" export const HttpRequestDetails = Schema.Struct({ method: Schema.Literals(["GET", "POST", "PATCH", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE", "QUERY"]), url: Schema.String, - urlParams: Schema.Array(Schema.Tuple([Schema.String, Schema.String])), + urlParams: Schema.Array( + Schema.Tuple([Schema.String, Schema.Union([Schema.String, Schema.Redacted(Schema.String)])]) + ), hash: Schema.optional(Schema.String), headers: Schema.Record( Schema.String, diff --git a/repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts b/packages/effect/src/ai/AnthropicStructuredOutput.ts similarity index 97% rename from repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts rename to packages/effect/src/ai/AnthropicStructuredOutput.ts index e94586526..ff4aeff07 100644 --- a/repos/effect/packages/effect/src/ai/AnthropicStructuredOutput.ts +++ b/packages/effect/src/ai/AnthropicStructuredOutput.ts @@ -14,8 +14,8 @@ import type * as JsonSchema from "../JsonSchema.ts" import * as Rec from "../Record.ts" import * as Schema from "../Schema.ts" import * as InternalStructuredOutput from "./internal/structured-output.ts" -import * as LanguageModel from "./LanguageModel.ts" -import * as OpenAiStructuredOutput from "./OpenAiStructuredOutput.ts" +import type * as LanguageModel from "./LanguageModel.ts" +import type * as OpenAiStructuredOutput from "./OpenAiStructuredOutput.ts" /** * Converts a `Schema.Codec` to Anthropic structured-output JSON Schema and a diff --git a/repos/effect/packages/effect/src/ai/Chat.ts b/packages/effect/src/ai/Chat.ts similarity index 99% rename from repos/effect/packages/effect/src/ai/Chat.ts rename to packages/effect/src/ai/Chat.ts index f8c25ac36..a68b1e37f 100644 --- a/repos/effect/packages/effect/src/ai/Chat.ts +++ b/packages/effect/src/ai/Chat.ts @@ -228,7 +228,7 @@ export interface Chat { , Options>>( options: Options & { readonly toolkit?: undefined } & LanguageModel.GenerateTextOptions<{}> ): Effect.Effect< - LanguageModel.GenerateTextResponse<{}>, + LanguageModel.GenerateTextResponse<{}, LanguageModel.ExtractToolParametersModeWithoutToolkit>, LanguageModel.ExtractError, LanguageModel.LanguageModel | LanguageModel.ExtractServices > @@ -310,7 +310,7 @@ export interface Chat { , Options>>( options: Options & { readonly toolkit?: undefined } & LanguageModel.GenerateTextOptions<{}> ): Stream.Stream< - Response.StreamPart<{}>, + Response.StreamPart<{}, LanguageModel.ExtractToolParametersModeWithoutToolkit>, LanguageModel.ExtractError, LanguageModel.LanguageModel | LanguageModel.ExtractServices > diff --git a/repos/effect/packages/effect/src/ai/Decision.ts b/packages/effect/src/ai/Decision.ts similarity index 76% rename from repos/effect/packages/effect/src/ai/Decision.ts rename to packages/effect/src/ai/Decision.ts index 0fb98663c..1775bd80b 100644 --- a/repos/effect/packages/effect/src/ai/Decision.ts +++ b/packages/effect/src/ai/Decision.ts @@ -28,9 +28,23 @@ export type TypeId = "~effect/ai/Decision" */ export const TypeId: TypeId = "~effect/ai/Decision" +/** + * Decision instructions or descriptions: a string, JSON object, or JSON array. + * + * **Details** + * + * TypeScript interfaces lack the index signature required by `Schema.JsonObject`; + * use object literals, type aliases, or Schema types instead. + * + * @stability unstable + * @category models + * @since 4.0.5 + */ +export type Description = string | Schema.JsonObject | Schema.JsonArray + /** * Decision that assigns the input one label out of a set of criteria. - * `criteria` maps labels to descriptions. + * `criteria` maps labels to descriptions; use `null` for a label without a description. * * @see {@link classify} for the constructor * @see {@link ClassifyAnswer} for the answer produced by this decision @@ -41,13 +55,14 @@ export const TypeId: TypeId = "~effect/ai/Decision" */ export interface Classify(self: Flag, description: string): Flag } = dual(2, (self: Flag, description: string) => Param.withDescription(self, description)) +/** + * Lets a flag be given without a value, in which case `value` is used. + * + * **Details** + * + * Follows GNU-style optional values, like `--color[=WHEN]`. An explicit value + * must be attached with `=` (`--watch=until-done` or `-w=until-done`). A bare + * `--watch` is parsed as `--watch=`, so `value` is validated like any + * other input. The flag never consumes the next token, so in `--watch build`, + * `build` stays a positional argument or subcommand. The flag is still + * required unless it is made optional or given a default. + * + * **Example** (Flag with an optional value) + * + * ```ts import.meta.vitest + * import { Flag } from "effect/cli" + * + * const watch = Flag.Literals("watch", ["first-failure", "until-done"]).pipe( + * Flag.withOptionalValue("first-failure"), + * Flag.optional + * ) + * // --watch => Option.some("first-failure") + * // --watch=until-done => Option.some("until-done") + * // (absent) => Option.none() + * // In help: --watch [=choice] + * watch.kind // => "flag" + * ``` + * + * @stability unstable + * @category combinators + * @since 4.1.0 + */ +export const withOptionalValue: { + (value: string): (self: Flag) => Flag + (self: Flag, value: string): Flag +} = dual(2, (self: Flag, value: string) => Param.withOptionalValue(self, value)) + // ------------------------------------------------------------------------------------- // metadata // ------------------------------------------------------------------------------------- diff --git a/repos/effect/packages/effect/src/cli/GlobalFlag.ts b/packages/effect/src/cli/GlobalFlag.ts similarity index 93% rename from repos/effect/packages/effect/src/cli/GlobalFlag.ts rename to packages/effect/src/cli/GlobalFlag.ts index 348e20815..f1d8f4480 100644 --- a/repos/effect/packages/effect/src/cli/GlobalFlag.ts +++ b/packages/effect/src/cli/GlobalFlag.ts @@ -224,21 +224,29 @@ export const Wizard: Action = Action({ * * **Details** * - * Accepted values are `bash`, `zsh`, `fish`, and `sh`; `sh` is normalized to - * `bash`. + * Accepted values are `bash`, `zsh`, `fish`, `powershell`, `sh`, and `pwsh`; + * `sh` is normalized to `bash` and `pwsh` to `powershell`. * * @stability unstable * @category references * @since 4.0.0 */ -export const Completions: Action> = Action({ - flag: Flag.Literals("completions", ["bash", "zsh", "fish", "sh"] as const) - .pipe( - Flag.optional, - Flag.map((v) => Option.map(v, (s) => s === "sh" ? "bash" : s)), - Flag.withMetavar(""), - Flag.withDescription("Print shell completion script") - ), +export const Completions: Action> = Action({ + flag: Flag.ChoiceWithValue( + "completions", + [ + ["bash", "bash"], + ["zsh", "zsh"], + ["fish", "fish"], + ["powershell", "powershell"], + ["sh", "bash"], + ["pwsh", "powershell"] + ] as const + ).pipe( + Flag.optional, + Flag.withMetavar(""), + Flag.withDescription("Print shell completion script") + ), run: Effect.fnUntraced(function*(shell, { command }) { if (Option.isNone(shell)) return const descriptor = CommandDescriptor.fromCommand(command) @@ -317,7 +325,7 @@ export const BuiltIns: readonly [ Action, Action, Action, - Action>, + Action>, Setting<"log-level", Option.Option> ] = [Help, Version, Wizard, Completions, LogLevel] diff --git a/repos/effect/packages/effect/src/cli/HelpDoc.ts b/packages/effect/src/cli/HelpDoc.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/HelpDoc.ts rename to packages/effect/src/cli/HelpDoc.ts diff --git a/repos/effect/packages/effect/src/cli/Param.ts b/packages/effect/src/cli/Param.ts similarity index 98% rename from repos/effect/packages/effect/src/cli/Param.ts rename to packages/effect/src/cli/Param.ts index 96bf81bba..5fcc31e2f 100644 --- a/repos/effect/packages/effect/src/cli/Param.ts +++ b/packages/effect/src/cli/Param.ts @@ -213,6 +213,10 @@ export interface Single extends Param { readonly primitiveType: Primitive.Primitive readonly typeName?: string | undefined readonly hidden: boolean + /** + * Raw value used for a bare flag. See `withOptionalValue`. + */ + readonly optionalValue?: string | undefined } /** @@ -362,6 +366,7 @@ export const makeSingle = (params: { readonly description?: Option.Option | undefined readonly aliases?: ReadonlyArray | undefined readonly hidden?: boolean | undefined + readonly optionalValue?: string | undefined }): Single => { const parse: Parse = (args) => params.kind === argumentKind @@ -1823,6 +1828,45 @@ export const withMetavar: { typeName: metavar }))) +/** + * Lets a flag be given without a value, in which case `value` is used. + * + * **Details** + * + * Follows GNU-style optional values. An explicit value must be attached with + * `=` (`--watch=until-done`), and a bare `--watch` is parsed as + * `--watch=`. The flag never consumes the next token. It is still + * required unless it is made optional or given a default. + * + * **Example** (Flag with an optional value) + * + * ```ts import.meta.vitest + * import { Param } from "effect/cli" + * + * const watch = Param.String(Param.flagKind, "watch").pipe( + * Param.withOptionalValue("first-failure"), + * Param.optional + * ) + * watch.kind // => "flag" + * ``` + * + * @stability unstable + * @category combinators + * @since 4.1.0 + */ +export const withOptionalValue: { + (value: string): (self: Param) => Param + (self: Param, value: string): Param +} = dual(2, ( + self: Param, + value: string +) => + transformSingle(self, (single) => + makeSingle({ + ...single, + optionalValue: value + }))) + /** * Validates parsed values against a Schema, providing detailed error messages. * diff --git a/repos/effect/packages/effect/src/cli/Primitive.ts b/packages/effect/src/cli/Primitive.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/Primitive.ts rename to packages/effect/src/cli/Primitive.ts diff --git a/repos/effect/packages/effect/src/cli/Prompt.ts b/packages/effect/src/cli/Prompt.ts similarity index 92% rename from repos/effect/packages/effect/src/cli/Prompt.ts rename to packages/effect/src/cli/Prompt.ts index c6fba52d4..79b18a4c4 100644 --- a/repos/effect/packages/effect/src/cli/Prompt.ts +++ b/packages/effect/src/cli/Prompt.ts @@ -26,6 +26,7 @@ import * as Path from "../Path.ts" import * as Predicate from "../Predicate.ts" import * as Queue from "../Queue.ts" import * as Redacted from "../Redacted.ts" +import type * as Scope from "../Scope.ts" import * as Terminal from "../Terminal.ts" import type { Covariant } from "../Types.ts" import * as Ansi from "./internal/ansi.ts" @@ -203,6 +204,11 @@ export interface Theme { readonly errorColor: string /** The color used for submitted values. */ readonly submittedColor: string + /** + * Whether prompts and the command wizard emit color and style escapes. + * Cursor movement and redraw controls are always emitted. + */ + readonly colors: boolean } /** @@ -620,7 +626,7 @@ export interface ToggleOptions extends ThemeOptions { readonly inactive?: string } -const defaultTheme: Theme = { +const defaultTheme: Omit = { prefix: "?", arrowUp: "↑", arrowDown: "↓", @@ -640,7 +646,7 @@ const defaultTheme: Theme = { submittedColor: Ansi.white } -const windowsTheme: Theme = { +const windowsTheme: Omit = { ...defaultTheme, checkboxOn: "[*]", checkboxOff: "[ ]", @@ -653,13 +659,19 @@ const windowsTheme: Theme = { /** * Creates a prompt theme using the current platform defaults. * + * **Details** + * + * Unless `colors` is provided, it is detected when the theme is created: + * colors are enabled when stdout is a TTY and `NO_COLOR` is unset or empty. + * * @stability unstable * @category constructors * @since 4.0.0 */ export const makeTheme = (options?: Partial): Theme => ({ ...(process.platform === "win32" ? windowsTheme : defaultTheme), - ...options + ...options, + colors: options?.colors ?? Ansi.detectColors() }) /** @@ -668,7 +680,13 @@ export const makeTheme = (options?: Partial): Theme => ({ * **Details** * * Provide this reference once to theme every prompt in an application. A - * prompt's `theme` option takes precedence over the context value. + * prompt's `theme` option takes precedence over the context value. The + * command wizard also reads `colors` from this reference, so provide a theme + * with `colors: false` to disable wizard styling. + * + * The default value is created with `makeTheme` the first time it is read, + * so color detection runs once per process. Changes to `NO_COLOR` after the + * first prompt do not affect the default theme. * * @stability unstable * @category services @@ -832,11 +850,10 @@ export const all: < return allTupled(arguments[0]) as any } -const annotateLine = (line: string): string => Ansi.annotate(line, Ansi.bold) -const annotateErrorLine = (line: string, color: string): string => - Ansi.annotate(line, Ansi.combine(Ansi.italicized, color)) -const annotateSymbol = (symbol: string, ...styles: Array>): string => - symbol.length === 0 ? "" : Ansi.annotate(symbol, ...styles) +const annotateErrorLine = (theme: Theme, line: string): string => + Ansi.annotate(theme.colors, line, Ansi.combine(Ansi.italicized, theme.errorColor)) +const annotateSymbol = (theme: Theme, symbol: string, ...styles: Array>): string => + symbol.length === 0 ? "" : Ansi.annotate(theme.colors, symbol, ...styles) const separateSymbol = (symbol: string, text: string): string => symbol.length === 0 ? text : symbol + " " + text const renderPagingPrefix = (theme: Theme, showArrowUp: boolean, showArrowDown: boolean): string => { const width = Math.max(theme.arrowUp.length, theme.arrowDown.length) @@ -1240,7 +1257,7 @@ export const run: ( > = Effect.fnUntraced( function*(self: Prompt) { const terminal = yield* Terminal.Terminal - const input = yield* terminal.readInput + const input = yield* Effect.cached(terminal.readInput) return yield* runWithInput(self, terminal, input) }, Effect.mapError(() => new Terminal.QuitError({})), @@ -1505,13 +1522,13 @@ const allTupled = >>(arg: T): Prompt< const runWithInput = ( prompt: Prompt, terminal: Terminal.Terminal, - input: Queue.Dequeue -): Effect.Effect => + input: Terminal.Terminal["readInput"] +): Effect.Effect => Effect.suspend(() => { const op = prompt as PromptPrimitive switch (op._tag) { case "Loop": { - return runLoop(op, terminal, input) + return Effect.flatMap(input, (queue) => runLoop(op, terminal, queue)) } case "OnSuccess": { return Effect.flatMap( @@ -1625,48 +1642,36 @@ const handleConfirmClear = (options: ConfirmOptionsReq) => { const confirmMessage = state.value ? options.placeholder.defaultConfirm! : options.placeholder.defaultDeny! - const promptText = renderConfirmOutput( - confirmMessage, - figures.prefix, - figures.pointerSmall, - options, - { plain: true } - ) + const promptText = renderPrompt(confirmMessage, options.message, figures.prefix, figures.pointerSmall, figures, { + plain: true + }) const clearOutput = eraseText(promptText, columns) const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft return clearOutput + resetCurrentLine }) } -const renderConfirmOutput = ( - confirm: string, - leadingSymbol: string, - trailingSymbol: string, - options: ConfirmOptionsReq, - renderOptions?: RenderOptions | undefined -) => renderPrompt(confirm, options.message, leadingSymbol, trailingSymbol, renderOptions) - const renderConfirmNextFrame = Effect.fnUntraced(function*(state: ConfirmState, options: ConfirmOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) // Marking these explicitly as present with `!` because they always will be // and there is really no value in adding a `DeepRequired` type helper just // for these internal cases const confirmMessage = state.value ? options.placeholder.defaultConfirm! : options.placeholder.defaultDeny! - const confirm = Ansi.annotate(confirmMessage, figures.mutedColor) - const promptMsg = renderConfirmOutput(confirm, leadingSymbol, trailingSymbol, options) + const confirm = Ansi.annotate(figures.colors, confirmMessage, figures.mutedColor) + const promptMsg = renderPrompt(confirm, options.message, leadingSymbol, trailingSymbol, figures) return Ansi.cursorHide + promptMsg }) const renderConfirmSubmission = Effect.fnUntraced(function*(value: boolean, options: ConfirmOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) const confirmMessage = value ? options.label.confirm : options.label.deny - const promptMsg = renderConfirmOutput(confirmMessage, leadingSymbol, trailingSymbol, options) + const promptMsg = renderPrompt(confirmMessage, options.message, leadingSymbol, trailingSymbol, figures) return promptMsg + "\n" }) @@ -1714,7 +1719,9 @@ const handleDateClear = (options: DateOptionsReq) => { const figures = yield* getTheme(options) const resetCurrentLine = Ansi.eraseLine + Ansi.cursorLeft const parts = Arr.reduce(state.dateParts, "", (doc, part) => doc + part.toString()) - const promptText = renderDateOutput(figures.prefix, figures.pointerSmall, parts, options, { plain: true }) + const promptText = renderPrompt(parts, options.message, figures.prefix, figures.pointerSmall, figures, { + plain: true + }) const errorText = Option.isSome(state.error) ? Arr.match(state.error.value.split(NEWLINE_REGEXP), { onEmpty: () => "", @@ -1730,8 +1737,8 @@ const renderDateError = (state: DateState, pointer: string, theme: Theme): strin if (Option.isSome(state.error)) { const errorLines = state.error.value.split(NEWLINE_REGEXP) if (Arr.isReadonlyArrayNonEmpty(errorLines)) { - const prefix = annotateSymbol(pointer, theme.errorColor) - const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + const prefix = annotateSymbol(theme, pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(theme, str)) return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition } } @@ -1746,37 +1753,29 @@ const renderParts = (state: DateState, theme: Theme, submitted: boolean = false) const partDoc = part.toString() if (currentIndex === state.cursor && !submitted) { const annotation = Ansi.combine(Ansi.underlined, theme.primaryColor) - return doc + Ansi.annotate(partDoc, annotation) + return doc + Ansi.annotate(theme.colors, partDoc, annotation) } return doc + partDoc } ) } -const renderDateOutput = ( - leadingSymbol: string, - trailingSymbol: string, - parts: string, - options: DateOptionsReq, - renderOptions?: RenderOptions | undefined -) => renderPrompt(parts, options.message, leadingSymbol, trailingSymbol, renderOptions) - const renderDateNextFrame = Effect.fnUntraced(function*(state: DateState, options: DateOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) const parts = renderParts(state, figures) - const promptMsg = renderDateOutput(leadingSymbol, trailingSymbol, parts, options) + const promptMsg = renderPrompt(parts, options.message, leadingSymbol, trailingSymbol, figures) const errorMsg = renderDateError(state, figures.pointerSmall, figures) return Ansi.cursorHide + promptMsg + errorMsg }) const renderDateSubmission = Effect.fnUntraced(function*(state: DateState, options: DateOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) const parts = renderParts(state, figures, true) - const promptMsg = renderDateOutput(leadingSymbol, trailingSymbol, parts, options) + const promptMsg = renderPrompt(parts, options.message, leadingSymbol, trailingSymbol, figures) return promptMsg + "\n" }) @@ -2364,12 +2363,13 @@ const handleFileClear = (options: FileOptionsReq) => { const resolvedPathText = separateSymbol(figures.pointerSmall, resolvedPath) const isConfirming = showConfirmation(state.confirm) const promptText = isConfirming - ? renderPrompt("(Y/n)", CONFIRM_MESSAGE, figures.prefix, figures.pointerSmall, { plain: true }) + ? renderPrompt("(Y/n)", CONFIRM_MESSAGE, figures.prefix, figures.pointerSmall, figures, { plain: true }) : renderPrompt( renderFileFilter(state, figures, { plain: true }), options.message, figures.tick, figures.ellipsis, + figures, { plain: true } @@ -2395,6 +2395,7 @@ const renderPrompt = ( message: string, leadingSymbol: string, trailingSymbol: string, + theme: Theme, options?: RenderOptions | undefined ) => { const prefix = leadingSymbol.length === 0 ? "" : leadingSymbol + " " @@ -2410,13 +2411,12 @@ const renderPrompt = ( } return output } - const annotate = options?.plain === true - ? (line: string) => line - : annotateLine return Arr.match(message.split(NEWLINE_REGEXP), { onEmpty: () => renderLine(""), onNonEmpty: (promptLines) => { - const lines = Arr.map(promptLines, (line) => annotate(line)) + const lines = options?.plain === true + ? promptLines + : Arr.map(promptLines, (line) => Ansi.annotate(theme.colors, line, Ansi.bold)) return renderLine(lines.join("\n")) } }) @@ -2438,7 +2438,7 @@ const renderPrefix = ( if (state.cursor === currentIndex) { return renderOptions?.plain === true ? figures.pointer + prefix - : annotateSymbol(figures.pointer, figures.primaryColor) + prefix + : annotateSymbol(figures, figures.pointer, figures.primaryColor) + prefix } return prefix + " ".repeat(figures.pointer.length) } @@ -2453,7 +2453,7 @@ const renderFileName = ( return file } return isSelected - ? Ansi.annotate(file, Ansi.combine(Ansi.underlined, theme.primaryColor)) + ? Ansi.annotate(theme.colors, file, Ansi.combine(Ansi.underlined, theme.primaryColor)) : file } @@ -2461,10 +2461,10 @@ const renderFileFilter = (state: FileState, theme: Theme, renderOptions?: Render const filterValue = state.query.length === 0 ? renderOptions?.plain === true ? FILE_FILTER_PLACEHOLDER - : Ansi.annotate(FILE_FILTER_PLACEHOLDER, theme.mutedColor) + : Ansi.annotate(theme.colors, FILE_FILTER_PLACEHOLDER, theme.mutedColor) : renderOptions?.plain === true ? state.query - : Ansi.annotate(state.query, Ansi.combine(Ansi.underlined, theme.primaryColor)) + : Ansi.annotate(theme.colors, state.query, Ansi.combine(Ansi.underlined, theme.primaryColor)) return `[${FILE_FILTER_LABEL}: ${filterValue}]` } @@ -2479,7 +2479,7 @@ const renderFiles = ( if (length === 0) { return renderOptions?.plain === true ? FILE_EMPTY_MESSAGE - : Ansi.annotate(FILE_EMPTY_MESSAGE, figures.mutedColor) + : Ansi.annotate(figures.colors, FILE_EMPTY_MESSAGE, figures.mutedColor) } const toDisplay = entriesToDisplay(state.cursor, length, options.maxPerPage) const documents: Array = [] @@ -2498,28 +2498,44 @@ const renderFileNextFrame = Effect.fnUntraced(function*(state: FileState, option const currentPath = yield* resolveCurrentPath(state.path, options) const selectedPath = state.files[state.cursor] const resolvedPath = selectedPath === undefined ? currentPath : path.resolve(currentPath, selectedPath) - const resolvedPathMsg = Ansi.annotate(separateSymbol(figures.pointerSmall, resolvedPath), figures.mutedColor) + const resolvedPathMsg = Ansi.annotate( + figures.colors, + separateSymbol(figures.pointerSmall, resolvedPath), + figures.mutedColor + ) if (showConfirmation(state.confirm)) { - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) - const confirm = Ansi.annotate("(Y/n)", figures.mutedColor) - const promptMsg = renderPrompt(confirm, CONFIRM_MESSAGE, leadingSymbol, trailingSymbol) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) + const confirm = Ansi.annotate(figures.colors, "(Y/n)", figures.mutedColor) + const promptMsg = renderPrompt(confirm, CONFIRM_MESSAGE, leadingSymbol, trailingSymbol, figures) return Ansi.cursorHide + promptMsg + "\n" + resolvedPathMsg } - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) - const promptMsg = renderPrompt(renderFileFilter(state, figures), options.message, leadingSymbol, trailingSymbol) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) + const promptMsg = renderPrompt( + renderFileFilter(state, figures), + options.message, + leadingSymbol, + trailingSymbol, + figures + ) const files = renderFiles(state, state.files, figures, options) return Ansi.cursorHide + promptMsg + "\n" + resolvedPathMsg + "\n" + files }) const renderFileSubmission = Effect.fnUntraced(function*(state: FileState, value: string, options: FileOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) - const promptMsg = renderPrompt(renderFileFilter(state, figures), options.message, leadingSymbol, trailingSymbol) - return promptMsg + " " + Ansi.annotate(value, figures.submittedColor) + "\n" + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) + const promptMsg = renderPrompt( + renderFileFilter(state, figures), + options.message, + leadingSymbol, + trailingSymbol, + figures + ) + return promptMsg + " " + Ansi.annotate(figures.colors, value, figures.submittedColor) + "\n" }) const handleFileRender = (options: FileOptionsReq) => { @@ -2720,8 +2736,8 @@ const renderMultiSelectError = ( if (renderOptions?.plain === true) { return separateSymbol(pointer, errorLines.join("\n")) } - const prefix = annotateSymbol(pointer, theme.errorColor) - const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + const prefix = annotateSymbol(theme, pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(theme, str)) return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition } }) @@ -2739,7 +2755,7 @@ const renderChoiceDescription = ( const text = theme.descriptionSeparator + choice.description return renderOptions?.plain === true ? text - : Ansi.annotate(text, theme.mutedColor) + : Ansi.annotate(theme.colors, text, theme.mutedColor) } return "" } @@ -2755,7 +2771,7 @@ const renderMultiSelectTitle = ( if (renderOptions?.plain === true || !isHighlighted) { return title } - return Ansi.annotate(title, Ansi.combine(Ansi.underlined, theme.primaryColor)) + return Ansi.annotate(theme.colors, title, Ansi.combine(Ansi.underlined, theme.primaryColor)) } const renderMultiSelectChoices = ( @@ -2801,7 +2817,7 @@ const renderMultiSelectChoices = ( const isSelected = state.selectedIndices.has(choiceIndex) const checkbox = isSelected ? figures.checkboxOn : figures.checkboxOff const annotatedCheckbox = isHighlighted && renderOptions?.plain !== true - ? Ansi.annotate(checkbox, figures.primaryColor) + ? Ansi.annotate(figures.colors, checkbox, figures.primaryColor) : checkbox const selectChoice = choice as SelectChoice const title = renderChoiceTitle(selectChoice, isHighlighted, figures, renderOptions) @@ -2821,9 +2837,9 @@ const renderMultiSelectNextFrame = Effect.fnUntraced( function*(state: MultiSelectState, options: SelectOptionsReq) { const figures = yield* getTheme(options) const choices = renderMultiSelectChoices(state, options, figures) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) - const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options, figures) const error = renderMultiSelectError(state, figures.pointer, figures) return Ansi.cursorHide + withSelectHeader(promptMsg, choices) + error } @@ -2836,10 +2852,10 @@ const renderMultiSelectSubmission = Effect.fnUntraced( options.choices[index].title ) const selectedText = selectedChoices.join(", ") - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) - const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) ?? leadingSymbol - return promptMsg + " " + Ansi.annotate(selectedText, figures.submittedColor) + "\n" + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options, figures) ?? leadingSymbol + return promptMsg + " " + Ansi.annotate(figures.colors, selectedText, figures.submittedColor) + "\n" } ) @@ -2897,7 +2913,7 @@ const handleMultiSelectClear = (options: SelectOptionsReq) => const columns = yield* terminal.columns const figures = yield* getTheme(options) const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft - const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, { plain: true }) + const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, figures, { plain: true }) const choicesText = renderMultiSelectChoices(state, options, figures, { plain: true }) const errorText = renderMultiSelectError(state, figures.pointer, figures, { plain: true }) const clearOutput = clearOutputWithError(withSelectHeader(promptText, choicesText), columns, errorText) @@ -2991,7 +3007,7 @@ const renderNumberInput = ( const annotation = Option.isSome(state.error) ? theme.errorColor : Ansi.combine(Ansi.underlined, theme.primaryColor) - return Ansi.annotate(value, annotation) + return Ansi.annotate(theme.colors, value, annotation) } const renderNumberError = ( @@ -3007,8 +3023,8 @@ const renderNumberError = ( if (renderOptions?.plain === true) { return separateSymbol(pointer, errorLines.join("\n")) } - const prefix = annotateSymbol(pointer, theme.errorColor) - const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + const prefix = annotateSymbol(theme, pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(theme, str)) return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition } }) @@ -3026,13 +3042,13 @@ const renderNumberOutput = ( submitted: boolean = false ) => { const value = renderNumberInput(state, submitted, theme, renderOptions) - return renderPrompt(value, options.message, leadingSymbol, trailingSymbol, renderOptions) + return renderPrompt(value, options.message, leadingSymbol, trailingSymbol, theme, renderOptions) } const renderNumberNextFrame = Effect.fnUntraced(function*(state: NumberState, options: IntOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) const errorMsg = renderNumberError(state, figures.pointerSmall, figures) const promptMsg = renderNumberOutput(state, leadingSymbol, trailingSymbol, options, figures) return promptMsg + errorMsg @@ -3040,8 +3056,8 @@ const renderNumberNextFrame = Effect.fnUntraced(function*(state: NumberState, op const renderNumberSubmission = Effect.fnUntraced(function*(nextState: NumberState, options: IntOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) const promptMsg = renderNumberOutput(nextState, leadingSymbol, trailingSymbol, options, figures, undefined, true) return promptMsg + "\n" }) @@ -3299,11 +3315,12 @@ const renderSelectOutput = ( leadingSymbol: string, trailingSymbol: string, options: SelectOptionsReq, + theme: Theme, renderOptions?: RenderOptions | undefined ): string | undefined => options.message === undefined ? undefined - : renderPrompt("", options.message, leadingSymbol, trailingSymbol, renderOptions) + : renderPrompt("", options.message, leadingSymbol, trailingSymbol, theme, renderOptions) const withSelectHeader = (header: string | undefined, body: string): string => header === undefined ? body : header + "\n" + body @@ -3317,10 +3334,10 @@ const renderAutoCompleteFilter = ( const filterValue = state.query.length === 0 ? renderOptions?.plain === true ? options.filterPlaceholder - : Ansi.annotate(options.filterPlaceholder, theme.mutedColor) + : Ansi.annotate(theme.colors, options.filterPlaceholder, theme.mutedColor) : renderOptions?.plain === true ? state.query - : Ansi.annotate(state.query, Ansi.combine(Ansi.underlined, theme.primaryColor)) + : Ansi.annotate(theme.colors, state.query, Ansi.combine(Ansi.underlined, theme.primaryColor)) return `[${options.filterLabel}: ${filterValue}]` } @@ -3333,7 +3350,7 @@ const renderAutoCompleteOutput = ( renderOptions?: RenderOptions | undefined ) => { const filter = renderAutoCompleteFilter(state, options, theme, renderOptions) - return renderPrompt(filter, options.message, leadingSymbol, trailingSymbol, renderOptions) + return renderPrompt(filter, options.message, leadingSymbol, trailingSymbol, theme, renderOptions) } const renderChoicePrefix = ( @@ -3357,11 +3374,11 @@ const renderChoicePrefix = ( if (choices[currentIndex].disabled) { const annotation = Ansi.combine(Ansi.bold, figures.mutedColor) return state === currentIndex - ? annotateSymbol(figures.pointer, annotation) + prefix + ? annotateSymbol(figures, figures.pointer, annotation) + prefix : prefix + " ".repeat(figures.pointer.length) } return state === currentIndex - ? annotateSymbol(figures.pointer, figures.primaryColor) + prefix + ? annotateSymbol(figures, figures.pointer, figures.primaryColor) + prefix : prefix + " ".repeat(figures.pointer.length) } @@ -3388,11 +3405,11 @@ const renderAutoCompleteChoicePrefix = ( if (choice.disabled) { const annotation = Ansi.combine(Ansi.bold, figures.mutedColor) return state.index === choiceIndex - ? annotateSymbol(figures.pointer, annotation) + prefix + ? annotateSymbol(figures, figures.pointer, annotation) + prefix : prefix + " ".repeat(figures.pointer.length) } return state.index === choiceIndex - ? annotateSymbol(figures.pointer, figures.primaryColor) + prefix + ? annotateSymbol(figures, figures.pointer, figures.primaryColor) + prefix : prefix + " ".repeat(figures.pointer.length) } @@ -3408,11 +3425,11 @@ const renderChoiceTitle = ( const title = choice.title if (isSelected) { return choice.disabled - ? Ansi.annotate(title, Ansi.combine(Ansi.underlined, theme.mutedColor)) - : Ansi.annotate(title, Ansi.combine(Ansi.underlined, theme.primaryColor)) + ? Ansi.annotate(theme.colors, title, Ansi.combine(Ansi.underlined, theme.mutedColor)) + : Ansi.annotate(theme.colors, title, Ansi.combine(Ansi.underlined, theme.primaryColor)) } return choice.disabled - ? Ansi.annotate(title, Ansi.combine(Ansi.strikethrough, theme.mutedColor)) + ? Ansi.annotate(theme.colors, title, Ansi.combine(Ansi.strikethrough, theme.mutedColor)) : title } @@ -3445,7 +3462,7 @@ const renderAutoCompleteChoices = ( if (state.filtered.length === 0) { return renderOptions?.plain === true ? options.emptyMessage - : Ansi.annotate(options.emptyMessage, figures.mutedColor) + : Ansi.annotate(figures.colors, options.emptyMessage, figures.mutedColor) } const cursor = autoCompleteCursor(state) const toDisplay = entriesToDisplay(cursor, state.filtered.length, options.maxPerPage) @@ -3465,9 +3482,9 @@ const renderAutoCompleteChoices = ( const renderSelectNextFrame = Effect.fnUntraced(function*(state: SelectState, options: SelectOptionsReq) { const figures = yield* getTheme(options) const choices = renderSelectChoices(state, options, figures) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) - const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options, figures) return Ansi.cursorHide + withSelectHeader(promptMsg, choices) }) @@ -3477,8 +3494,8 @@ const renderAutoCompleteNextFrame = Effect.fnUntraced(function*( ) { const figures = yield* getTheme(options) const choices = renderAutoCompleteChoices(state, options, figures) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) const promptMsg = renderAutoCompleteOutput(state, leadingSymbol, trailingSymbol, options, figures) return Ansi.cursorHide + promptMsg + "\n" + choices }) @@ -3486,10 +3503,10 @@ const renderAutoCompleteNextFrame = Effect.fnUntraced(function*( const renderSelectSubmission = Effect.fnUntraced(function*(state: SelectState, options: SelectOptionsReq) { const figures = yield* getTheme(options) const selected = options.choices[state].title - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) - const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options) ?? leadingSymbol - return promptMsg + " " + Ansi.annotate(selected, figures.submittedColor) + "\n" + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) + const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options, figures) ?? leadingSymbol + return promptMsg + " " + Ansi.annotate(figures.colors, selected, figures.submittedColor) + "\n" }) const renderAutoCompleteSubmission = Effect.fnUntraced(function*( @@ -3498,10 +3515,10 @@ const renderAutoCompleteSubmission = Effect.fnUntraced(function*( ) { const figures = yield* getTheme(options) const selected = options.choices[state.index].title - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) const promptMsg = renderAutoCompleteOutput(state, leadingSymbol, trailingSymbol, options, figures) - return promptMsg + " " + Ansi.annotate(selected, figures.submittedColor) + "\n" + return promptMsg + " " + Ansi.annotate(figures.colors, selected, figures.submittedColor) + "\n" }) const processSelectCursorUp = (state: SelectState, choices: SelectOptionsReq["choices"]) => { @@ -3587,7 +3604,7 @@ const handleSelectClear = (options: SelectOptionsReq) => const columns = yield* terminal.columns const figures = yield* getTheme(options) const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft - const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, { plain: true }) + const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, figures, { plain: true }) const choicesText = renderSelectChoices(state, options, figures, { plain: true }) const clearOutput = eraseText(withSelectHeader(promptText, choicesText), columns) return clearOutput + clearPrompt @@ -3759,10 +3776,10 @@ const renderTextInput = ( return "" } case "password": { - return annotateSymbol(theme.passwordMask.repeat(text.length), annotation) + return annotateSymbol(theme, theme.passwordMask.repeat(text.length), annotation) } case "text": { - return Ansi.annotate(text, annotation) + return Ansi.annotate(theme.colors, text, annotation) } } } @@ -3780,8 +3797,8 @@ const renderTextError = ( if (renderOptions?.plain === true) { return separateSymbol(pointer, errorLines.join("\n")) } - const prefix = annotateSymbol(pointer, theme.errorColor) - const lines = Arr.map(errorLines, (str) => annotateErrorLine(str, theme.errorColor)) + const prefix = annotateSymbol(theme, pointer, theme.errorColor) + const lines = Arr.map(errorLines, (str) => annotateErrorLine(theme, str)) return Ansi.cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + Ansi.cursorRestorePosition } }) @@ -3799,13 +3816,13 @@ const renderTextOutput = ( submitted: boolean = false ) => { const value = renderTextInput(nextState, options, theme, submitted, renderOptions) - return renderPrompt(value, options.message, leadingSymbol, trailingSymbol, renderOptions) + return renderPrompt(value, options.message, leadingSymbol, trailingSymbol, theme, renderOptions) } const renderTextNextFrame = Effect.fnUntraced(function*(state: TextState, options: TextOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) const promptMsg = renderTextOutput(state, leadingSymbol, trailingSymbol, options, figures) const errorMsg = renderTextError(state, figures.pointerSmall, figures) const cursorWidth = options.type === "password" ? figures.passwordMask.length : 1 @@ -3815,8 +3832,8 @@ const renderTextNextFrame = Effect.fnUntraced(function*(state: TextState, option const renderTextSubmission = Effect.fnUntraced(function*(state: TextState, options: TextOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) const promptMsg = renderTextOutput(state, leadingSymbol, trailingSymbol, options, figures, undefined, true) return promptMsg + "\n" }) @@ -4009,7 +4026,9 @@ const handleToggleClear = Effect.fnUntraced(function*(options: ToggleOptionsReq) const figures = yield* getTheme(options) const clearPrompt = Ansi.eraseLine + Ansi.cursorLeft const toggleText = options.active + " " + separateSymbol(figures.toggleSeparator, options.inactive) - const promptText = renderPrompt(toggleText, options.message, figures.prefix, figures.pointerSmall, { plain: true }) + const promptText = renderPrompt(toggleText, options.message, figures.prefix, figures.pointerSmall, figures, { + plain: true + }) const clearOutput = eraseText(promptText, columns) return clearOutput + clearPrompt }) @@ -4020,44 +4039,35 @@ const renderToggle = ( theme: Theme, submitted: boolean = false ) => { - const separator = annotateSymbol(theme.toggleSeparator, theme.mutedColor) + const separator = annotateSymbol(theme, theme.toggleSeparator, theme.mutedColor) const selectedAnnotation = Ansi.combine( Ansi.underlined, submitted ? theme.submittedColor : theme.primaryColor ) const inactive = value ? options.inactive - : Ansi.annotate(options.inactive, selectedAnnotation) + : Ansi.annotate(theme.colors, options.inactive, selectedAnnotation) const active = value - ? Ansi.annotate(options.active, selectedAnnotation) + ? Ansi.annotate(theme.colors, options.active, selectedAnnotation) : options.active return active + " " + separateSymbol(separator, inactive) } -const renderToggleOutput = ( - toggle: string, - leadingSymbol: string, - trailingSymbol: string, - options: ToggleOptionsReq -) => { - return renderPrompt(toggle, options.message, leadingSymbol, trailingSymbol) -} - const renderToggleNextFrame = Effect.fnUntraced(function*(state: ToggleState, options: ToggleOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor) - const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.prefix, figures.primaryColor) + const trailingSymbol = annotateSymbol(figures, figures.pointerSmall, figures.mutedColor) const toggle = renderToggle(state, options, figures) - const promptMsg = renderToggleOutput(toggle, leadingSymbol, trailingSymbol, options) + const promptMsg = renderPrompt(toggle, options.message, leadingSymbol, trailingSymbol, figures) return Ansi.cursorHide + promptMsg }) const renderToggleSubmission = Effect.fnUntraced(function*(value: boolean, options: ToggleOptionsReq) { const figures = yield* getTheme(options) - const leadingSymbol = annotateSymbol(figures.tick, figures.successColor) - const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor) + const leadingSymbol = annotateSymbol(figures, figures.tick, figures.successColor) + const trailingSymbol = annotateSymbol(figures, figures.ellipsis, figures.mutedColor) const toggle = renderToggle(value, options, figures, true) - const promptMsg = renderToggleOutput(toggle, leadingSymbol, trailingSymbol, options) + const promptMsg = renderPrompt(toggle, options.message, leadingSymbol, trailingSymbol, figures) return promptMsg + "\n" }) diff --git a/repos/effect/packages/effect/src/cli/index.ts b/packages/effect/src/cli/index.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/index.ts rename to packages/effect/src/cli/index.ts diff --git a/repos/effect/packages/effect/src/cli/internal/ansi.ts b/packages/effect/src/cli/internal/ansi.ts similarity index 75% rename from repos/effect/packages/effect/src/cli/internal/ansi.ts rename to packages/effect/src/cli/internal/ansi.ts index 207d5825b..581a91664 100644 --- a/repos/effect/packages/effect/src/cli/internal/ansi.ts +++ b/packages/effect/src/cli/internal/ansi.ts @@ -105,9 +105,32 @@ export const blackBright = `${ESC}90m` */ export const cyanBright = `${ESC}96m` -export const annotate = (text: string, ...styles: Array>) => { - const flat = styles.flat() - return `${flat.join("")}${text}${reset}` +/** + * Wraps `text` in the given styles followed by a reset. When `colors` is + * `false` the text is returned unchanged, so callers gate color and style + * escapes in one place while cursor and erase controls stay untouched. + * + * @internal + */ +export const annotate = (colors: boolean, text: string, ...styles: Array>): string => { + if (!colors) return text + return `${styles.flat().join("")}${text}${reset}` +} + +/** + * Detects whether CLI output should use color and style escapes: stdout must + * be a TTY and `NO_COLOR` must be unset or empty. + * + * @internal + */ +export const detectColors = (): boolean => { + const globalProcess = (globalThis as any).process + return typeof globalProcess === "object" && + globalProcess !== null && + typeof globalProcess.stdout === "object" && + globalProcess.stdout !== null && + globalProcess.stdout.isTTY === true && + !globalProcess.env?.NO_COLOR } export const combine = (...styles: Array): Array => styles diff --git a/repos/effect/packages/effect/src/cli/internal/auto-suggest.ts b/packages/effect/src/cli/internal/auto-suggest.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/auto-suggest.ts rename to packages/effect/src/cli/internal/auto-suggest.ts diff --git a/repos/effect/packages/effect/src/cli/internal/command.ts b/packages/effect/src/cli/internal/command.ts similarity index 98% rename from repos/effect/packages/effect/src/cli/internal/command.ts rename to packages/effect/src/cli/internal/command.ts index af161fc6d..7b52da248 100644 --- a/repos/effect/packages/effect/src/cli/internal/command.ts +++ b/packages/effect/src/cli/internal/command.ts @@ -242,10 +242,11 @@ export const toFlagDoc = ( metadata: ReturnType ): FlagDoc => { const formattedAliases = single.aliases.map((alias) => alias.length === 1 ? `-${alias}` : `--${alias}`) + const type = single.typeName ?? Primitive.getTypeName(single.primitiveType) return { name: single.name, aliases: formattedAliases, - type: single.typeName ?? Primitive.getTypeName(single.primitiveType), + type: single.optionalValue === undefined ? type : `[=${type}]`, description: appendChoiceKeys(single.description, Primitive.getChoiceKeys(single.primitiveType)), required: single.primitiveType._tag !== "Boolean" && !metadata.isOptional } diff --git a/repos/effect/packages/effect/src/cli/internal/completions/bash.ts b/packages/effect/src/cli/internal/completions/bash.ts similarity index 98% rename from repos/effect/packages/effect/src/cli/internal/completions/bash.ts rename to packages/effect/src/cli/internal/completions/bash.ts index d71d0dfd1..2ccead6ce 100644 --- a/repos/effect/packages/effect/src/cli/internal/completions/bash.ts +++ b/packages/effect/src/cli/internal/completions/bash.ts @@ -7,6 +7,7 @@ * @internal */ import type * as Completions from "../../Completions.ts" +import { takesSeparateValue } from "./descriptor.ts" // --------------------------------------------------------------------------- // Helpers @@ -164,7 +165,7 @@ const generateFunction = ( lines.push(``) // Build flag-value dispatch - const flagsWithValues = descriptor.flags.filter((f) => f.type._tag !== "Boolean") + const flagsWithValues = descriptor.flags.filter(takesSeparateValue) if (flagsWithValues.length > 0) { lines.push(` # Flag value completions`) lines.push(` case "$prev" in`) @@ -196,7 +197,7 @@ const generateFunction = ( lines.push(` fi`) lines.push(` case "\${words[i]}" in`) for (const flag of descriptor.flags) { - if (flag.type._tag === "Boolean") continue + if (!takesSeparateValue(flag)) continue const forms = flagNamesForWordlist(flag) lines.push(` ${forms.join("|")}) _skip_next=1 ;;`) lines.push(` ${forms.map((form) => `${form}=*`).join("|")}) ;;`) @@ -248,7 +249,7 @@ const generateFunction = ( lines.push(` --) _end_of_options=1 ;;`) for (const flag of descriptor.flags) { const forms = flagNamesForWordlist(flag) - if (flag.type._tag === "Boolean") { + if (!takesSeparateValue(flag)) { lines.push(` ${forms.join("|")}) ;;`) } else { lines.push(` ${forms.join("|")}) _skip_next=1 ;;`) diff --git a/repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts b/packages/effect/src/cli/internal/completions/descriptor.ts similarity index 94% rename from repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts rename to packages/effect/src/cli/internal/completions/descriptor.ts index c84db2bc3..378d418b1 100644 --- a/repos/effect/packages/effect/src/cli/internal/completions/descriptor.ts +++ b/packages/effect/src/cli/internal/completions/descriptor.ts @@ -95,7 +95,8 @@ export const fromCommand = ( name: single.name, aliases: single.aliases, description: Option.getOrUndefined(single.description), - type: toFlagType(single as Param.Single<"flag", unknown>) + type: toFlagType(single as Param.Single<"flag", unknown>), + optionalValue: single.optionalValue !== undefined }) } } @@ -139,3 +140,11 @@ export const fromCommand = ( subcommands } } + +/** + * Whether the flag reads its value from the next word. + * + * @internal + */ +export const takesSeparateValue = (flag: Completions.FlagDescriptor): boolean => + flag.type._tag !== "Boolean" && flag.optionalValue !== true diff --git a/repos/effect/packages/effect/src/cli/internal/completions/fish.ts b/packages/effect/src/cli/internal/completions/fish.ts similarity index 96% rename from repos/effect/packages/effect/src/cli/internal/completions/fish.ts rename to packages/effect/src/cli/internal/completions/fish.ts index aaca1cfca..0aefa710b 100644 --- a/repos/effect/packages/effect/src/cli/internal/completions/fish.ts +++ b/packages/effect/src/cli/internal/completions/fish.ts @@ -7,6 +7,7 @@ * @internal */ import type * as Completions from "../../Completions.ts" +import { takesSeparateValue } from "./descriptor.ts" // --------------------------------------------------------------------------- // Helpers @@ -88,7 +89,7 @@ const flagCompletionArgs = (flag: Completions.FlagDescriptor): Array => if (flag.description) { args.push(`-d '${escapeFishString(flag.description)}'`) } - const valueArgs = flagValueArgs(flag.type) + const valueArgs = takesSeparateValue(flag) ? flagValueArgs(flag.type) : undefined if (valueArgs) { args.push(valueArgs) } @@ -159,8 +160,7 @@ const generateCompletions = ( // Flag completions for (const flag of descriptor.flags) { - const isBoolean = flag.type._tag === "Boolean" - const dedup = isBoolean ? flagContainsOptCondition(flag) : valueFlagDedupCondition(flag) + const dedup = takesSeparateValue(flag) ? valueFlagDedupCondition(flag) : flagContainsOptCondition(flag) const flagCondition = condition ? `${condition}; and ${dedup}` : dedup const flagCondArg = flagCondition ? `-n '${flagCondition}'` : `` @@ -170,7 +170,7 @@ const generateCompletions = ( lines.push(parts.join(" ")) // Boolean negation - if (isBoolean) { + if (flag.type._tag === "Boolean") { const negParts = [`complete -c ${executableName}`] if (flagCondArg) negParts.push(flagCondArg) negParts.push(`-l no-${flag.name}`) diff --git a/packages/effect/src/cli/internal/completions/powershell.ts b/packages/effect/src/cli/internal/completions/powershell.ts new file mode 100644 index 000000000..868d5a26a --- /dev/null +++ b/packages/effect/src/cli/internal/completions/powershell.ts @@ -0,0 +1,325 @@ +/** + * Static PowerShell completion script generator. + * + * Produces a self-contained completion script from a `CommandDescriptor`, + * without invoking the CLI at runtime. The script sticks to Windows + * PowerShell 5.1 syntax and APIs. + * + * @internal + */ +import type * as Completions from "../../Completions.ts" + +/** + * Encode non-ASCII and control characters as `[char]` code units. Windows + * PowerShell 5.1 can misread BOM-less UTF-8 as ANSI, turning some bytes into + * PowerShell quote delimiters. + */ +const quotePs = (s: string): string => { + const parts: Array = [] + let literal = "" + for (let i = 0; i < s.length; i++) { + const code = s.charCodeAt(i) + if (code >= 0x20 && code < 0x80) { + literal += s[i] === "'" ? "''" : s[i] + continue + } + if (literal !== "" || parts.length === 0) parts.push(`'${literal}'`) + literal = "" + parts.push(`[char]0x${code.toString(16).toUpperCase().padStart(4, "0")}`) + } + if (literal !== "" || parts.length === 0) parts.push(`'${literal}'`) + return parts.length === 1 ? parts[0] : `(${parts.join(" + ")})` +} + +const psArray = (items: ReadonlyArray): string => `@(${items.map(quotePs).join(", ")})` + +const psOptional = (s: string | undefined): string => s ? quotePs(s) : "$null" + +const sanitize = (s: string): string => s.replace(/[^a-zA-Z0-9_]/g, "_") + +const flagForms = (flag: Completions.FlagDescriptor): Array => [ + `--${flag.name}`, + ...flag.aliases.map((alias) => alias.length === 1 ? `-${alias}` : `--${alias}`) +] + +const valueFields = (type: Completions.FlagType | Completions.ArgumentType): string => + `values = ${psArray(type._tag === "Choice" ? type.values : [])}; ` + + `pathType = ${type._tag === "Path" ? quotePs(type.pathType) : "$null"}` + +const flagEntry = (flag: Completions.FlagDescriptor): string => + `@{ name = ${quotePs(flag.name)}; forms = ${psArray(flagForms(flag))}; ` + + `takesValue = $${flag.type._tag !== "Boolean"}; ` + + (flag.optionalValue === true ? `optionalValue = $true; ` : "") + + `${valueFields(flag.type)}; ` + + `description = ${psOptional(flag.description)} }` + +const argumentEntry = (argument: Completions.ArgumentDescriptor): string => + `@{ name = ${quotePs(argument.name)}; variadic = $${argument.variadic}; ${valueFields(argument.type)}; ` + + `description = ${psOptional(argument.description)} }` + +const pushList = (lines: Array, field: string, entries: ReadonlyArray): void => { + if (entries.length === 0) { + lines.push(` ${field} = @()`) + return + } + lines.push(` ${field} = @(`) + for (const entry of entries) { + lines.push(` ${entry}`) + } + lines.push(` )`) +} + +/** + * Emit one context per subcommand path. Subcommands are case-sensitive, so + * contexts go in an ordinal hashtable instead of a hash literal, whose keys + * ignore case. + */ +const generateContexts = ( + dataName: string, + descriptor: Completions.CommandDescriptor, + path: ReadonlyArray, + lines: Array, + seen: Set +): void => { + const key = path.join(" ") + if (seen.has(key)) return + seen.add(key) + lines.push(`${dataName}[${quotePs(key)}] = @{`) + pushList( + lines, + "subcommands", + descriptor.subcommands.map((sub) => + `@{ name = ${quotePs(sub.name)}; description = ${psOptional(sub.description)} }` + ) + ) + pushList(lines, "flags", descriptor.flags.map(flagEntry)) + pushList(lines, "arguments", descriptor.arguments.map(argumentEntry)) + lines.push(`}`) + for (const sub of descriptor.subcommands) { + generateContexts(dataName, sub, [...path, sub.name], lines, seen) + } +} + +/** + * Replay the committed words with the CLI lexer and parser rules: `--`, short + * flag clusters, inline values, boolean literals, negative numbers, and + * subcommand selection by the first value only. + */ +const completer = (dataName: string): string => + String.raw` param($wordToComplete, $commandAst, $cursorPosition) + + $spec = ${dataName} + $results = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new() + $booleanLiterals = @('true', 'yes', 'on', '1', 'y', 'false', 'no', 'off', '0', 'n') + + $index = { + param($context) + $lookup = [hashtable]::new([System.StringComparer]::Ordinal) + foreach ($flag in $context.flags) { + foreach ($form in $flag.forms) { $lookup[$form] = $flag } + } + $lookup + } + + $add = { + param([string]$value, [string]$type, $tooltip, [string]$typed, [string]$textPrefix) + if (-not $value.StartsWith($typed, [System.StringComparison]::OrdinalIgnoreCase)) { return } + $text = $value + if ($value -notmatch '^[\w./:%+=\\-]+$') { $text = "'" + ($value -replace "['\u2018-\u201b]", '$0$0') + "'" } + $text = $textPrefix + $text + $label = $value + if (-not $label) { $label = $text } + if (-not $tooltip) { $tooltip = $label } + $results.Add([System.Management.Automation.CompletionResult]::new($text, $label, $type, $tooltip)) + } + + $addValues = { + param($entry, [string]$typed, [string]$textPrefix) + foreach ($value in $entry.values) { + if ($textPrefix -eq '' -and (& $optionLike $value)) { continue } + & $add $value 'ParameterValue' $entry.description $typed $textPrefix + } + if ($entry.pathType) { + foreach ($file in [System.Management.Automation.CompletionCompleters]::CompleteFilename($typed)) { + if ($entry.pathType -eq 'directory' -and $file.ResultType -ne 'ProviderContainer') { continue } + $results.Add([System.Management.Automation.CompletionResult]::new($textPrefix + $file.CompletionText, $file.ListItemText, $file.ResultType, $file.ToolTip)) + } + } + } + + $addFlags = { + param([string]$typed) + foreach ($flag in $context.flags) { + if ($used.ContainsKey($flag.name)) { continue } + foreach ($form in $flag.forms) { + & $add $form 'ParameterName' $flag.description $typed '' + } + if (-not $flag.takesValue) { + $tooltip = $null + if ($flag.description) { $tooltip = 'Disable ' + $flag.name } + & $add ('--no-' + $flag.name) 'ParameterName' $tooltip $typed '' + } + } + } + + # As in the CLI lexer, nothing after '--' is an option, and negative numbers + # and a lone '-' are values. + $optionLike = { + param([string]$word) + -not $endOfOptions -and $word -match '^-.' -and $word -notmatch '^-(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$' + } + + # A typed value that has no candidates completes to itself, so the engine + # does not fall back to listing files. + $keepTyped = { + if ($results.Count -eq 0 -and $current) { + $results.Add([System.Management.Automation.CompletionResult]::new($current, $current, 'ParameterValue', $current)) + } + } + + $path = '' + $context = $spec[$path] + $lookup = & $index $context + $used = [hashtable]::new([System.StringComparer]::Ordinal) + $pending = $null + $endOfOptions = $false + $position = 0 + + # The engine strips the quotes from a quoted word. + $typed = [string]$wordToComplete + if ($typed -match '^([''"])(.*?)\1?$') { $typed = $Matches[2] } + $current = $null + + $elements = $commandAst.CommandElements + for ($i = 1; $i -lt $elements.Count; $i++) { + $element = $elements[$i] + $word = $element.Extent.Text + if ($element -is [System.Management.Automation.Language.StringConstantExpressionAst]) { $word = $element.Value } + if ($element.Extent.EndOffset -ge $cursorPosition) { + if ($element -is [System.Management.Automation.Language.ConstantExpressionAst] -and $element.Extent.StartOffset -le $cursorPosition) { + $current = $element.Extent.Text + if ($element.Extent.EndOffset -eq $cursorPosition) { $typed = $word } + } + break + } + + # A flag takes the next word as its value unless it is an option or '--'; + # a boolean flag only takes a boolean literal. + $isOption = & $optionLike $word + if ($null -ne $pending) { + $flag = $pending + $pending = $null + if ($flag.takesValue) { + if (-not $isOption) { continue } + } elseif ($booleanLiterals -ccontains $word) { continue } + } + if ($endOfOptions) { $position++; continue } + if ($word -ceq '--') { $endOfOptions = $true; continue } + + if ($isOption) { + $hasValue = $word.Contains('=') + if ($hasValue) { + $forms = @($word.Substring(0, $word.IndexOf('='))) + } elseif ($word.StartsWith('--')) { + $forms = @($word) + } else { + $forms = @(foreach ($char in $word.Substring(1).ToCharArray()) { '-' + $char }) + } + foreach ($form in $forms) { + $pending = $null + $flag = $lookup[$form] + if ($null -eq $flag) { + if ($form.StartsWith('--no-')) { + $name = $form.Substring(5) + $flag = $lookup['--' + $name] + if ($null -ne $flag -and -not $flag.takesValue -and $flag.name -ceq $name) { $used[$name] = $true } + } + continue + } + $used[$flag.name] = $true + # A flag with an optional value only takes a value attached with '='. + if (-not $hasValue -and -not $flag.optionalValue) { $pending = $flag } + } + continue + } + + if ($position -eq 0 -and $context.subcommands.name -ccontains $word) { + if ($path) { $path = $path + ' ' + $word } else { $path = $word } + $context = $spec[$path] + $lookup = & $index $context + $used = [hashtable]::new([System.StringComparer]::Ordinal) + continue + } + $position++ + } + + $typedIsOption = & $optionLike $typed + if ($null -ne $pending -and $pending.takesValue -and -not $typedIsOption) { + & $addValues $pending $typed '' + & $keepTyped + return $results + } + + if ($typedIsOption) { + if ($typed -match '^(-[^=]+)=(.*)$') { + $form = $Matches[1] + $flag = $lookup[$form] + if ($null -ne $flag -and $flag.takesValue) { + & $addValues $flag $Matches[2] ($form + '=') + & $keepTyped + } + } else { + & $addFlags $typed + } + return $results + } + + if (-not $endOfOptions -and $position -eq 0) { + foreach ($sub in $context.subcommands) { + & $add $sub.name 'Command' $sub.description $typed '' + } + } + + $arguments = $context.arguments + if ($position -lt $arguments.Count) { + & $addValues $arguments[$position] $typed '' + } elseif ($arguments.Count -gt 0 -and $arguments[-1].variadic) { + & $addValues $arguments[-1] $typed '' + } + + if (-not $endOfOptions) { + & $addFlags $typed + } + return $results` + +/** @internal */ +export const generate = ( + executableName: string, + descriptor: Completions.CommandDescriptor +): string => { + const dataName = `$global:_${sanitize(executableName)}_Completions` + const lines: Array = [] + + lines.push(`###-begin-${executableName}-completions-###`) + lines.push(`#`) + lines.push(`# Static completion script for PowerShell`) + lines.push(`#`) + lines.push(`# Installation:`) + // `>>` on Windows PowerShell 5.1 appends UTF-16LE to the profile, so the + // script goes in its own file. + lines.push(`# ${executableName} --completions powershell > ${executableName}-completion.ps1`) + lines.push(`# then add this line to your $PROFILE (use the full path to the file):`) + lines.push(`# . \\${executableName}-completion.ps1`) + lines.push(`#`) + lines.push(``) + lines.push(`${dataName} = [hashtable]::new([System.StringComparer]::Ordinal)`) + generateContexts(dataName, descriptor, [], lines, new Set()) + lines.push(``) + lines.push(`Register-ArgumentCompleter -Native -CommandName ${quotePs(executableName)} -ScriptBlock {`) + lines.push(completer(dataName)) + lines.push(`}`) + lines.push(``) + lines.push(`###-end-${executableName}-completions-###`) + + return lines.join("\n") +} diff --git a/repos/effect/packages/effect/src/cli/internal/completions/zsh.ts b/packages/effect/src/cli/internal/completions/zsh.ts similarity index 90% rename from repos/effect/packages/effect/src/cli/internal/completions/zsh.ts rename to packages/effect/src/cli/internal/completions/zsh.ts index e7a58ce8e..334bf4218 100644 --- a/repos/effect/packages/effect/src/cli/internal/completions/zsh.ts +++ b/packages/effect/src/cli/internal/completions/zsh.ts @@ -14,6 +14,8 @@ import type * as Completions from "../../Completions.ts" const escapeZsh = (s: string): string => s.replace(/\\/g, "\\\\").replace(/'/g, "'\\''").replace(/:/g, "\\:") +const escapeZshExplanation = (s: string): string => escapeZsh(s).replace(/]/g, "\\]") + /** Escape choices for the second parse of a Zsh `_arguments` action. */ const escapeZshChoice = (s: string): string => s.replace(/[^A-Za-z0-9_.,/@%+-]/gu, "\\$&").replace(/'/g, "'\\''") @@ -69,22 +71,26 @@ const argAction = (type: Completions.ArgumentType): string => { */ const flagSpecs = (flag: Completions.FlagDescriptor): Array => { const specs: Array = [] - const desc = flag.description ? `[${escapeZsh(flag.description)}]` : "" - const action = valueAction(flag.type) + const desc = flag.description ? `[${escapeZshExplanation(flag.description)}]` : "" + // An optional value must follow `=` in the same word (`--name=-`), and `::` + // marks it optional. + const optional = flag.optionalValue === true && flag.type._tag !== "Boolean" + const valueSuffix = optional ? "=-" : "" + const action = optional ? `:${valueAction(flag.type)}` : valueAction(flag.type) const excl = `(${allForms(flag).join(" ")})` // Primary long name - specs.push(`'${excl}--${flag.name}${desc}${action}'`) + specs.push(`'${excl}--${flag.name}${valueSuffix}${desc}${action}'`) // Aliases for (const alias of flag.aliases) { const prefix = alias.length === 1 ? "-" : "--" - specs.push(`'${excl}${prefix}${alias}${desc}${action}'`) + specs.push(`'${excl}${prefix}${alias}${valueSuffix}${desc}${action}'`) } // Boolean negation if (flag.type._tag === "Boolean") { - const negDesc = flag.description ? `[${escapeZsh(`Disable ${flag.name}`)}]` : "" + const negDesc = flag.description ? `[${escapeZshExplanation(`Disable ${flag.name}`)}]` : "" specs.push(`'${excl}--no-${flag.name}${negDesc}'`) } diff --git a/repos/effect/packages/effect/src/cli/internal/config.ts b/packages/effect/src/cli/internal/config.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/config.ts rename to packages/effect/src/cli/internal/config.ts diff --git a/repos/effect/packages/effect/src/cli/internal/help.ts b/packages/effect/src/cli/internal/help.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/help.ts rename to packages/effect/src/cli/internal/help.ts diff --git a/repos/effect/packages/effect/src/cli/internal/lexer.ts b/packages/effect/src/cli/internal/lexer.ts similarity index 100% rename from repos/effect/packages/effect/src/cli/internal/lexer.ts rename to packages/effect/src/cli/internal/lexer.ts diff --git a/repos/effect/packages/effect/src/cli/internal/parser.ts b/packages/effect/src/cli/internal/parser.ts similarity index 98% rename from repos/effect/packages/effect/src/cli/internal/parser.ts rename to packages/effect/src/cli/internal/parser.ts index 627a21410..2e77922d4 100644 --- a/repos/effect/packages/effect/src/cli/internal/parser.ts +++ b/packages/effect/src/cli/internal/parser.ts @@ -425,8 +425,9 @@ const asBooleanLiteral = (token: Token | undefined): string | undefined => * * Value resolution order: * 1. Inline value: --flag=value or -f=value - * 2. Boolean special case: implicit "true" or explicit boolean literal - * 3. Next token: consume following Value token if present + * 2. Optional value: the flag's configured value, without consuming a token + * 3. Boolean special case: implicit "true" or explicit boolean literal + * 4. Next token: consume following Value token if present */ const consumeFlagValue = ( cursor: TokenCursor, @@ -465,7 +466,7 @@ const consumeFlagValueWithTokens = ( } } - const literal = asBooleanLiteral(cursor.peek()) + const literal = spec.optionalValue === undefined ? asBooleanLiteral(cursor.peek()) : undefined if (literal !== undefined) { const literalToken = cursor.take() return { @@ -490,6 +491,14 @@ const consumeFlagValueWithTokens = ( } } + if (spec.optionalValue !== undefined) { + return { + _tag: "Value", + value: spec.optionalValue, + tokens: [] + } + } + // Boolean flags: check for explicit literal or default to "true" if (Primitive.isBoolean(spec.primitiveType)) { const literal = asBooleanLiteral(cursor.peek()) diff --git a/repos/effect/packages/effect/src/cli/internal/wizard.ts b/packages/effect/src/cli/internal/wizard.ts similarity index 82% rename from repos/effect/packages/effect/src/cli/internal/wizard.ts rename to packages/effect/src/cli/internal/wizard.ts index f4ddbebe8..6a55aaf9b 100644 --- a/repos/effect/packages/effect/src/cli/internal/wizard.ts +++ b/packages/effect/src/cli/internal/wizard.ts @@ -34,8 +34,9 @@ export const run: ( const commandPath = options?.commandPath ?? [command.name] const selected = getCommandAtPath(command, commandPath) const commandLine = (options?.prefix ?? commandPath).map((value) => commandLineArg(value)) - yield* logCurrentCommand(commandLine) - yield* promptCommand(selected, commandLine, selected === command ? "ROOT" : selected.name) + const { colors } = yield* Prompt.Theme + yield* logCurrentCommand(commandLine, colors) + yield* promptCommand(selected, commandLine, selected === command ? "ROOT" : selected.name, colors) return { args: commandLine.map((arg) => arg.value), displayArgs: commandLine.map((arg) => arg.displayValue) @@ -63,30 +64,31 @@ const getCommandAtPath = ( const promptCommand: ( command: Command.Command.Any, commandLine: Array, - sectionName: string + sectionName: string, + colors: boolean ) => Effect.Effect = Effect.fnUntraced( - function*(command, commandLine, sectionName) { + function*(command, commandLine, sectionName, colors) { const impl = toImpl(command) const visibleSubcommands = command.subcommands.flatMap((group) => group.commands.filter((child) => !child.unlisted)) const config = visibleSubcommands.length === 0 ? impl.config : impl.contextConfig if (config.flags.length > 0) { - yield* Console.log(renderSection(sectionName, "FLAGS")) + yield* Console.log(renderSection(sectionName, "FLAGS", colors)) for (const param of config.flags) { commandLine.push(...yield* promptParam(param)) } if (config.arguments.length > 0 || visibleSubcommands.length > 0) { - yield* logCurrentCommand(commandLine) + yield* logCurrentCommand(commandLine, colors) } } if (config.arguments.length > 0) { - yield* Console.log(renderSection(sectionName, "ARGUMENTS")) + yield* Console.log(renderSection(sectionName, "ARGUMENTS", colors)) for (const param of config.arguments) { commandLine.push(...yield* promptParam(param)) } if (visibleSubcommands.length > 0) { - yield* logCurrentCommand(commandLine) + yield* logCurrentCommand(commandLine, colors) } } @@ -109,9 +111,9 @@ const promptCommand: ( yield* Console.log() commandLine.push(commandLineArg(child.name)) if (hasWizardSteps(child)) { - yield* logCurrentCommand(commandLine) + yield* logCurrentCommand(commandLine, colors) } - yield* promptCommand(child, commandLine, child.name) + yield* promptCommand(child, commandLine, child.name, colors) } ) @@ -169,8 +171,9 @@ const promptParam = Effect.fnUntraced( return values } // Inline option-looking values so the lexer cannot interpret them as flags or the -- delimiter. + // Flags with an optional value only accept an inline value. return values.flatMap((value) => - value.value.startsWith("-") && value.value.length > 1 + single.optionalValue !== undefined || (value.value.startsWith("-") && value.value.length > 1) ? [commandLineArg(`--${single.name}=${value.value}`, `--${single.name}=${value.displayValue}`)] : [commandLineArg(`--${single.name}`), value] ) @@ -244,41 +247,47 @@ const humanize = (name: string): string => { return [words[0][0].toUpperCase() + words[0].slice(1), ...words.slice(1)].join(" ") } -const logCurrentCommand = (commandLine: ReadonlyArray): Effect.Effect => - Console.log(renderCommandBlock("Current command", commandLine.map((arg) => arg.displayValue), Ansi.magenta)) +const logCurrentCommand = (commandLine: ReadonlyArray, colors: boolean): Effect.Effect => + Console.log(renderCommandBlock("Current command", commandLine.map((arg) => arg.displayValue), colors, Ansi.magenta)) -const renderSection = (commandName: string, section: string): string => - `${Ansi.annotate(commandName.toUpperCase(), Ansi.bold, Ansi.cyanBright)} ${Ansi.annotate("·", Ansi.blackBright)} ${ - Ansi.annotate(section, Ansi.bold, Ansi.white) - }` +const renderSection = (commandName: string, section: string, colors: boolean): string => + `${Ansi.annotate(colors, commandName.toUpperCase(), Ansi.bold, Ansi.cyanBright)} ${ + Ansi.annotate(colors, "·", Ansi.blackBright) + } ${Ansi.annotate(colors, section, Ansi.bold, Ansi.white)}` -export const renderIntroduction = (name: string, version: string, summary: string | undefined): string => { - const title = `${Ansi.annotate(name, Ansi.bold, Ansi.cyanBright)} ${Ansi.annotate(`v${version}`, Ansi.white)} ${ - Ansi.annotate("· Command wizard", Ansi.bold, Ansi.white) - }` +export const renderIntroduction = ( + name: string, + version: string, + summary: string | undefined, + colors: boolean +): string => { + const title = `${Ansi.annotate(colors, name, Ansi.bold, Ansi.cyanBright)} ${ + Ansi.annotate(colors, `v${version}`, Ansi.white) + } ${Ansi.annotate(colors, "· Command wizard", Ansi.bold, Ansi.white)}` return [ title, ...(summary === undefined || summary.length === 0 ? [] : [summary]), - Ansi.annotate("Build a command interactively. Press Ctrl+C to cancel.", Ansi.blackBright), + Ansi.annotate(colors, "Build a command interactively. Press Ctrl+C to cancel.", Ansi.blackBright), "" ].join("\n") } -export const renderCompletion = (commandLine: ReadonlyArray): string => - renderCommandBlock("Command ready", commandLine, Ansi.cyanBright, Ansi.green) +export const renderCompletion = (commandLine: ReadonlyArray, colors: boolean): string => + renderCommandBlock("Command ready", commandLine, colors, Ansi.cyanBright, Ansi.green) -export const renderQuit = (): string => `\n${Ansi.annotate("Wizard cancelled.", Ansi.red)}` +export const renderQuit = (colors: boolean): string => `\n${Ansi.annotate(colors, "Wizard cancelled.", Ansi.red)}` const renderCommandBlock = ( label: string, commandLine: ReadonlyArray, + colors: boolean, commandColor: string, labelColor: string = Ansi.white ): string => { const lines = wrapCommand(commandLine) return [ - Ansi.annotate(label, Ansi.bold, labelColor), - ...lines.map((line) => Ansi.annotate(line, commandColor)), + Ansi.annotate(colors, label, Ansi.bold, labelColor), + ...lines.map((line) => Ansi.annotate(colors, line, commandColor)), "" ].join("\n") } diff --git a/repos/effect/packages/effect/src/cluster/ClusterCron.ts b/packages/effect/src/cluster/ClusterCron.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterCron.ts rename to packages/effect/src/cluster/ClusterCron.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterError.ts b/packages/effect/src/cluster/ClusterError.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterError.ts rename to packages/effect/src/cluster/ClusterError.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterMetrics.ts b/packages/effect/src/cluster/ClusterMetrics.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ClusterMetrics.ts rename to packages/effect/src/cluster/ClusterMetrics.ts diff --git a/repos/effect/packages/effect/src/cluster/ClusterSchema.ts b/packages/effect/src/cluster/ClusterSchema.ts similarity index 79% rename from repos/effect/packages/effect/src/cluster/ClusterSchema.ts rename to packages/effect/src/cluster/ClusterSchema.ts index c6744cd31..f46606f02 100644 --- a/repos/effect/packages/effect/src/cluster/ClusterSchema.ts +++ b/packages/effect/src/cluster/ClusterSchema.ts @@ -62,11 +62,22 @@ export const Persisted = Context.Reference("effect/cluster/ClusterSchem * The default value is `false`. When `true`, entity handling wraps server * writes with the configured storage transaction. * + * Persisted requests deliver terminal replies and mark requests processed only + * after commit or clean rollback. Success replies are saved in the transaction; + * failures are saved after rollback. If commit reports failure, a stored success + * reply is recovered. Otherwise, commit or rollback failures retry the request + * without delivering a reply. Handlers must tolerate retries. + * * **Gotchas** * * This annotation has transactional behavior only when the configured * `MessageStorage` implements it. * + * Stream chunks are saved and delivered immediately, even if the transaction + * later rolls back. A handler returning a `Deferred` commits before its reply + * is saved. Non-persisted requests save no reply; successes are delivered when + * the handler exits, and failures after the transaction exits. + * * @stability unstable * @category services * @since 4.0.0 @@ -95,6 +106,27 @@ export const Uninterruptible = Context.Reference( { defaultValue: constFalse } ) +/** + * Annotation that interrupts a cluster request when entity termination starts, + * rather than waiting for the termination grace period to expire. + * + * **Details** + * + * The default value is `false`. Use for long-lived requests that cannot finish + * on their own. Persisted requests resume under the next owner without saving + * the interruption as a reply. Non-persisted requests receive an interrupt exit. + * + * Server-side `Uninterruptible` takes precedence over this annotation. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export const InterruptOnTermination = Context.Reference( + "effect/cluster/ClusterSchema/InterruptOnTermination", + { defaultValue: constFalse } +) + /** * Returns whether the `Uninterruptible` annotation applies to server-side * request handling for the provided context. diff --git a/repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts b/packages/effect/src/cluster/ClusterWorkflowEngine.ts similarity index 79% rename from repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts rename to packages/effect/src/cluster/ClusterWorkflowEngine.ts index 357d06184..8e0c3be7b 100644 --- a/repos/effect/packages/effect/src/cluster/ClusterWorkflowEngine.ts +++ b/packages/effect/src/cluster/ClusterWorkflowEngine.ts @@ -15,7 +15,6 @@ import * as Effect from "../Effect.ts" import * as Exit from "../Exit.ts" import * as Fiber from "../Fiber.ts" import * as Headers from "../http/Headers.ts" -import * as Latch from "../Latch.ts" import * as Layer from "../Layer.ts" import * as Option from "../Option.ts" import * as PrimaryKey from "../PrimaryKey.ts" @@ -132,7 +131,6 @@ export const make = Effect.gen(function*() { readonly context: Context.Context }>() const interruptedActivities = new Set() - const activityLatches = new Map() const deferredState = WorkflowEngine.makeDeferredState() // Rebuilds share an activation; overlapping activations have separate results. // Weak keys do not retain scopes, but contexts captured by clients, activities, @@ -284,7 +282,7 @@ export const make = Effect.gen(function*() { yield* storage.clearAddress(clockAddress) }) - const resume = Effect.fnUntraced(function*(workflow: Workflow.Any, executionId: string) { + const resume = Effect.fnUntraced(function*(workflow: Workflow.Any, executionId: string, mode: WakeMode) { const maybeReply = yield* requestReply({ workflow, entityType: `Workflow/${workflow._tag}`, @@ -293,12 +291,11 @@ export const make = Effect.gen(function*() { id: "" }) - const maybeSuspended = Option.filter( - maybeReply, - (reply) => reply.exit._tag === "Success" && reply.exit.value._tag === "Suspended" - ) + const maybeSuspended = Option.filter(maybeReply, isSuspendedReply) if (Option.isNone(maybeSuspended)) return + // Replies without `failed` retain automatic wakes for compatibility. + if (mode === "automatic" && isFailedReply(maybeSuspended.value)) return if ( !(yield* sharding.reset( Snowflake.Snowflake(maybeSuspended.value.requestId), @@ -310,6 +307,15 @@ export const make = Effect.gen(function*() { yield* sharding.pollStorage }) + // Activations load a persisted interrupt when they start, so the per-call + // check reads only memory. + const interruptPending = (instance: WorkflowEngine.WorkflowInstance["Service"]) => + Effect.map(Effect.serviceOption(CurrentActivationScope), (activation) => { + const pending = Option.isSome(activation) ? pendingByActivation.get(activation.value) : undefined + return pending?.workflowName === instance.workflow._tag && pending.executionId === instance.executionId && + pending.results.has(InterruptSignal.name) + }) + const sendResumeParent = Effect.fnUntraced(function*(options: { readonly workflowName: string readonly executionId: string @@ -330,10 +336,14 @@ export const make = Effect.gen(function*() { yield* sharding.reset(requestId.value) }, Effect.scoped) - // A child or deferred may complete while a run is still unwinding. Subscribe - // to the run's reply before reading storage so the wake cannot fall between - // the run's last read and its suspended reply. - const waitForRunReply = Effect.fnUntraced(function*(workflow: Workflow.Any, request: Entity.Request) { + // Subscribe to the run's next reply before reading storage, so a reply + // cannot fall between the read and the subscription. Returns `true` when the + // stored reply already settles the wait, `false` when a new reply arrived. + const awaitRunReply = Effect.fnUntraced(function*( + workflow: Workflow.Any, + request: Envelope.Request, + isSettled: (reply: WithExitEncoded>) => boolean + ) { const waiter = yield* storage.registerReplyHandler( new Message.OutgoingRequest({ envelope: request, @@ -345,12 +355,45 @@ export const make = Effect.gen(function*() { }) ).pipe(Effect.forkScoped({ startImmediately: true })) const reply = yield* replyForRequestId(request.requestId) - if (Option.isSome(reply)) return - yield* Fiber.join(waiter).pipe( + if (Option.isSome(reply) && isSettled(reply.value)) return true + yield* Fiber.join(waiter) + return false + }, Effect.scoped) + + // A child or deferred may complete while a run is still unwinding. Wait for + // the run's reply so the wake cannot fall between the run's last read and + // its suspended reply. + const waitForRunReply = (workflow: Workflow.Any, request: Entity.Request) => + awaitRunReply(workflow, request, () => true).pipe( + Effect.asVoid, Effect.catchTag("EntityNotAssignedToRunner", () => ClusterAbandon.interrupt), Effect.catchCause((cause) => ClusterAbandon.isCause(cause) ? ClusterAbandon.interrupt : Effect.failCause(cause)) ) - }, Effect.scoped) + + // Wake an external caller once its run publishes a reply other than + // `Suspended`. Replies are only observed on the runner that saves them, so + // callers on other runners, and any failure here, leave delivery to the + // caller's retry schedule. + const awaitComplete = (workflow: Workflow.Any, executionId: string) => + Effect.gen(function*() { + const address = entityAddressFor({ workflow, entityType: `Workflow/${workflow._tag}`, executionId }) + if (!sharding.hasShardId(address.shardId)) return yield* Effect.never + const requestId = yield* storage.requestIdForPrimaryKey({ address, tag: "run", id: "" }) + if (Option.isNone(requestId)) return yield* Effect.never + const request = Envelope.makeRequest({ + requestId: requestId.value, + address, + tag: "run", + payload: {}, + headers: Headers.empty + }) + // A replay that suspends again re-arms the wait, so it does not spend a + // step of the caller's retry schedule. + let settled = false + while (!settled) { + settled = yield* awaitRunReply(workflow, request, (reply) => !isSuspendedReply(reply)) + } + }).pipe(Effect.catchCause(() => Effect.never)) const interrupt = Effect.fnUntraced( function*(workflow: Workflow.Any, executionId: string) { @@ -369,7 +412,7 @@ export const make = Effect.gen(function*() { const nonSuspendedReply = Option.filter( reply, - (reply) => reply.exit._tag !== "Success" || reply.exit.value._tag !== "Suspended" + (reply) => !isSuspendedReply(reply) ) if (Option.isSome(nonSuspendedReply)) { return Option.none() @@ -404,7 +447,25 @@ export const make = Effect.gen(function*() { const activation = yield* CurrentActivationScope let pending = pendingByActivation.get(activation) if (!pending) { - pending = { workflowName: workflow._tag, executionId, results: new Map() } + const results = new Map>() + // A new owner must see an interrupt persisted before it took over. + // Publish the cache only once loaded, so a retried build repeats + // a failed lookup. + const interrupt = yield* Effect.orDie(requestReply({ + workflow, + entityType: `Workflow/${workflow._tag}`, + executionId, + tag: "deferred", + id: InterruptSignal.name + })) + if (Option.isSome(interrupt)) { + const decoded = decodeDeferredWithExit(interrupt.value as any) + results.set( + InterruptSignal.name, + (decoded.exit._tag === "Success" ? decoded.exit.value : decoded.exit) as Exit.Exit + ) + } + pending = { workflowName: workflow._tag, executionId, results } pendingByActivation.set(activation, pending) } // Retain awaited names through reply persistence, including suspension. @@ -412,25 +473,30 @@ export const make = Effect.gen(function*() { let currentRun: { readonly request: Entity.Request readonly instance: WorkflowEngine.WorkflowInstance["Service"] + // Activity awaits must wake the run after suspension without preempting it. + readonly activityAwaits: Map } | undefined // Concurrent wakes share one wait for the current run to publish its reply. + // Explicit wakes cannot be dropped: operator resume bypasses the gate; + // InterruptSignal enters it only on a fresh entity, with no run to wait for. const resumeGate = Semaphore.makeUnsafe(1) - const resumeCurrentRun = Effect.suspend(() => - resumeGate.withPermitsIfAvailable(1)( - currentRun ? waitForRunReply(workflow, currentRun.request) : Effect.void + const resumeCurrentRun = (mode: WakeMode) => + Effect.suspend(() => + resumeGate.withPermitsIfAvailable(1)( + currentRun ? waitForRunReply(workflow, currentRun.request) : Effect.void + ) + ).pipe( + // Release the gate before reset can start another run, so later + // completions can wake that replay. + Effect.flatMap((waited) => Option.isSome(waited) ? resume(workflow, executionId, mode) : Effect.void), + ensureSuccess ) - ).pipe( - // Release the gate before reset can start another run, so later - // completions can wake that replay. - Effect.flatMap((waited) => Option.isSome(waited) ? resume(workflow, executionId) : Effect.void), - ensureSuccess - ) return { run: (request: Entity.Request) => { const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId) - currentRun = { request, instance } + currentRun = { request, instance, activityAwaits: new Map() } const parent = (request.payload as any)[payloadParentKey] as - | { workflowName: string; executionId: string } + | { workflowName: string; executionId: string; detached?: boolean } | undefined return execute(workflow.payloadSchema.make(request.payload) as object, executionId).pipe( Effect.onExit((exit) => { @@ -447,7 +513,9 @@ export const make = Effect.gen(function*() { !instance.abandoned && !(suspendOnFailure && exit._tag === "Failure") ) { - return parent ? ensureSuccess(sendResumeParent(parent, executionId)) : Effect.void + return parent && !parent.detached + ? ensureSuccess(sendResumeParent(parent, executionId)) + : Effect.void } return engine.deferredResult(InterruptSignal).pipe( Effect.flatMap((maybeExit) => { @@ -474,25 +542,23 @@ export const make = Effect.gen(function*() { activity(request: Entity.Request) { const payload = request.payload as { name: string; attempt: number } const activityId = `${executionId}/${payload.name}` - const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId) + const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId, { + awaitedDeferreds: currentRun?.activityAwaits + }) interruptedActivities.delete(activityId) - return Effect.gen(function*() { - let entry = activities.get(activityId) - while (!entry) { - const latch = Latch.makeUnsafe() - activityLatches.set(activityId, latch) - yield* latch.await - entry = activities.get(activityId) - } - const context = entry.context.pipe( - Context.add(WorkflowEngine.WorkflowInstance, instance), - Context.add(CurrentActivationScope, activation), - Context.add(Activity.CurrentAttempt, payload.attempt) - ) - return yield* entry.activity.executeEncoded.pipe( - Effect.provideContext(context) - ) - }).pipe( + const entry = activities.get(activityId) + if (!entry) { + // Replay may never register a losing race activity. Answer it + // without waiting; a later registration can reset this reply. + return Effect.succeed(new Workflow.Suspended({})).pipe(Rpc.wrap({ fork: true })) + } + const context = entry.context.pipe( + Context.add(WorkflowEngine.WorkflowInstance, instance), + Context.add(CurrentActivationScope, activation), + Context.add(Activity.CurrentAttempt, payload.attempt) + ) + return entry.activity.executeEncoded.pipe( + Effect.provideContext(context), Workflow.intoResult, Effect.catchCause((cause) => { // we only want to store interrupts as suspends when the @@ -518,18 +584,24 @@ export const make = Effect.gen(function*() { deferred: (request: Entity.Request) => { const payload = request.payload as any pending.results.set(payload.name, payload.exit) + const mode: WakeMode = payload.name === InterruptSignal.name ? "explicit" : "automatic" // Await registers before reading the cache. If this run has not // registered yet, a later await will see the result just stored. // Check when the wake runs and skip preemption too: interrupting // a later await would require waiting for its reply before reset. // A run completing its own deferred also reads it from the cache - // when it awaits later, so that completion never wakes the run. + // when it awaits later, unless an activity is already parked on it. const wake = Effect.suspend(() => currentRun && !currentRun.instance.awaitedDeferreds.has(payload.name) - ? currentRun.instance.completedDeferreds.has(payload.name) + ? currentRun.activityAwaits.has(payload.name) + // Hold an activity wake until the run has published its reply. + ? resumeCurrentRun(mode) + : currentRun.instance.completedDeferreds.has(payload.name) ? Effect.void - : ensureSuccess(resume(workflow, executionId)) - : deferredState.deferredDone(executionId, payload.name).pipe(Effect.andThen(resumeCurrentRun)) + : ensureSuccess(resume(workflow, executionId, mode)) + : deferredState.deferredDone(executionId, payload.name).pipe( + Effect.andThen(resumeCurrentRun(mode)) + ) ) // An asynchronous reply releases the RPC concurrency permit while // the wake waits; the pending request still occupies mailbox capacity. @@ -544,7 +616,7 @@ export const make = Effect.gen(function*() { ) }, - resume: () => resumeCurrentRun.pipe(Rpc.wrap({ fork: true, uninterruptible: true })) + resume: () => resumeCurrentRun("automatic").pipe(Rpc.wrap({ fork: true, uninterruptible: true })) } }), // Alongside the run, let deferred handlers briefly acquire a permit to @@ -561,7 +633,7 @@ export const make = Effect.gen(function*() { ).pipe(Effect.andThen(registration)) }), - execute: (workflow, { discard, executionId, parent, payload }) => { + execute: (workflow, { detached, discard, executionId, parent, payload }) => { ensureEntity(workflow) return RcMap.get(clients, workflow._tag).pipe( Effect.flatMap((make) => @@ -569,7 +641,10 @@ export const make = Effect.gen(function*() { parent ? { ...payload, - [payloadParentKey]: { workflowName: parent.workflow._tag, executionId: parent.executionId } + // A detached child keeps its parent for observability only. + [payloadParentKey]: detached + ? { workflowName: parent.workflow._tag, executionId: parent.executionId, detached: true } + : { workflowName: parent.workflow._tag, executionId: parent.executionId } } : payload, { discard } @@ -621,7 +696,9 @@ export const make = Effect.gen(function*() { )) }), - resume: (workflow, executionId) => ensureSuccess(resume(workflow, executionId)), + resume: (workflow, executionId) => ensureSuccess(resume(workflow, executionId, "explicit")), + + awaitComplete, activityExecute: Effect.fnUntraced( function*(activity, attempt) { @@ -629,15 +706,25 @@ export const make = Effect.gen(function*() { const instance = Context.get(services, WorkflowEngine.WorkflowInstance) yield* Effect.annotateCurrentSpan("executionId", instance.executionId) const activityId = `${instance.executionId}/${activity.name}` + // A pending interrupt replays only to rebuild compensations, so it must + // not run new activities. Finalizers run once `interrupted` is set. + if (!instance.interrupted && (yield* interruptPending(instance))) { + const reply = yield* Effect.orDie(requestReply({ + workflow: instance.workflow, + entityType: `Workflow/${instance.workflow._tag}`, + executionId: instance.executionId, + tag: "activity", + id: activityPrimaryKey(activity.name, attempt) + })) + // Suspend so `onExit` applies the interrupt to the run. + if (Option.isNone(reply) || isSuspendedReply(reply.value)) { + return new Workflow.Suspended({}) + } + } const client = (yield* RcMap.get(clientsPartial, instance.workflow._tag))(instance.executionId) while (true) { if (!activities.has(activityId)) { activities.set(activityId, { activity, context: services }) - const latch = activityLatches.get(activityId) - if (latch) { - yield* latch.open - activityLatches.delete(activityId) - } } const result = yield* Effect.orDie( client.activity({ @@ -820,6 +907,14 @@ const DeferredRpc = Rpc.make("deferred", { .annotate(ClusterSchema.Persisted, true) .annotate(ClusterSchema.Uninterruptible, true) +type WakeMode = "explicit" | "automatic" + +const isSuspendedReply = (reply: WithExitEncoded>) => + reply.exit._tag === "Success" && reply.exit.value._tag === "Suspended" + +const isFailedReply = (reply: WithExitEncoded>) => + reply.exit._tag === "Success" && reply.exit.value._tag === "Suspended" && reply.exit.value.failed === true + const decodeDeferredWithExit = Schema.decodeSync(Schema.toCodecJson(Reply.WithExit.schema(DeferredRpc))) const ResumeRpc = Rpc.make("resume", { @@ -847,7 +942,8 @@ const makeWorkflowEntity = (workflow: Workflow.Any) => ...workflow.payloadSchema.fields, [payloadParentKey]: Schema.optional(Schema.Struct({ workflowName: Schema.String, - executionId: Schema.String + executionId: Schema.String, + detached: Schema.optional(Schema.Boolean) })) }, primaryKey: () => "", diff --git a/repos/effect/packages/effect/src/cluster/DeliverAt.ts b/packages/effect/src/cluster/DeliverAt.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/DeliverAt.ts rename to packages/effect/src/cluster/DeliverAt.ts diff --git a/repos/effect/packages/effect/src/cluster/Entity.ts b/packages/effect/src/cluster/Entity.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Entity.ts rename to packages/effect/src/cluster/Entity.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityAddress.ts b/packages/effect/src/cluster/EntityAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityAddress.ts rename to packages/effect/src/cluster/EntityAddress.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityId.ts b/packages/effect/src/cluster/EntityId.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityId.ts rename to packages/effect/src/cluster/EntityId.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityProxy.ts b/packages/effect/src/cluster/EntityProxy.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityProxy.ts rename to packages/effect/src/cluster/EntityProxy.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityProxyServer.ts b/packages/effect/src/cluster/EntityProxyServer.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityProxyServer.ts rename to packages/effect/src/cluster/EntityProxyServer.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityResource.ts b/packages/effect/src/cluster/EntityResource.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityResource.ts rename to packages/effect/src/cluster/EntityResource.ts diff --git a/repos/effect/packages/effect/src/cluster/EntityType.ts b/packages/effect/src/cluster/EntityType.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/EntityType.ts rename to packages/effect/src/cluster/EntityType.ts diff --git a/repos/effect/packages/effect/src/cluster/Envelope.ts b/packages/effect/src/cluster/Envelope.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Envelope.ts rename to packages/effect/src/cluster/Envelope.ts diff --git a/repos/effect/packages/effect/src/cluster/HttpRunner.ts b/packages/effect/src/cluster/HttpRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/HttpRunner.ts rename to packages/effect/src/cluster/HttpRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/K8sHttpClient.ts b/packages/effect/src/cluster/K8sHttpClient.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/K8sHttpClient.ts rename to packages/effect/src/cluster/K8sHttpClient.ts diff --git a/repos/effect/packages/effect/src/cluster/K8sTypes.ts b/packages/effect/src/cluster/K8sTypes.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/K8sTypes.ts rename to packages/effect/src/cluster/K8sTypes.ts diff --git a/repos/effect/packages/effect/src/cluster/MachineId.ts b/packages/effect/src/cluster/MachineId.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/MachineId.ts rename to packages/effect/src/cluster/MachineId.ts diff --git a/repos/effect/packages/effect/src/cluster/Message.ts b/packages/effect/src/cluster/Message.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Message.ts rename to packages/effect/src/cluster/Message.ts diff --git a/repos/effect/packages/effect/src/cluster/MessageStorage.ts b/packages/effect/src/cluster/MessageStorage.ts similarity index 93% rename from repos/effect/packages/effect/src/cluster/MessageStorage.ts rename to packages/effect/src/cluster/MessageStorage.ts index 3822e3612..b1876c5c0 100644 --- a/repos/effect/packages/effect/src/cluster/MessageStorage.ts +++ b/packages/effect/src/cluster/MessageStorage.ts @@ -207,7 +207,12 @@ export class MessageStorage extends Context.Service Effect.Effect /** - * Used to wrap requests with transactions. + * Wraps requests in storage transactions. + * + * `WithExit` replies saved in a transaction notify callers after the outermost + * commit; stream chunks notify immediately. Replies saved in a failed + * transaction must be discarded. The wrapper may fail only with the wrapped + * effect's own failure or a defect. */ readonly withTransaction: ( effect: Effect.Effect @@ -513,6 +518,10 @@ export type EncodedRepliesOptions = { readonly cursor: Option.Option } +const TransactionNotifications = Context.Reference< + Array> | undefined +>("effect/cluster/MessageStorage/TransactionNotifications", { defaultValue: () => undefined }) + /** * Wraps a concrete message storage implementation with reply-handler management. * @@ -555,6 +564,20 @@ export const make = ( const replyHandlersShard = new Map>() return MessageStorage.of({ ...storage, + // Nested transactions defer notifications to the outermost commit. + withTransaction: (effect) => + TransactionNotifications.use((outer) => { + const notifications: Array> = [] + return storage.withTransaction(Effect.provideService(effect, TransactionNotifications, notifications)).pipe( + Effect.tap(() => + outer + ? Effect.sync(() => { + outer.push(...notifications) + }) + : Effect.orDie(Effect.forEach(notifications, identity, { discard: true })) + ) + ) + }), registerReplyHandler: (message) => { const requestId = message.envelope.requestId return Effect.callback((resume) => { @@ -619,20 +642,28 @@ export const make = ( saveReply(reply) { const requestId = reply.reply.requestId return Effect.flatMap(storage.saveReply(reply), (persisted) => { - const handlers = replyHandlers.get(requestId) - if (!handlers) { - return Effect.void - } else if (persisted.reply._tag === "WithExit") { - replyHandlers.delete(requestId) - for (let i = 0; i < handlers.length; i++) { - const handler = handlers[i] - handler.shardSet.delete(handler) - handler.resume(Effect.void) + const notify = Effect.suspend(() => { + const handlers = replyHandlers.get(requestId) + if (!handlers) { + return Effect.void + } else if (persisted.reply._tag === "WithExit") { + replyHandlers.delete(requestId) + for (let i = 0; i < handlers.length; i++) { + const handler = handlers[i] + handler.shardSet.delete(handler) + handler.resume(Effect.void) + } } - } - return handlers.length === 1 - ? handlers[0].respond(persisted) - : Effect.forEach(handlers, (handler) => handler.respond(persisted)) + return handlers.length === 1 + ? handlers[0].respond(persisted) + : Effect.forEach(handlers, (handler) => handler.respond(persisted), { discard: true }) + }) + if (persisted.reply._tag !== "WithExit") return notify + return TransactionNotifications.use((notifications) => + notifications === undefined ? notify : Effect.sync(() => { + notifications.push(notify) + }) + ) }) } }) @@ -1055,8 +1086,8 @@ export class MemoryDriver extends Context.Service()("effect/cluste journal.push(envelope) return SaveResultEncoded.Success() }), - saveReply: (reply_) => - Effect.sync(() => { + saveReply: (reply_) => { + const save = Effect.sync(() => { const reply = JSON.parse(JSON.stringify(reply_)) as Reply.Encoded const entry = requests.get(reply.requestId) if (!entry || replyIds.has(reply.id)) return @@ -1067,7 +1098,18 @@ export class MemoryDriver extends Context.Service()("effect/cluste entry.replies.push(reply) replyIds.add(reply.id) replyLatch.openUnsafe() - }), + }) + // Memory has no transaction isolation. Publish terminal replies at the + // outer commit boundary, before their queued handler notifications, so + // reply readers cannot deliver a success that may still be rolled back. + return TransactionNotifications.use((notifications) => + reply_._tag === "WithExit" && notifications + ? Effect.sync(() => { + notifications.push(save) + }) + : save + ) + }, clearReplies: (id, options) => Effect.sync(() => { const entry = requests.get(String(id)) diff --git a/repos/effect/packages/effect/src/cluster/Reply.ts b/packages/effect/src/cluster/Reply.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Reply.ts rename to packages/effect/src/cluster/Reply.ts diff --git a/repos/effect/packages/effect/src/cluster/Runner.ts b/packages/effect/src/cluster/Runner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Runner.ts rename to packages/effect/src/cluster/Runner.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerAddress.ts b/packages/effect/src/cluster/RunnerAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerAddress.ts rename to packages/effect/src/cluster/RunnerAddress.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerHealth.ts b/packages/effect/src/cluster/RunnerHealth.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerHealth.ts rename to packages/effect/src/cluster/RunnerHealth.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerServer.ts b/packages/effect/src/cluster/RunnerServer.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerServer.ts rename to packages/effect/src/cluster/RunnerServer.ts diff --git a/repos/effect/packages/effect/src/cluster/RunnerStorage.ts b/packages/effect/src/cluster/RunnerStorage.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/RunnerStorage.ts rename to packages/effect/src/cluster/RunnerStorage.ts diff --git a/repos/effect/packages/effect/src/cluster/Runners.ts b/packages/effect/src/cluster/Runners.ts similarity index 86% rename from repos/effect/packages/effect/src/cluster/Runners.ts rename to packages/effect/src/cluster/Runners.ts index f0c29d54c..7872bcaee 100644 --- a/repos/effect/packages/effect/src/cluster/Runners.ts +++ b/packages/effect/src/cluster/Runners.ts @@ -14,6 +14,7 @@ import * as Context from "../Context.ts" import * as Effect from "../Effect.ts" import * as Exit from "../Exit.ts" +import * as Fiber from "../Fiber.ts" import * as Latch from "../Latch.ts" import * as Layer from "../Layer.ts" import * as Option from "../Option.ts" @@ -247,72 +248,93 @@ export const make: ( type StorageRequestEntry = { readonly latch: Latch.Latch - doneLatch: Latch.Latch | undefined readonly messages: Set> + readonly pump: Fiber.Fiber replies: Array> } const storageRequests = new Map() const waitingStorageRequests = new Map>() - const replyFromStorage = Effect.fnUntraced( - function*(message: Message.OutgoingRequest) { - let entry = storageRequests.get(message.envelope.requestId) - if (entry) { - entry.messages.add(message) - entry.doneLatch ??= Latch.makeUnsafe(false) - return yield* entry.doneLatch.await - } else { - entry = { - latch: Latch.makeUnsafe(false), - doneLatch: undefined, - replies: [], - messages: new Set([message]) - } - storageRequests.set(message.envelope.requestId, entry) - } - while (true) { - // wait for the storage loop to notify us - entry.latch.closeUnsafe() - waitingStorageRequests.set(message.envelope.requestId, message) - storageLatch.openUnsafe() - yield* entry.latch.await - - // send the replies back - for (let i = 0; i < entry.replies.length; i++) { - const reply = entry.replies[i] - // we have reached the end - if (reply._tag === "WithExit") { - for (const message of entry.messages) { - yield* message.respond(reply) - } - entry.doneLatch?.openUnsafe() - return - } + // A reset request can register a replacement entry under the same id, which + // must survive the old entry's cleanup. + const removeStorageRequest = (requestId: Snowflake.Snowflake, entry: Omit) => { + if (storageRequests.get(requestId) !== entry) return + storageRequests.delete(requestId) + waitingStorageRequests.delete(requestId) + } - entry.latch.closeUnsafe() + // The pump is owned by the shared entry rather than the first caller, so any + // waiter can be interrupted without stopping delivery to the others. + const pumpReplies = Effect.fnUntraced(function*( + requestId: Snowflake.Snowflake, + entry: Omit + ) { + while (true) { + // wait for the storage loop to notify us + entry.latch.closeUnsafe() + const message = entry.messages.values().next().value + if (!message) return + waitingStorageRequests.set(requestId, message) + storageLatch.openUnsafe() + yield* entry.latch.await + + // send the replies back + for (let i = 0; i < entry.replies.length; i++) { + const reply = entry.replies[i] + // we have reached the end + if (reply._tag === "WithExit") { + // A reply can immediately reset and resend the same request. Do not + // let that sender attach to this completed entry. + removeStorageRequest(requestId, entry) for (const message of entry.messages) { yield* message.respond(reply) } - // wait for ack - yield* entry.latch.await + return } - entry.replies = [] + + entry.latch.closeUnsafe() + for (const message of entry.messages) { + yield* message.respond(reply) + } + // wait for ack + yield* entry.latch.await } - }, - (effect, message) => - Effect.ensuring( - effect, - Effect.sync(() => { - const entry = storageRequests.get(message.envelope.requestId) - if (!entry || entry.messages.size > 1) { - entry?.messages.delete(message) - return + entry.replies = [] + } + }) + + const replyFromStorage = (message: Message.OutgoingRequest) => + Effect.uninterruptibleMask((restore) => + Effect.gen(function*() { + const requestId = message.envelope.requestId + let entry = storageRequests.get(requestId) + if (!entry) { + const state = { + latch: Latch.makeUnsafe(false), + messages: new Set>(), + replies: [] as Array> } - storageRequests.delete(message.envelope.requestId) - waitingStorageRequests.delete(message.envelope.requestId) - }) - ) - ) + entry = Object.assign(state, { pump: yield* Effect.forkIn(pumpReplies(requestId, state), runnersScope) }) + storageRequests.set(requestId, entry) + } + const current = entry + current.messages.add(message) + return yield* restore(Fiber.join(current.pump)).pipe( + Effect.ensuring(Effect.suspend(() => { + current.messages.delete(message) + if (current.messages.size === 0) { + removeStorageRequest(requestId, current) + return Fiber.interrupt(current.pump) + } + // The storage loop decodes replies using the waiting message. + if (waitingStorageRequests.get(requestId) === message) { + waitingStorageRequests.set(requestId, current.messages.values().next().value!) + } + return Effect.void + })) + ) + }) + ) const storageLatch = Latch.makeUnsafe(false) if (storage !== MessageStorage.noop) { @@ -409,22 +431,28 @@ export const make: ( }) }, notifyLocal(options) { - return notifyWith(options.message, (message, duplicate) => { + return notifyWith(options.message, (message) => { if (options.discard || message._tag === "OutgoingEnvelope") { return Effect.catchTag( options.notify(Message.incomingLocalFromOutgoing(message)), "EntityNotAssignedToRunner", () => Effect.void ) - } else if (!duplicate && options.storageOnly !== true) { - return options.notify(Message.incomingLocalFromOutgoing(message)).pipe( - Effect.andThen(storage.registerReplyHandler(message)), - Effect.catchTag("EntityNotAssignedToRunner", () => replyFromStorage(message)) - ) } + // The request can be processed as soon as it is saved, so replies are + // always read back from storage. The local reply handler only wakes the + // storage loop, so streams do not have to wait for the next poll. + const readReplies = options.storageOnly + ? replyFromStorage(message) + : Effect.raceFirst( + replyFromStorage(message), + storage.registerReplyHandler( + new Message.OutgoingRequest({ ...message, respond: () => storageLatch.open }) + ).pipe(Effect.ignore, Effect.andThen(Effect.never)) + ) return options.notify(Message.incomingLocalFromOutgoing(message)).pipe( Effect.catchTag("EntityNotAssignedToRunner", () => Effect.void), - Effect.andThen(replyFromStorage(message)) + Effect.andThen(readReplies) ) }) } diff --git a/repos/effect/packages/effect/src/cluster/ShardId.ts b/packages/effect/src/cluster/ShardId.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ShardId.ts rename to packages/effect/src/cluster/ShardId.ts diff --git a/repos/effect/packages/effect/src/cluster/Sharding.ts b/packages/effect/src/cluster/Sharding.ts similarity index 87% rename from repos/effect/packages/effect/src/cluster/Sharding.ts rename to packages/effect/src/cluster/Sharding.ts index 43b634c65..2e088dfd7 100644 --- a/repos/effect/packages/effect/src/cluster/Sharding.ts +++ b/packages/effect/src/cluster/Sharding.ts @@ -277,6 +277,13 @@ const make = Effect.gen(function*() { // the active shards are the ones that we have acquired the lock for const acquiredShards = MutableHashSet.empty() const activeShardsLatch = yield* Latch.make(false) + // open once shutdown starts handing every shard off + const handoffLatch = Latch.makeUnsafe(false) + // open once the handoff has released every shard lock + const handedOffLatch = Latch.makeUnsafe(false) + // serializes runner registration with unregistration, so a registration + // in flight cannot re-advertise a runner that is shutting down + const withRegistrationLock = Semaphore.makeUnsafe(1).withPermits(1) const events = yield* PubSub.unbounded() const getRegistrationEvents: Stream.Stream = Stream.fromPubSub(events) @@ -295,6 +302,33 @@ const make = Effect.gen(function*() { return MutableHashSet.has(acquiredShards, address.shardId) } + function isAwaitingShardLock(shardId: ShardId): boolean { + return !MutableRef.get(isShutdown) && + MutableHashSet.has(selfShards, shardId) && + !MutableHashSet.has(acquiredShards, shardId) + } + + // Requests for a shard assigned to this runner wait for its lock, for at most + // `shardLockExpiration`, instead of bouncing while the previous owner drains. + // Each notification opens the current latch and replaces it, so a waiter that + // checked its shard before a notification cannot miss it. + let shardLocksChanged = Latch.makeUnsafe(false) + const notifyShardLockWaiters = Effect.suspend(() => { + const latch = shardLocksChanged + shardLocksChanged = Latch.makeUnsafe(false) + return latch.open + }) + const awaitShardLock = (shardId: ShardId): Effect.Effect => { + const loop: Effect.Effect = Effect.suspend(() => + isAwaitingShardLock(shardId) ? Effect.andThen(shardLocksChanged.await, loop) : Effect.void + ) + return Effect.suspend(() => + isAwaitingShardLock(shardId) + ? Effect.asVoid(Effect.timeoutOption(loop, config.shardLockExpiration)) + : Effect.void + ) + } + yield* Scope.addFinalizer( shardingScope, Effect.logDebug("Shutdown complete").pipe(Effect.annotateLogs({ @@ -310,6 +344,8 @@ const make = Effect.gen(function*() { // allow them to move to another runner. const releasingShards = MutableHashSet.empty() + // Hands every held shard off on shutdown, set when this is a runner + let handoff: Effect.Effect = Effect.void // Shards whose entities must be force interrupted and locks fully released // before normal reacquisition. const forceReleasingShards = MutableHashSet.empty() @@ -321,8 +357,37 @@ const make = Effect.gen(function*() { return Effect.ignore(runnerStorage.releaseAll(selfAddress)) }) + // Shard releases fork into a child scope that closes after the shutdown + // handoff, as forks into the closing sharding scope never run. + const releaseScope = yield* Scope.fork(shardingScope) + // Interrupt the shards' entities on every entity manager that is open + const forkInterruptShards = Effect.fnUntraced(function*(shardIds: Iterable, force: boolean) { + const fibers = Arr.empty>() + for (const shardId of shardIds) { + for (const state of entityManagers.values()) { + if (state.status === "closed") continue + fibers.push( + yield* Effect.forkIn(state.manager.interruptShard(shardId, { force }), releaseScope, { + startImmediately: true + }) + ) + } + } + return fibers + }) const releaseShardsMap = yield* FiberMap.make() let forcedShardReleaseRunning = false + // Called from the acquisition loop only, so no `acquire` is in flight + const checkHandedOff = () => { + if ( + handoffLatch.isOpen() && + MutableHashSet.size(acquiredShards) === 0 && + MutableHashSet.size(releasingShards) === 0 && + !forcedShardReleasePending() + ) { + handedOffLatch.openUnsafe() + } + } // Interrupt the shards' entities, wait for lock health, run the storage // release, then clear the shards' bookkeeping. const runShardRelease = Effect.fnUntraced(function*( @@ -330,14 +395,11 @@ const make = Effect.gen(function*() { force: boolean, release: Effect.Effect ) { - const fibers = Arr.empty>() - for (const shardId of shardIds) { - for (const state of entityManagers.values()) { - if (state.status === "closed") continue - fibers.push(yield* Effect.forkScoped(state.manager.interruptShard(shardId, { force }))) - } - } - yield* Fiber.joinAll(fibers) + yield* Fiber.joinAll(yield* forkInterruptShards(shardIds, force)) + // stop the singletons of shards that are no longer acquired, and wait + // for those on the released shards + yield* startSyncSingletons + yield* awaitStoppingSingletons(shardIds) yield* shardLocksHealthyLatch.await yield* release for (const shardId of shardIds) { @@ -351,6 +413,8 @@ const make = Effect.gen(function*() { interrupt: MutableRef.get(isShutdown) }) } + // let the acquisition loop check whether the handoff has finished + if (handoffLatch.isOpen()) activeShardsLatch.openUnsafe() }) const retryShardRelease = (annotations: { readonly fiber: string; readonly shardId?: ShardId }) => @@ -402,7 +466,7 @@ const make = Effect.gen(function*() { forcedShardReleaseRunning = false activeShardsLatch.openUnsafe() })), - Effect.forkIn(shardingScope), + Effect.forkIn(releaseScope), Effect.asVoid ) }) @@ -422,9 +486,11 @@ const make = Effect.gen(function*() { yield* activeShardsLatch.await activeShardsLatch.closeUnsafe() - // if a shard is no longer assigned to this runner, we release it + // if a shard is no longer assigned to this runner, or the runner is + // handing every shard off, we release it + const handingOff = handoffLatch.isOpen() for (const shardId of acquiredShards) { - if (MutableHashSet.has(selfShards, shardId)) continue + if (!handingOff && MutableHashSet.has(selfShards, shardId)) continue MutableHashSet.remove(acquiredShards, shardId) MutableHashSet.add(releasingShards, shardId) } @@ -433,6 +499,7 @@ const make = Effect.gen(function*() { yield* Effect.forkIn(syncSingletons, shardingScope) yield* releaseShards } + checkHandedOff() if (!shardLocksHealthyLatch.isOpen()) { continue @@ -444,6 +511,11 @@ const make = Effect.gen(function*() { continue } + // a runner that is shutting down takes no new shards + if (isShutdown.current) { + continue + } + // if a shard has been assigned to this runner, we acquire it const unacquiredShards = MutableHashSet.empty() for (const shardId of selfShards) { @@ -484,13 +556,15 @@ const make = Effect.gen(function*() { MutableHashSet.add(acquiredShards, shardId) } if (acquired.length > 0) { + yield* notifyShardLockWaiters yield* storageReadLatch.open yield* Effect.forkIn(syncSingletons, shardingScope) // update metrics ClusterMetrics.shards.updateUnsafe(BigInt(MutableHashSet.size(acquiredShards)), Context.empty()) } - yield* Effect.sleep(1000) + // the shutdown handoff releases the shards acquired here without waiting + yield* Effect.raceFirst(Effect.sleep(1000), handoffLatch.await) activeShardsLatch.openUnsafe() } }).pipe( @@ -519,19 +593,11 @@ const make = Effect.gen(function*() { activeShardsLatch.openUnsafe() return Effect.gen(function*() { + yield* notifyShardLockWaiters yield* Effect.logError("Shard lock storage is unhealthy", cause) yield* Effect.forkIn(syncSingletons, shardingScope, { startImmediately: true }) - for (const shardId of affectedShards) { - for (const state of entityManagers.values()) { - if (state.status === "closed") continue - yield* Effect.forkIn( - state.manager.interruptShard(shardId, { force: true }), - shardingScope, - { startImmediately: true } - ) - } - } + yield* forkInterruptShards(affectedShards, true) activeShardsLatch.openUnsafe() }) }) @@ -598,6 +664,18 @@ const make = Effect.gen(function*() { Effect.forever, Effect.forkIn(shardingScope) ) + + // Release every held shard through the per-shard path. Their entities are + // forced to stop, so the handoff does not wait out graceful termination. + // This is best effort: the wait is bounded, and if it times out, the final + // `releaseAll` attempts to release the remaining locks. + handoff = Effect.suspend(() => { + if (handoffLatch.openUnsafe()) activeShardsLatch.openUnsafe() + return forkInterruptShards([...acquiredShards, ...releasingShards], true) + }).pipe( + Effect.andThen(Effect.timeoutOption(handedOffLatch.await, shardLockInterval)), + Effect.asVoid + ) } // --- Storage inbox --- @@ -614,6 +692,12 @@ const make = Effect.gen(function*() { const storageReadLatch = yield* Latch.make(true) const openStorageReadLatch = constant(Effect.asVoid(storageReadLatch.open)) + type PendingNotification = { + resume: (_: Effect.Effect) => void + readonly message: Message.IncomingRequest + } + const pendingNotifications = new Map() + const storageReadLock = Semaphore.makeUnsafe(1) const withStorageReadLock = storageReadLock.withPermits(1) @@ -647,7 +731,7 @@ const make = Effect.gen(function*() { if (storageEnabled && initialRunnerAddress) { const selfAddress = initialRunnerAddress - yield* Effect.gen(function*() { + const storageReadLoop = Effect.gen(function*() { yield* Effect.logDebug("Starting") yield* Effect.addFinalizer(() => Effect.logDebug("Shutting down")) @@ -904,8 +988,7 @@ const make = Effect.gen(function*() { module: "effect/cluster/Sharding", fiber: "Storage read loop", runner: selfAddress - }), - Effect.forkIn(shardingScope) + }) ) // open the storage latch every poll interval @@ -1032,20 +1115,28 @@ const make = Effect.gen(function*() { ), Effect.forkIn(shardingScope) ) + + // fork after the state the loop reads has been initialized + yield* Effect.forkIn(storageReadLoop, shardingScope) } // --- Sending messages --- - const sendLocal = | Message.Incoming>(message: M) => - Effect.suspend(function loop(): Effect.Effect< + const sendLocal = | Message.Incoming>(message: M) => { + const address = message.envelope.address + const isRequest = message._tag === "IncomingRequest" || message._tag === "OutgoingRequest" + const send = Effect.suspend(function loop(): Effect.Effect< void, | EntityNotAssignedToRunner | MailboxFull | AlreadyProcessingMessage | (M extends Message.Incoming ? never : PersistenceError) > { - const address = message.envelope.address - if (!isEntityOnLocalShards(address)) { + if ( + !isEntityOnLocalShards(address) && + // Entities draining from a released shard still accept interrupts and acks + (isRequest || !MutableHashSet.has(releasingShards, address.shardId)) + ) { return Effect.fail(new EntityNotAssignedToRunner({ address })) } const state = entityManagers.get(address.entityType) @@ -1064,12 +1155,9 @@ const make = Effect.gen(function*() { simulateRemoteSerialization: config.simulateRemoteSerialization }) as any }) - - type PendingNotification = { - resume: (_: Effect.Effect) => void - readonly message: Message.IncomingRequest + return isRequest ? Effect.andThen(awaitShardLock(address.shardId), send) : send } - const pendingNotifications = new Map() + const notifyLocal = | Message.Incoming>( message: M, discard: boolean, @@ -1087,7 +1175,7 @@ const make = Effect.gen(function*() { const state = entityManagers.get(address.entityType) if (!state) { return Effect.flatMap(waitForEntityManager(address.entityType), loop) - } else if (state.status === "closed" || !isEntityOnLocalShards(address)) { + } else if (state.status === "closed") { return Effect.fail(new EntityNotAssignedToRunner({ address })) } @@ -1129,7 +1217,10 @@ const make = Effect.gen(function*() { function sendOutgoing( message: Message.Outgoing, discard: boolean, - retries?: number + retries?: number, + // The runner serving the request, for its interrupts and acks. Later + // attempts fall back to the current shard assignment. + runner?: RunnerAddress ): Effect.Effect< void, MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner @@ -1172,7 +1263,9 @@ const make = Effect.gen(function*() { if (shouldFail && MutableRef.get(isShutdown)) { return Effect.fail(new EntityNotAssignedToRunner({ address })) } - const maybeRunner = MutableHashMap.get(shardAssignments, address.shardId) + const maybeRunner = runner !== undefined + ? Option.some(runner) + : MutableHashMap.get(shardAssignments, address.shardId) const runnerIsLocal = Option.isSome(maybeRunner) && isLocalRunner(maybeRunner.value) if (isPersisted) { return runnerIsLocal @@ -1180,6 +1273,9 @@ const make = Effect.gen(function*() { : runnersService.notify({ address: maybeRunner, message, discard }) } else if (Option.isNone(maybeRunner)) { return Effect.fail(new EntityNotAssignedToRunner({ address })) + } else if (message._tag === "OutgoingRequest") { + const entry = clientRequests.get(message.envelope.requestId) + if (entry) entry.runner = maybeRunner.value } return runnerIsLocal ? sendLocal(message) @@ -1250,9 +1346,12 @@ const make = Effect.gen(function*() { while (true) { // Ensure the current runner is registered if (selfRunner && !isShutdown.current && !MutableHashMap.has(allRunners, selfRunner)) { - yield* Effect.logDebug("Registering runner", selfRunner) - const machineId = yield* withTimeout(runnerStorage.register(selfRunner, true)) - yield* snowflakeGen.setMachineId(machineId) + yield* withRegistrationLock(Effect.gen(function*() { + if (isShutdown.current) return + yield* Effect.logDebug("Registering runner", selfRunner) + const machineId = yield* withTimeout(runnerStorage.register(selfRunner, true)) + yield* snowflakeGen.setMachineId(machineId) + })) } const runners = yield* withTimeout(runnerStorage.getRunners) @@ -1324,6 +1423,7 @@ const make = Effect.gen(function*() { }) yield* Effect.logDebug("New shard assignments", selfShards) activeShardsLatch.openUnsafe() + yield* notifyShardLockWaiters // update metrics if (selfRunner) { @@ -1361,6 +1461,8 @@ const make = Effect.gen(function*() { readonly context: Context.Context readonly message: Message.OutgoingRequest lastChunkId?: Snowflake.Snowflake + // the runner a volatile request was last sent to + runner?: RunnerAddress } const clientRequests = new Map() @@ -1445,7 +1547,9 @@ const make = Effect.gen(function*() { }), rpc: entry.rpc }), - false + false, + undefined, + entry.runner ) } case "Interrupt": { @@ -1476,7 +1580,8 @@ const make = Effect.gen(function*() { rpc: entry.rpc }), false, - 3 + 3, + entry.runner )) } } @@ -1569,6 +1674,47 @@ const make = Effect.gen(function*() { const singletonFibers = yield* FiberMap.make() const withSingletonLock = Semaphore.makeUnsafe(1).withPermits(1) + // Singletons that are stopping. They count as running, so they are not + // restarted, and shard releases wait for them. Their teardown is awaited + // without the singleton lock, as it can register or stop other singletons. + const stoppingSingletons = MutableHashMap.empty>() + const isSingletonRegistered = (address: SingletonAddress) => { + const map = singletons.get(address.shardId) + return map !== undefined && MutableHashMap.has(map, address) + } + + // Called with the singleton lock held. Starts stopping the singleton and + // returns the wait for its teardown, to run without the lock. + const stopSingletonUnsafe = (address: SingletonAddress, activeTeardown: boolean): Effect.Effect => { + const stopping = MutableHashMap.get(stoppingSingletons, address) + if (Option.isSome(stopping)) return Fiber.join(stopping.value) + const fiber = FiberMap.getUnsafe(singletonFibers, address) + if (Option.isNone(fiber)) return Effect.void + const interrupt = Fiber.interrupt(fiber.value) + const stop = Effect.runForkWith(services)( + activeTeardown ? ActiveTeardown.aroundShard(address.shardId, interrupt) : interrupt + ) + MutableHashMap.set(stoppingSingletons, address, stop) + stop.addObserver(() => { + MutableHashMap.remove(stoppingSingletons, address) + // a registration made while the previous singleton was stopping + if (isSingletonRegistered(address)) runFork(syncSingletons) + }) + return Fiber.join(stop) + } + + // Waits for the singletons of the given shards that are still stopping + const awaitStoppingSingletons = (shardIds: ReadonlyArray) => + Effect.suspend(() => { + const stops = Arr.empty>() + MutableHashMap.forEach(stoppingSingletons, (stop, address) => { + if (shardIds.some((shardId) => Equal.equals(shardId, address.shardId))) { + stops.push(stop) + } + }) + return Fiber.joinAll(stops) + }) + const registerSingleton: Sharding["Service"]["registerSingleton"] = Effect.fnUntraced( function*(name, run, options) { const shardGroup = options?.shardGroup ?? "default" @@ -1599,32 +1745,38 @@ const make = Effect.gen(function*() { yield* PubSub.publish(events, SingletonRegistered({ address })) - // start if we are on the right shard - if (MutableHashSet.has(acquiredShards, address.shardId)) { + // start if we are on the right shard, unless a previous registration of + // this singleton is still stopping + if ( + MutableHashSet.has(acquiredShards, address.shardId) && + !MutableHashMap.has(stoppingSingletons, address) + ) { yield* Effect.logDebug("Starting singleton", address) yield* FiberMap.run(singletonFibers, address, wrappedRun) } - yield* Effect.addFinalizer(() => { - const map = singletons.get(address.shardId)! - MutableHashMap.remove(map, address) - return FiberMap.remove(singletonFibers, address) - }) + yield* Effect.addFinalizer(() => + withSingletonLock(Effect.sync(() => { + MutableHashMap.remove(singletons.get(address.shardId)!, address) + return stopSingletonUnsafe(address, false) + })).pipe(Effect.flatten) + ) }, withSingletonLock ) - const syncSingletons = withSingletonLock(Effect.gen(function*() { + // Starts and stops singletons to match the acquired shards, returning the + // waits for the stopped singletons + const startSyncSingletons = withSingletonLock(Effect.gen(function*() { + const stops = Arr.empty>() for (const [shardId, map] of singletons) { for (const [address, run] of map) { - const running = FiberMap.hasUnsafe(singletonFibers, address) + const stopping = MutableHashMap.has(stoppingSingletons, address) + const running = stopping || FiberMap.hasUnsafe(singletonFibers, address) const shouldBeRunning = MutableHashSet.has(acquiredShards, shardId) if (running && !shouldBeRunning) { - yield* Effect.logDebug("Stopping singleton", address) - yield* ActiveTeardown.aroundShard( - address.shardId, - FiberMap.remove(singletonFibers, address) - ) + if (!stopping) yield* Effect.logDebug("Stopping singleton", address) + stops.push(stopSingletonUnsafe(address, true)) } else if (!running && shouldBeRunning) { yield* Effect.logDebug("Starting singleton", address) yield* FiberMap.run(singletonFibers, address, run) @@ -1635,8 +1787,15 @@ const make = Effect.gen(function*() { BigInt(yield* FiberMap.size(singletonFibers)), Context.empty() ) + return stops })) + const syncSingletons: Effect.Effect = Effect.flatMap( + startSyncSingletons, + // wait for the stopped singletons without the lock + (stops) => Effect.all(stops, { discard: true }) + ) + // --- Entities --- const reaper = yield* EntityReaper @@ -1777,12 +1936,14 @@ const make = Effect.gen(function*() { if (isShutdown.current) return MutableRef.set(isShutdown, true) + yield* notifyShardLockWaiters if (selfRunner) { - yield* Effect.ignore(runnerStorage.unregister(selfRunner.address)) + yield* Effect.ignore(withRegistrationLock(runnerStorage.unregister(selfRunner.address))) } }) - yield* Scope.addFinalizerExit(shardingScope, shutdown) + // hand the shards off before the loops stop + yield* Scope.addFinalizerExit(shardingScope, (exit) => Effect.andThen(shutdown(exit), handoff)) const activeEntityCount = Effect.gen(function*() { let count = 0 diff --git a/repos/effect/packages/effect/src/cluster/ShardingConfig.ts b/packages/effect/src/cluster/ShardingConfig.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ShardingConfig.ts rename to packages/effect/src/cluster/ShardingConfig.ts diff --git a/repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts b/packages/effect/src/cluster/ShardingRegistrationEvent.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/ShardingRegistrationEvent.ts rename to packages/effect/src/cluster/ShardingRegistrationEvent.ts diff --git a/repos/effect/packages/effect/src/cluster/SingleRunner.ts b/packages/effect/src/cluster/SingleRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SingleRunner.ts rename to packages/effect/src/cluster/SingleRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/Singleton.ts b/packages/effect/src/cluster/Singleton.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Singleton.ts rename to packages/effect/src/cluster/Singleton.ts diff --git a/repos/effect/packages/effect/src/cluster/SingletonAddress.ts b/packages/effect/src/cluster/SingletonAddress.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SingletonAddress.ts rename to packages/effect/src/cluster/SingletonAddress.ts diff --git a/repos/effect/packages/effect/src/cluster/Snowflake.ts b/packages/effect/src/cluster/Snowflake.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/Snowflake.ts rename to packages/effect/src/cluster/Snowflake.ts diff --git a/repos/effect/packages/effect/src/cluster/SocketRunner.ts b/packages/effect/src/cluster/SocketRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/SocketRunner.ts rename to packages/effect/src/cluster/SocketRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/SqlMessageStorage.ts b/packages/effect/src/cluster/SqlMessageStorage.ts similarity index 92% rename from repos/effect/packages/effect/src/cluster/SqlMessageStorage.ts rename to packages/effect/src/cluster/SqlMessageStorage.ts index 8d7f9b218..74fc016a3 100644 --- a/repos/effect/packages/effect/src/cluster/SqlMessageStorage.ts +++ b/packages/effect/src/cluster/SqlMessageStorage.ts @@ -8,6 +8,11 @@ * storage constructor, layers, migrations, optional table prefixes, and the row * mapping needed by encoded message storage. * + * `layer`, `layerWith`, `make`, and `makeEncoded` run the migrations before + * building the storage. To run migrations with a different connection, such as + * a schema owner in a deploy step, use `layerMigrations` there and + * `layerStorage` in the runtime. + * * Request deduplication keys that exceed the 255-character `message_id` * column are hashed with the `Crypto` service before they are written, so * composed keys of any length are supported; shorter keys are stored as @@ -24,7 +29,6 @@ import * as Hex from "../encoding/Hex.ts" import * as Layer from "../Layer.ts" import * as Option from "../Option.ts" import type * as PlatformError from "../PlatformError.ts" -import * as Schedule from "../Schedule.ts" import * as Migrator from "../sql/Migrator.ts" import * as SqlClient from "../sql/SqlClient.ts" import type { Row } from "../sql/SqlConnection.ts" @@ -73,19 +77,18 @@ export const makeEncoded: (options?: { MessageStorage.Encoded, never, SqlClient.SqlClient | Crypto.Crypto -> = Effect.fnUntraced(function*(options) { +> = (options) => Effect.andThen(runMessageMigrations(options), makeEncodedStorage(options)) + +const makeEncodedStorage = Effect.fnUntraced(function*( + options: { + readonly prefix?: string | undefined + } | undefined +) { const sql = (yield* SqlClient.SqlClient).withoutTransforms() const crypto = yield* Crypto.Crypto const prefix = options?.prefix ?? "cluster" const table = (name: string) => `${prefix}_${name}` - yield* Effect.orDie( - Migrator.make({})({ - loader: migrations(options), - table: table("migrations") - }) - ) - const messageKindAckChunk = sql.literal(String(messageKind.AckChunk)) const messageKindInterrupt = sql.literal(String(messageKind.Interrupt)) const replyKindWithExit = sql.literal(String(replyKind.WithExit)) @@ -810,69 +813,27 @@ export const make: (options?: { > = (options) => Effect.flatMap(makeEncoded(options), MessageStorage.makeEncoded) /** - * Layer that provides SQL-backed `MessageStorage` using the default table prefix - * and the default snowflake generator. - * - * **When to use** - * - * Use when a cluster should persist mailbox messages and replies in SQL using - * the default `cluster` table prefix and the standard snowflake generator. + * Migration loader for the SQL message storage tables. * * **Details** * - * The layer runs the SQL migrations through `make`, provides `MessageStorage`, - * and supplies `Snowflake.layerGenerator` internally. Callers still provide - * `SqlClient`, `ShardingConfig`, and `Crypto.Crypto`, which is used to hash - * message deduplication keys that would overflow the fixed-width - * `message_id` column. - * - * **Gotchas** - * - * This layer always uses the `cluster` table prefix. Use `layerWith` before - * deployment if you need a different stable prefix, because changing prefixes - * later points the runtime at a different set of tables. - * - * @see {@link layerWith} for the same SQL storage layer with a custom table prefix - * @see {@link make} for the lower-level service constructor that uses an existing `Snowflake.Generator` + * History is recorded in `_migrations`; the default + * prefix is `cluster`. * * @stability unstable - * @category layers - * @since 4.0.0 - */ -export const layer: Layer.Layer< - MessageStorage.MessageStorage, - never, - SqlClient.SqlClient | ShardingConfig | Crypto.Crypto -> = Layer.effect(MessageStorage.MessageStorage, make()).pipe( - Layer.provide(Snowflake.layerGenerator) -) - -/** - * Layer that provides SQL-backed `MessageStorage` using a custom table prefix. - * - * @stability unstable - * @category layers - * @since 4.0.0 + * @category migrations + * @since 4.1.0 */ -export const layerWith = (options: { - readonly prefix?: string | undefined -}): Layer.Layer => - Layer.effect(MessageStorage.MessageStorage, make(options)).pipe( - Layer.provide(Snowflake.layerGenerator) - ) - -// ------------------------------------------------------------------------------------------------- -// internal -// ------------------------------------------------------------------------------------------------- - -const migrations = (options?: { +export const migrations = (options?: { readonly prefix?: string | undefined -}) => { +}): Migrator.Loader => { const prefix = options?.prefix ?? "cluster" const table = (name: string) => `${prefix}_${name}` const messagesTable = table("messages") const repliesTable = table("replies") + // The migrator serializes PostgreSQL DDL. Propagate errors: retrying in an + // aborted transaction cannot succeed. return Migrator.fromRecord({ "0001_create_tables": Effect.gen(function*() { const sql = (yield* SqlClient.SqlClient).withoutTransforms() @@ -956,7 +917,7 @@ const migrations = (options?: { deliver_at BIGINT, UNIQUE (message_id) ) - `.pipe(Effect.ignore), + `, orElse: () => // sqlite sql` @@ -1016,18 +977,7 @@ const migrations = (options?: { CREATE INDEX IF NOT EXISTS ${sql(requestIdLookupIndex)} ON ${messagesTableSql} (request_id) ` - }).pipe( - sql.withTransaction, - Effect.tapDefect((error) => - Effect.annotateLogs(Effect.logDebug("Failed to create indexes", error), { - package: "@effect/cluster", - module: "SqlMessageStorage" - }) - ), - Effect.retry({ - schedule: Schedule.spaced(1000) - }) - ), + }), orElse: () => // sqlite Effect.all([ @@ -1121,17 +1071,7 @@ const migrations = (options?: { sql` CREATE INDEX IF NOT EXISTS ${sql(replyLookupIndex)} ON ${repliesTableSql} (request_id, kind, acked); - `.pipe( - Effect.tapDefect((error) => - Effect.annotateLogs(Effect.logDebug("Failed to create indexes", error), { - package: "@effect/cluster", - module: "SqlMessageStorage" - }) - ), - Effect.retry({ - schedule: Schedule.spaced(1000) - }) - ), + `, orElse: () => // sqlite sql` @@ -1180,6 +1120,100 @@ const migrations = (options?: { }) } +const runMessageMigrations = (options?: { + readonly prefix?: string | undefined +}): Effect.Effect => + Migrator.make({})({ + loader: migrations(options), + table: `${options?.prefix ?? "cluster"}_migrations` + }).pipe(Effect.asVoid, Effect.orDie) + +/** + * Runs the SQL message storage migrations without providing storage. + * + * **Details** + * + * Use an owner connection in a deploy step, then use `layerStorage` with a + * DML-only runtime connection. History is recorded in `_migrations`. + * Migration errors become defects. + * + * On PostgreSQL, use the role's `search_path` to select a schema. A + * schema-qualified prefix such as `app.cluster` produces an invalid index name. + * + * @stability unstable + * @category layers + * @since 4.1.0 + */ +export const layerMigrations = (options: { + readonly prefix?: string | undefined +}): Layer.Layer => Layer.effectDiscard(runMessageMigrations(options)) + +/** + * Provides SQL-backed `MessageStorage` without DDL or startup schema checks. + * + * **Details** + * + * Run `layerMigrations` separately before using this layer. + * This layer supplies `Snowflake.layerGenerator` internally. + * + * @stability unstable + * @category layers + * @since 4.1.0 + */ +export const layerStorage = (options: { + readonly prefix?: string | undefined +}): Layer.Layer => + Layer.effect( + MessageStorage.MessageStorage, + Effect.flatMap(makeEncodedStorage(options), MessageStorage.makeEncoded) + ).pipe( + Layer.provide(Snowflake.layerGenerator) + ) + +/** + * Provides SQL-backed `MessageStorage` with a custom table prefix, + * running migrations first. + * + * **Details** + * + * Migration errors become defects. Use `layerMigrations` and `layerStorage` + * to migrate separately. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layerWith = (options: { + readonly prefix?: string | undefined +}): Layer.Layer => + layerStorage(options).pipe( + Layer.provide(layerMigrations(options)) + ) + +/** + * Provides SQL-backed `MessageStorage` with the `cluster` table prefix, + * running migrations first and supplying `Snowflake.layerGenerator`. + * + * **Details** + * + * The connection needs DDL permissions. PostgreSQL takes an exclusive lock + * on the migration history table. Use `layerWith` for a custom prefix or + * `layerMigrations` and `layerStorage` to migrate separately. + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer: Layer.Layer< + MessageStorage.MessageStorage, + never, + SqlClient.SqlClient | ShardingConfig | Crypto.Crypto +> = layerWith({}) + +// ------------------------------------------------------------------------------------------------- +// internal +// ------------------------------------------------------------------------------------------------- + const messageKind = { "Request": 0, "AckChunk": 1, diff --git a/repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts b/packages/effect/src/cluster/SqlRunnerStorage.ts similarity index 80% rename from repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts rename to packages/effect/src/cluster/SqlRunnerStorage.ts index d0c278e3f..f5a807823 100644 --- a/repos/effect/packages/effect/src/cluster/SqlRunnerStorage.ts +++ b/packages/effect/src/cluster/SqlRunnerStorage.ts @@ -3,10 +3,15 @@ * * The SQL-backed `RunnerStorage` records runners, health flags, machine ids, * and shard locks so multiple processes can coordinate which runner owns each - * shard. This module creates the required runner and lock tables, supports an + * shard. This module migrates the required runner and lock tables, supports an * optional table prefix, uses advisory locks for PostgreSQL and MySQL when * enabled, and provides constructors and layers for the storage service. * + * `layer`, `layerWith`, and `make` run the migrations before building the + * storage. To run migrations with a different connection, such as a schema + * owner in a deploy step, use `layerMigrations` there and `layerStorage` in the + * runtime. + * * @stability unstable * @since 4.0.0 */ @@ -17,6 +22,7 @@ import * as Effect from "../Effect.ts" import * as Fiber from "../Fiber.ts" import * as Layer from "../Layer.ts" import * as Scope from "../Scope.ts" +import * as Migrator from "../sql/Migrator.ts" import * as SqlClient from "../sql/SqlClient.ts" import type { Connection } from "../sql/SqlConnection.ts" import type { SqlError } from "../sql/SqlError.ts" @@ -41,36 +47,7 @@ const postgresLockNamespace = (prefix: string): number => { return hash | 0 } -/** - * Creates a SQL-backed `RunnerStorage` implementation for registered runners and - * shard locks, using the configured table prefix and advisory locks where - * supported and enabled. - * - * **When to use** - * - * Use to create a SQL-backed `RunnerStorage` value directly when building - * custom service or layer composition around the storage implementation. - * - * **Details** - * - * When `prefix` is omitted, `make` uses the `cluster` prefix, creating - * `cluster_runners` and `cluster_locks`. PostgreSQL and MySQL use advisory - * locks unless `ShardingConfig.shardLockDisableAdvisory` is enabled; other - * dialects use rows in the locks table. - * - * **Gotchas** - * - * Changing `prefix` changes both generated table names, so runners using - * different prefixes do not share registrations or shard locks. - * - * @see {@link layer} for the default SQL-backed storage layer - * @see {@link layerWith} for a SQL-backed storage layer with a custom table prefix - * - * @stability unstable - * @category constructors - * @since 4.0.0 - */ -export const make = Effect.fnUntraced(function*(options: { +const makeStorage = Effect.fnUntraced(function*(options: { readonly prefix?: string | undefined }) { const config = yield* ShardingConfig.ShardingConfig @@ -183,99 +160,9 @@ export const make = Effect.fnUntraced(function*(options: { const runnersTable = table("runners") const runnersTableSql = sql(runnersTable) - yield* sql.onDialectOrElse({ - mssql: () => - sql` - IF OBJECT_ID(N'${runnersTableSql}', N'U') IS NULL - CREATE TABLE ${runnersTableSql} ( - machine_id INT IDENTITY PRIMARY KEY, - address VARCHAR(255) NOT NULL, - runner TEXT NOT NULL, - healthy BIT NOT NULL DEFAULT 1, - last_heartbeat DATETIME NOT NULL DEFAULT GETDATE(), - UNIQUE(address) - ) - `, - mysql: () => - sql` - CREATE TABLE IF NOT EXISTS ${runnersTableSql} ( - machine_id INT AUTO_INCREMENT PRIMARY KEY, - address VARCHAR(255) NOT NULL, - runner TEXT NOT NULL, - healthy BOOLEAN NOT NULL DEFAULT TRUE, - last_heartbeat DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE(address) - ) - `, - pg: () => - sql` - CREATE TABLE IF NOT EXISTS ${runnersTableSql} ( - machine_id SERIAL PRIMARY KEY, - address VARCHAR(255) NOT NULL, - runner TEXT NOT NULL, - healthy BOOLEAN NOT NULL DEFAULT TRUE, - last_heartbeat TIMESTAMP NOT NULL DEFAULT NOW(), - UNIQUE(address) - ) - `, - orElse: () => - // sqlite - sql` - CREATE TABLE IF NOT EXISTS ${runnersTableSql} ( - machine_id INTEGER PRIMARY KEY AUTOINCREMENT, - address TEXT NOT NULL, - runner TEXT NOT NULL, - healthy INTEGER NOT NULL DEFAULT 1, - last_heartbeat DATETIME NOT NULL DEFAULT (CURRENT_TIMESTAMP), - UNIQUE(address) - ) - ` - }) - const locksTable = table("locks") const locksTableSql = sql(locksTable) - yield* sql.onDialectOrElse({ - mssql: () => - sql` - IF OBJECT_ID(N'${locksTableSql}', N'U') IS NULL - CREATE TABLE ${locksTableSql} ( - shard_id VARCHAR(50) PRIMARY KEY, - address VARCHAR(255) NOT NULL, - acquired_at DATETIME NOT NULL - ) - `, - mysql: () => - disableAdvisoryLocks ? - sql` - CREATE TABLE IF NOT EXISTS ${locksTableSql} ( - shard_id VARCHAR(50) PRIMARY KEY, - address VARCHAR(255) NOT NULL, - acquired_at DATETIME NOT NULL - ) - ` : - Effect.void, - pg: () => - disableAdvisoryLocks ? - sql` - CREATE TABLE IF NOT EXISTS ${locksTableSql} ( - shard_id VARCHAR(50) PRIMARY KEY, - address VARCHAR(255) NOT NULL, - acquired_at TIMESTAMP NOT NULL - ) - ` : - Effect.void, - orElse: () => - // sqlite - sql` - CREATE TABLE IF NOT EXISTS ${locksTableSql} ( - shard_id TEXT PRIMARY KEY, - address TEXT NOT NULL, - acquired_at DATETIME NOT NULL - ) - ` - }) - const sqlNowString = sql.onDialectOrElse({ pg: () => "NOW()", mysql: () => "NOW()", @@ -765,20 +652,213 @@ export const make = Effect.fnUntraced(function*(options: { }, withTracerDisabled) /** - * Layer that provides SQL-backed `RunnerStorage` using the default table prefix. + * Creates a SQL-backed `RunnerStorage` implementation for registered runners and + * shard locks, using the configured table prefix and advisory locks where + * supported and enabled. + * + * **When to use** + * + * Use to create a SQL-backed `RunnerStorage` value directly when building + * custom service or layer composition around the storage implementation. + * + * **Details** + * + * `make` first runs the runner storage migrations, so the connection needs + * permission to create tables. When `prefix` is omitted, `make` uses the + * `cluster` prefix, creating `cluster_runners`, `cluster_locks`, and the + * `cluster_runner_migrations` history table. PostgreSQL and MySQL use advisory + * locks unless `ShardingConfig.shardLockDisableAdvisory` is enabled; other + * dialects use rows in the locks table. + * + * **Gotchas** + * + * Changing `prefix` changes all generated table names, so runners using + * different prefixes do not share registrations or shard locks. + * + * @see {@link layer} for the default SQL-backed storage layer + * @see {@link layerWith} for a SQL-backed storage layer with a custom table prefix + * @see {@link layerStorage} for a storage layer that does not run migrations * * @stability unstable - * @category layers + * @category constructors * @since 4.0.0 */ -export const layer: Layer.Layer< - RunnerStorage.RunnerStorage, +export const make = (options: { + readonly prefix?: string | undefined +}): Effect.Effect< + RunnerStorage.RunnerStorage["Service"], SqlError, - SqlClient.SqlClient | ShardingConfig.ShardingConfig -> = Layer.effect(RunnerStorage.RunnerStorage)(make({})) + SqlClient.SqlClient | ShardingConfig.ShardingConfig | Scope.Scope +> => Effect.andThen(runRunnerMigrations(options), makeStorage(options)) /** - * Layer that provides SQL-backed `RunnerStorage` using a custom table prefix. + * Migration loader for the SQL runner storage tables. + * + * **Details** + * + * History is recorded in `_runner_migrations`; the default + * prefix is `cluster`. The locks table is created regardless of advisory lock settings. + * + * @stability unstable + * @category migrations + * @since 4.1.0 + */ +export const migrations = (options: { + readonly prefix?: string | undefined +}): Migrator.Loader => { + const prefix = options.prefix ?? "cluster" + const table = (name: string) => `${prefix}_${name}` + const runnersTable = table("runners") + const locksTable = table("locks") + + return Migrator.fromRecord({ + // Existing deployments may have these tables but no migration history. + "0001_create_tables": Effect.gen(function*() { + const sql = (yield* SqlClient.SqlClient).withoutTransforms() + const runnersTableSql = sql(runnersTable) + const locksTableSql = sql(locksTable) + + yield* sql.onDialectOrElse({ + mssql: () => + sql` + IF OBJECT_ID(N'${runnersTableSql}', N'U') IS NULL + CREATE TABLE ${runnersTableSql} ( + machine_id INT IDENTITY PRIMARY KEY, + address VARCHAR(255) NOT NULL, + runner TEXT NOT NULL, + healthy BIT NOT NULL DEFAULT 1, + last_heartbeat DATETIME NOT NULL DEFAULT GETDATE(), + UNIQUE(address) + ) + `, + mysql: () => + sql` + CREATE TABLE IF NOT EXISTS ${runnersTableSql} ( + machine_id INT AUTO_INCREMENT PRIMARY KEY, + address VARCHAR(255) NOT NULL, + runner TEXT NOT NULL, + healthy BOOLEAN NOT NULL DEFAULT TRUE, + last_heartbeat DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + UNIQUE(address) + ) + `, + pg: () => + sql` + CREATE TABLE IF NOT EXISTS ${runnersTableSql} ( + machine_id SERIAL PRIMARY KEY, + address VARCHAR(255) NOT NULL, + runner TEXT NOT NULL, + healthy BOOLEAN NOT NULL DEFAULT TRUE, + last_heartbeat TIMESTAMP NOT NULL DEFAULT NOW(), + UNIQUE(address) + ) + `, + orElse: () => + // sqlite + sql` + CREATE TABLE IF NOT EXISTS ${runnersTableSql} ( + machine_id INTEGER PRIMARY KEY AUTOINCREMENT, + address TEXT NOT NULL, + runner TEXT NOT NULL, + healthy INTEGER NOT NULL DEFAULT 1, + last_heartbeat DATETIME NOT NULL DEFAULT (CURRENT_TIMESTAMP), + UNIQUE(address) + ) + ` + }) + + yield* sql.onDialectOrElse({ + mssql: () => + sql` + IF OBJECT_ID(N'${locksTableSql}', N'U') IS NULL + CREATE TABLE ${locksTableSql} ( + shard_id VARCHAR(50) PRIMARY KEY, + address VARCHAR(255) NOT NULL, + acquired_at DATETIME NOT NULL + ) + `, + mysql: () => + sql` + CREATE TABLE IF NOT EXISTS ${locksTableSql} ( + shard_id VARCHAR(50) PRIMARY KEY, + address VARCHAR(255) NOT NULL, + acquired_at DATETIME NOT NULL + ) + `, + pg: () => + sql` + CREATE TABLE IF NOT EXISTS ${locksTableSql} ( + shard_id VARCHAR(50) PRIMARY KEY, + address VARCHAR(255) NOT NULL, + acquired_at TIMESTAMP NOT NULL + ) + `, + orElse: () => + // sqlite + sql` + CREATE TABLE IF NOT EXISTS ${locksTableSql} ( + shard_id TEXT PRIMARY KEY, + address TEXT NOT NULL, + acquired_at DATETIME NOT NULL + ) + ` + }) + }) + }) +} + +const runRunnerMigrations = (options: { + readonly prefix?: string | undefined +}): Effect.Effect => + Migrator.make({})({ + loader: migrations(options), + // Message and runner migration ids overlap, so keep separate histories. + table: `${options.prefix ?? "cluster"}_runner_migrations` + }).pipe(Effect.asVoid, Effect.orDie) + +/** + * Runs the SQL runner storage migrations without providing storage. + * + * **Details** + * + * Use an owner connection in a deploy step, then use `layerStorage` with a + * DML-only runtime connection. History is recorded in `_runner_migrations`. + * Migration errors become defects. + * This layer does not require `ShardingConfig`. + * + * @stability unstable + * @category layers + * @since 4.1.0 + */ +export const layerMigrations = (options: { + readonly prefix?: string | undefined +}): Layer.Layer => Layer.effectDiscard(runRunnerMigrations(options)) + +/** + * Provides SQL-backed `RunnerStorage` without DDL or startup schema checks. + * + * **Details** + * + * Run `layerMigrations` separately before using this layer. + * + * @stability unstable + * @category layers + * @since 4.1.0 + */ +export const layerStorage = (options: { + readonly prefix?: string | undefined +}): Layer.Layer => + Layer.effect(RunnerStorage.RunnerStorage)(makeStorage(options)) + +/** + * Provides SQL-backed `RunnerStorage` with a custom table prefix, + * running migrations first. + * + * **Details** + * + * The connection needs DDL permissions. PostgreSQL takes an exclusive lock + * on the migration history table. Use `layerMigrations` and `layerStorage` + * to migrate separately. * * @stability unstable * @category layers @@ -788,3 +868,19 @@ export const layerWith = (options: { readonly prefix?: string | undefined }): Layer.Layer => Layer.effect(RunnerStorage.RunnerStorage)(make(options)) + +/** + * Layer that provides SQL-backed `RunnerStorage` using the default table prefix, + * running the runner storage migrations first. + * + * @see {@link layerWith} for the same layer with a custom table prefix + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer: Layer.Layer< + RunnerStorage.RunnerStorage, + SqlError, + SqlClient.SqlClient | ShardingConfig.ShardingConfig +> = layerWith({}) diff --git a/repos/effect/packages/effect/src/cluster/TestRunner.ts b/packages/effect/src/cluster/TestRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/TestRunner.ts rename to packages/effect/src/cluster/TestRunner.ts diff --git a/repos/effect/packages/effect/src/cluster/index.ts b/packages/effect/src/cluster/index.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/index.ts rename to packages/effect/src/cluster/index.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts b/packages/effect/src/cluster/internal/clusterAbandon.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/clusterAbandon.ts rename to packages/effect/src/cluster/internal/clusterAbandon.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/entityActivation.ts b/packages/effect/src/cluster/internal/entityActivation.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/entityActivation.ts rename to packages/effect/src/cluster/internal/entityActivation.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/entityManager.ts b/packages/effect/src/cluster/internal/entityManager.ts similarity index 79% rename from repos/effect/packages/effect/src/cluster/internal/entityManager.ts rename to packages/effect/src/cluster/internal/entityManager.ts index 2fb7c18b1..f36da9ae5 100644 --- a/repos/effect/packages/effect/src/cluster/internal/entityManager.ts +++ b/packages/effect/src/cluster/internal/entityManager.ts @@ -86,6 +86,10 @@ export interface Residency { readonly releaseUnsafe: () => void } +interface RequestTransaction { + settle?: ((outcome: Exit.Exit) => Effect.Effect) | undefined +} + // Represents the entities managed by this entity manager /** * @internal @@ -100,10 +104,13 @@ export type EntityState = { /** Excludes requests awaiting their first dispatch from replay. */ delivered: boolean sentExit: boolean + /** Treat early termination interrupts like shutdown interrupts. */ + terminating?: boolean | undefined lastSentChunk: Option.Option> sequence: number /** Set when the request should not outlive its caller. */ callerScope?: Scope.Scope | undefined + transaction?: RequestTransaction | undefined }> lastActiveCheck: number write: RpcServer.RpcServer["write"] @@ -152,9 +159,13 @@ export const make = Effect.fnUntraced(function*< entityRpcs.set(KeepAliveRpc._tag, KeepAliveRpc as any) const activeServers = new Map() + // Entities finishing their in-flight requests after removal, which still + // accept interrupts and acks + const drainingServers = new Map() const serverCloseLatches = new Map() const processedRequestIds = new Set() @@ -181,7 +192,8 @@ export const make = Effect.fnUntraced(function*< const keepAliveLatch = Latch.makeUnsafe() const closeLatches = { closed: Latch.makeUnsafe(), - force: Latch.makeUnsafe() + force: Latch.makeUnsafe(), + closing: false } yield* Scope.addFinalizer( @@ -238,7 +250,7 @@ export const make = Effect.fnUntraced(function*< ) as Effect.Effect>>) const server = yield* RpcServer.makeNoSerialization(entity.protocol, { - spanPrefix: `${entity.type}(${address.entityId})`, + spanPrefix: entity.type, spanAttributes: { ...options.spanAttributes, "entity.type": entity.type, @@ -264,22 +276,23 @@ export const make = Effect.fnUntraced(function*< // They will be retried when the entity is restarted. // Also, if the request is uninterruptible, we ignore the // interrupt. + const persisted = storageEnabled && Context.get(request.message.annotations, Persisted) if ( - storageEnabled && - Context.get(request.message.annotations, Persisted) && + persisted && Exit.hasInterrupts(response.exit) && - (isShuttingDown || isUninterruptibleForServer(request.message.annotations)) + (isShuttingDown || request.terminating || isUninterruptibleForServer(request.message.annotations)) ) { - if (!isShuttingDown) { + if (!isShuttingDown && !request.terminating) { request.sentExit = false return server.write(0, requestEnvelope(request), requestWriteOptions(request)).pipe( + Effect.setContext(handlerContext), Effect.forkIn(handlerScope) ) } activeRequests.delete(Snowflake.Snowflake(response.requestId)) return options.storage.unregisterReplyHandler(request.message.envelope.requestId) } - return retryRespond( + const save = retryRespond( 4, Effect.suspend(() => request.message.respond( @@ -290,22 +303,53 @@ export const make = Effect.fnUntraced(function*< }) ) ) - ).pipe( - Effect.flatMap(() => { - if (storageEnabled) { - processedRequestIds.add(request.message.envelope.requestId) - } - activeRequests.delete(Snowflake.Snowflake(response.requestId)) + ) + const complete = Effect.sync(() => { + if (storageEnabled) { + processedRequestIds.add(request.message.envelope.requestId) + } + activeRequests.delete(Snowflake.Snowflake(response.requestId)) - // Start the idle timer when the last request completes. - if (activeRequests.size === 0) { - state.lastActiveCheck = clock.currentTimeMillisUnsafe() - } + // Start the idle timer when the last request completes. + if (activeRequests.size === 0) { + state.lastActiveCheck = clock.currentTimeMillisUnsafe() + } + }) + const respond = Effect.orDie(Effect.andThen(save, complete)) + const transaction = request.transaction + if (!transaction) return respond + const exit = response.exit + + if (!persisted) { + if (Exit.isSuccess(exit)) return respond + transaction.settle = () => respond + return Effect.void + } - return Effect.void - }), - Effect.orDie - ) + if (Exit.isSuccess(exit)) { + transaction.settle = (outcome) => + Exit.isSuccess(outcome) ? complete : Effect.flatMap( + hasStoredExit(request.message.envelope.requestId), + (committed) => { + if (!committed) return restartFrom(outcome.cause) + // Commit succeeded despite the error; let callers read the saved reply. + return Effect.andThen( + complete, + options.storage.unregisterReplyHandler(request.message.envelope.requestId) + ) + } + ) + return Effect.orDie(save) + } + transaction.settle = (outcome) => + // Transaction wrappers can add defects, but not typed errors; interrupts may be repeated. + Exit.isSuccess(outcome) || !outcome.cause.reasons.some((reason) => + Cause.isDieReason(reason) && + !exit.cause.reasons.some((r) => Cause.isDieReason(r) && r.defect === reason.defect) + ) + ? respond + : restartFrom(outcome.cause) + return Effect.void } case "Chunk": { const request = activeRequests.get(Snowflake.Snowflake(response.requestId)) @@ -330,10 +374,7 @@ export const make = Effect.fnUntraced(function*< )) } case "Defect": { - if (!isActive()) return endLatch.open - const rebuild = writeRef.rebuildUnsafe({ from: server.write, prepare: replay }) - if (!rebuild) return Effect.void - return Effect.forkIn(restart(Cause.die(response.defect), rebuild), managerScope) + return restartFrom(Cause.die(response.defect)) } case "ClientEnd": { return endLatch.open @@ -345,6 +386,21 @@ export const make = Effect.fnUntraced(function*< Effect.setContext(Context.merge(handlerContext, handlers)) ) + // Replay if the stored reply cannot be read. + const hasStoredExit = (requestId: Snowflake.Snowflake): Effect.Effect => + options.storage.repliesForUnfiltered([requestId]).pipe( + Effect.map((replies) => replies.some((reply) => reply._tag === "WithExit")), + Effect.catchCause(() => Effect.succeed(false)) + ) + + // Do not inherit the failed handler's transaction context. + const restartFrom = (cause: Cause.Cause): Effect.Effect => { + if (!isActive()) return endLatch.open + const rebuild = writeRef.rebuildUnsafe({ from: server.write, prepare: replay }) + if (!rebuild) return Effect.void + return Effect.forkIn(Effect.setContext(restart(cause, rebuild), handlerContext), managerScope) + } + yield* Scope.addFinalizer( handlerScope, Effect.sync(() => { @@ -415,15 +471,32 @@ export const make = Effect.fnUntraced(function*< Effect.suspend(() => { activeServers.delete(address.entityId) retired.openUnsafe() + closeLatches.closing = true acquireEntity(address) + drainingServers.set(address.entityId, state) return Effect.raceFirst( - state.write(0, { _tag: "Eof" }).pipe( + Effect.gen(function*() { + for (const [requestId, request] of activeRequests) { + if ( + !request.delivered || request.sentExit || + !Context.get(request.message.annotations, ClusterSchema.InterruptOnTermination) || + isUninterruptibleForServer(request.message.annotations) + ) continue + request.terminating = true + yield* state.write(0, { _tag: "Interrupt", requestId: requestId as any, interruptors: [] }) + } + yield* state.write(0, { _tag: "Eof" }) + }).pipe( Effect.andThen(endLatch.await), Effect.timeoutOption(config.entityTerminationTimeout), Effect.interruptible ), Effect.interruptible(closeLatches.force.await) - ) + ).pipe(Effect.ensuring(Effect.sync(() => { + if (drainingServers.get(address.entityId) === state) { + drainingServers.delete(address.entityId) + } + }))) }) ) if (!options.sharding.hasShardId(address.shardId)) { @@ -458,9 +531,38 @@ export const make = Effect.fnUntraced(function*< Effect.forkIn(managerScope) ) + function sendEnvelope(server: EntityState, message: Message.IncomingEnvelope): Effect.Effect { + const entry = server.activeRequests.get(message.envelope.requestId) + if (!entry) { + return Effect.void + } else if ( + message.envelope._tag === "AckChunk" && + Option.isSome(entry.lastSentChunk) && + message.envelope.replyId !== entry.lastSentChunk.value.id + ) { + return Effect.void + } + return server.write( + 0, + message.envelope._tag === "AckChunk" + ? { _tag: "Ack", requestId: message.envelope.requestId as any } + : { + _tag: "Interrupt", + requestId: message.envelope.requestId as any, + interruptors: [] + } + ) + } + function sendLocal( message: Message.IncomingLocal ): Effect.Effect { + if (message._tag === "IncomingEnvelope") { + const draining = drainingServers.get(message.envelope.address.entityId) + if (draining?.activeRequests.has(message.envelope.requestId)) { + return sendEnvelope(draining, message) + } + } return Effect.provideService( Effect.flatMap( entities.get(message.envelope.address), @@ -557,26 +659,7 @@ export const make = Effect.fnUntraced(function*< return server.write(0, requestEnvelope(entry), requestWriteOptions(entry)) } case "IncomingEnvelope": { - const entry = server.activeRequests.get(message.envelope.requestId) - if (!entry) { - return Effect.void - } else if ( - message.envelope._tag === "AckChunk" && - Option.isSome(entry.lastSentChunk) && - message.envelope.replyId !== entry.lastSentChunk.value.id - ) { - return Effect.void - } - return server.write( - 0, - message.envelope._tag === "AckChunk" - ? { _tag: "Ack", requestId: message.envelope.requestId as any } - : { - _tag: "Interrupt", - requestId: message.envelope.requestId as any, - interruptors: [] - } - ) + return sendEnvelope(server, message) } } } @@ -596,11 +679,24 @@ export const make = Effect.fnUntraced(function*< }) const requestWriteOptions = ( - entry: { readonly message: Message.IncomingRequestLocal; readonly callerScope?: Scope.Scope | undefined } + entry: { + readonly message: Message.IncomingRequestLocal + readonly callerScope?: Scope.Scope | undefined + transaction?: RequestTransaction | undefined + } ): Parameters[2] => { - const onTransaction = Context.get(entry.message.annotations, WithTransaction) - ? options.storage.withTransaction - : undefined + const onTransaction = !Context.get(entry.message.annotations, WithTransaction) + ? undefined + : (effect: Effect.Effect) => + // Interrupts during commit must not hide the outcome from settle. + Effect.uninterruptibleMask((restore) => { + const transaction: RequestTransaction = {} + entry.transaction = transaction + return Effect.onExit(options.storage.withTransaction(restore(effect)), (outcome) => { + if (entry.transaction === transaction) entry.transaction = undefined + return transaction.settle ? transaction.settle(outcome) : Effect.void + }) + }) const onCaller = entry.callerScope && bindToCaller(entry.callerScope) if (!onCaller) return onTransaction && { onRequest: onTransaction } return { onRequest: onTransaction ? (effect) => onCaller(onTransaction(effect)) : onCaller } @@ -621,13 +717,13 @@ export const make = Effect.fnUntraced(function*< } }) } - activeServers.forEach((state) => { - if (shardId[Equal.symbol](state.address.shardId)) { - fibers.push(runFork(entities.removeIgnore(state.address))) - } - }) + // Look entities up by address: `activeServers` is keyed by entity id, + // so an entity with the same id on another shard can hide this one. serverCloseLatches.forEach((latches, address) => { if (shardId[Equal.symbol](address.shardId)) { + if (!latches.closing) { + fibers.push(runFork(entities.removeIgnore(address))) + } fibers.push(runFork(latches.closed.await)) } }) diff --git a/repos/effect/packages/effect/src/cluster/internal/entityReaper.ts b/packages/effect/src/cluster/internal/entityReaper.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/entityReaper.ts rename to packages/effect/src/cluster/internal/entityReaper.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/hash.ts b/packages/effect/src/cluster/internal/hash.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/hash.ts rename to packages/effect/src/cluster/internal/hash.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/interruptors.ts b/packages/effect/src/cluster/internal/interruptors.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/interruptors.ts rename to packages/effect/src/cluster/internal/interruptors.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/resourceMap.ts b/packages/effect/src/cluster/internal/resourceMap.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/resourceMap.ts rename to packages/effect/src/cluster/internal/resourceMap.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/resourceRef.ts b/packages/effect/src/cluster/internal/resourceRef.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/resourceRef.ts rename to packages/effect/src/cluster/internal/resourceRef.ts diff --git a/repos/effect/packages/effect/src/cluster/internal/shardLock.ts b/packages/effect/src/cluster/internal/shardLock.ts similarity index 100% rename from repos/effect/packages/effect/src/cluster/internal/shardLock.ts rename to packages/effect/src/cluster/internal/shardLock.ts diff --git a/repos/effect/packages/effect/src/devtools/DevTools.ts b/packages/effect/src/devtools/DevTools.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevTools.ts rename to packages/effect/src/devtools/DevTools.ts diff --git a/repos/effect/packages/effect/src/devtools/DevToolsClient.ts b/packages/effect/src/devtools/DevToolsClient.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevToolsClient.ts rename to packages/effect/src/devtools/DevToolsClient.ts diff --git a/repos/effect/packages/effect/src/devtools/DevToolsSchema.ts b/packages/effect/src/devtools/DevToolsSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevToolsSchema.ts rename to packages/effect/src/devtools/DevToolsSchema.ts diff --git a/repos/effect/packages/effect/src/devtools/DevToolsServer.ts b/packages/effect/src/devtools/DevToolsServer.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/DevToolsServer.ts rename to packages/effect/src/devtools/DevToolsServer.ts diff --git a/repos/effect/packages/effect/src/devtools/index.ts b/packages/effect/src/devtools/index.ts similarity index 100% rename from repos/effect/packages/effect/src/devtools/index.ts rename to packages/effect/src/devtools/index.ts diff --git a/repos/effect/packages/effect/src/encoding/Base64.ts b/packages/effect/src/encoding/Base64.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Base64.ts rename to packages/effect/src/encoding/Base64.ts diff --git a/repos/effect/packages/effect/src/encoding/Base64Url.ts b/packages/effect/src/encoding/Base64Url.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Base64Url.ts rename to packages/effect/src/encoding/Base64Url.ts diff --git a/repos/effect/packages/effect/src/encoding/EncodingError.ts b/packages/effect/src/encoding/EncodingError.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/EncodingError.ts rename to packages/effect/src/encoding/EncodingError.ts diff --git a/repos/effect/packages/effect/src/encoding/Hex.ts b/packages/effect/src/encoding/Hex.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Hex.ts rename to packages/effect/src/encoding/Hex.ts diff --git a/repos/effect/packages/effect/src/encoding/Ini.ts b/packages/effect/src/encoding/Ini.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Ini.ts rename to packages/effect/src/encoding/Ini.ts diff --git a/repos/effect/packages/effect/src/encoding/Ndjson.ts b/packages/effect/src/encoding/Ndjson.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Ndjson.ts rename to packages/effect/src/encoding/Ndjson.ts diff --git a/repos/effect/packages/effect/src/encoding/SchemaBinary.ts b/packages/effect/src/encoding/SchemaBinary.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/SchemaBinary.ts rename to packages/effect/src/encoding/SchemaBinary.ts diff --git a/repos/effect/packages/effect/src/encoding/Sse.ts b/packages/effect/src/encoding/Sse.ts similarity index 84% rename from repos/effect/packages/effect/src/encoding/Sse.ts rename to packages/effect/src/encoding/Sse.ts index be2cf02f4..b1720f36a 100644 --- a/repos/effect/packages/effect/src/encoding/Sse.ts +++ b/packages/effect/src/encoding/Sse.ts @@ -92,6 +92,12 @@ export interface DecodeOptions { * Maximum number of string code units retained for a pending event. The default is 10 MiB. */ readonly maxEventSize?: number | undefined + /** + * Also dispatch an event that sets an `event:` name but has no `data:` + * line, with empty `data`. The WHATWG algorithm drops such events, but some + * protocols use them, such as graphql-sse's `complete`. Defaults to `false`. + */ + readonly dispatchEmptyNamedEvents?: boolean | undefined } const defaultMaxEventSize = 10 * 1024 * 1024 @@ -270,13 +276,16 @@ export const decodeDataSchema = ( */ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeOptions): Parser { const maxEventSize = options?.maxEventSize ?? defaultMaxEventSize + const dispatchEmptyNamedEvents = options?.dispatchEmptyNamedEvents === true // Processing state let isFirstChunk: boolean - let buffer: string - let startingPosition: number - let startingFieldLength: number + // Pieces of the unterminated line, joined once when the line ends + let pending: Array + let pendingLength: number let discardTrailingNewline: boolean + // Incremented by `reset`, so `feed` can stop if `onParse` resets the parser + let generation = 0 // Event state let lastEventId: string | undefined @@ -287,10 +296,10 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO return { feed, reset } function reset(): void { + generation++ isFirstChunk = true - buffer = "" - startingPosition = 0 - startingFieldLength = -1 + pending = [] + pendingLength = 0 discardTrailingNewline = false lastEventId = undefined @@ -299,120 +308,98 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO } function feed(chunk: string): SseError | undefined { - buffer = buffer ? buffer + chunk : chunk - // Strip any UTF-8 byte order mark (BOM) at the start of the stream. // Note that we do not strip any non - UTF8 BOM, as eventsource streams are // always decoded as UTF8 as per the specification. - if (isFirstChunk && buffer.startsWith(BOM)) { - buffer = buffer.slice(BOM.length) + if (isFirstChunk && chunk.startsWith(BOM)) { + chunk = chunk.slice(BOM.length) } isFirstChunk = false - // Set up chunk-specific processing state - const length = buffer.length + const length = chunk.length + const currentGeneration = generation let position = 0 - // Read the current buffer byte by byte while (position < length) { - // EventSource allows for carriage return + line feed, which means we - // need to ignore a linefeed character if the previous character was a - // carriage return - // @todo refactor to reduce nesting, consider checking previous byte? - // @todo but consider multiple chunks etc + // Skip the LF of a CRLF, whose CR may have ended the previous chunk if (discardTrailingNewline) { - if (buffer[position] === "\n") { + discardTrailingNewline = false + if (chunk.charCodeAt(position) === LF) { ++position + continue } - discardTrailingNewline = false } - let lineLength = -1 - let fieldLength = startingFieldLength - let character: string - - for (let index = position + startingPosition; lineLength < 0 && index < length; ++index) { - character = buffer[index] - if (character === ":" && fieldLength < 0) { - fieldLength = index - position - } else if (character === "\r") { - discardTrailingNewline = true - lineLength = index - position - } else if (character === "\n") { - lineLength = index - position - } + let lineEnd = position + while (lineEnd < length) { + const code = chunk.charCodeAt(lineEnd) + if (code === CR || code === LF) break + ++lineEnd } - if (lineLength < 0) { - startingPosition = length - position - startingFieldLength = fieldLength + if (lineEnd === length) { + pending.push(chunk.slice(position)) + pendingLength += length - position break - } else { - startingPosition = 0 - startingFieldLength = -1 } - parseEventStreamLine(buffer, position, fieldLength, lineLength) + discardTrailingNewline = chunk.charCodeAt(lineEnd) === CR - position += lineLength + 1 - } + let line = chunk.slice(position, lineEnd) + if (pending.length > 0) { + pending.push(line) + line = pending.join("") + pending = [] + pendingLength = 0 + } + parseLine(line) + + if (generation !== currentGeneration) { + // `onParse` reset the parser, so drop the rest of this chunk + return + } - if (position === length) { - // If we consumed the entire buffer to read the event, reset the buffer - buffer = "" - } else if (position > 0) { - // If there are bytes left to process, set the buffer to the unprocessed - // portion of the buffer only - buffer = buffer.slice(position) + position = lineEnd + 1 } - if (buffer.length + data.length > maxEventSize) { + if (pendingLength + data.length > maxEventSize) { const error = new SseError({ reason: new EventTooLarge({ maxEventSize }) }) reset() return error } } - function parseEventStreamLine( - lineBuffer: string, - index: number, - fieldLength: number, - lineLength: number - ) { - if (lineLength === 0) { - // We reached the last line of this event - if (data.length > 0) { + function parseLine(line: string) { + if (line.length === 0) { + // End of event. Clear the state first so a throwing `onParse` cannot + // leak it into the next event + const named = eventName !== undefined && eventName !== "" + const event = eventName || "message" + const eventData = data + eventName = undefined + data = "" + if (eventData.length > 0 || (dispatchEmptyNamedEvents && named)) { onParse({ _tag: "Event", id: lastEventId, - event: eventName || "message", - data: data.slice(0, -1) // remove trailing newline + event, + data: eventData.slice(0, -1) // remove trailing newline }) - data = "" } - eventName = undefined return } - const noValue = fieldLength < 0 - const field = lineBuffer.slice(index, index + (noValue ? lineLength : fieldLength)) - let step = 0 - - if (noValue) { - step = lineLength - } else if (lineBuffer[index + fieldLength + 1] === " ") { - step = fieldLength + 2 - } else { - step = fieldLength + 1 + const colon = line.indexOf(":") + let field = line + let value = "" + if (colon >= 0) { + field = line.slice(0, colon) + value = line.slice(line.charCodeAt(colon + 1) === SPACE ? colon + 2 : colon + 1) } - const position = index + step - const valueLength = lineLength - step - const value = lineBuffer.slice(position, position + valueLength).toString() - if (field === "data") { - data += value ? `${value}\n` : "\n" + data += value + "\n" } else if (field === "event") { eventName = value } else if (field === "id" && !value.includes("\u0000")) { @@ -425,6 +412,9 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO } const BOM = "\uFEFF" +const LF = 10 +const CR = 13 +const SPACE = 32 /** * Stateful Server-Sent Events parser returned by `makeParser`. @@ -693,7 +683,7 @@ export const encoder: Encoder = { if (event.event !== "message") { data += `event: ${event.event}\n` } - data += `data: ${event.data.replace(/\n/g, "\ndata: ")}\n` + data += `data: ${event.data.replace(/\r\n|\r|\n/g, "\ndata: ")}\n` return data + "\n" } case "Retry": { diff --git a/repos/effect/packages/effect/src/encoding/Toml.ts b/packages/effect/src/encoding/Toml.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Toml.ts rename to packages/effect/src/encoding/Toml.ts diff --git a/repos/effect/packages/effect/src/encoding/Yaml.ts b/packages/effect/src/encoding/Yaml.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/Yaml.ts rename to packages/effect/src/encoding/Yaml.ts diff --git a/repos/effect/packages/effect/src/encoding/index.ts b/packages/effect/src/encoding/index.ts similarity index 100% rename from repos/effect/packages/effect/src/encoding/index.ts rename to packages/effect/src/encoding/index.ts diff --git a/repos/effect/packages/effect/src/eventlog/Event.ts b/packages/effect/src/eventlog/Event.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/Event.ts rename to packages/effect/src/eventlog/Event.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventGroup.ts b/packages/effect/src/eventlog/EventGroup.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventGroup.ts rename to packages/effect/src/eventlog/EventGroup.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventJournal.ts b/packages/effect/src/eventlog/EventJournal.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventJournal.ts rename to packages/effect/src/eventlog/EventJournal.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLog.ts b/packages/effect/src/eventlog/EventLog.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLog.ts rename to packages/effect/src/eventlog/EventLog.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts b/packages/effect/src/eventlog/EventLogEncryption.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogEncryption.ts rename to packages/effect/src/eventlog/EventLogEncryption.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogMessage.ts b/packages/effect/src/eventlog/EventLogMessage.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogMessage.ts rename to packages/effect/src/eventlog/EventLogMessage.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogRemote.ts b/packages/effect/src/eventlog/EventLogRemote.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogRemote.ts rename to packages/effect/src/eventlog/EventLogRemote.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogServer.ts b/packages/effect/src/eventlog/EventLogServer.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogServer.ts rename to packages/effect/src/eventlog/EventLogServer.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts b/packages/effect/src/eventlog/EventLogServerEncrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogServerEncrypted.ts rename to packages/effect/src/eventlog/EventLogServerEncrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogServerUnencrypted.ts b/packages/effect/src/eventlog/EventLogServerUnencrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogServerUnencrypted.ts rename to packages/effect/src/eventlog/EventLogServerUnencrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts b/packages/effect/src/eventlog/EventLogSessionAuth.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/EventLogSessionAuth.ts rename to packages/effect/src/eventlog/EventLogSessionAuth.ts diff --git a/repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts b/packages/effect/src/eventlog/SqlEventJournal.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/SqlEventJournal.ts rename to packages/effect/src/eventlog/SqlEventJournal.ts diff --git a/repos/effect/packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts b/packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts rename to packages/effect/src/eventlog/SqlEventLogServerEncrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts b/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts rename to packages/effect/src/eventlog/SqlEventLogServerUnencrypted.ts diff --git a/repos/effect/packages/effect/src/eventlog/index.ts b/packages/effect/src/eventlog/index.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/index.ts rename to packages/effect/src/eventlog/index.ts diff --git a/repos/effect/packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts b/packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts similarity index 100% rename from repos/effect/packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts rename to packages/effect/src/eventlog/internal/identityRootSecretDerivation.ts diff --git a/packages/effect/src/graphql/GraphQL.ts b/packages/effect/src/graphql/GraphQL.ts new file mode 100644 index 000000000..e15ebfbac --- /dev/null +++ b/packages/effect/src/graphql/GraphQL.ts @@ -0,0 +1,494 @@ +/** + * Typed GraphQL operations for `GraphQLGroup` and `GraphQLClient.make`, + * written by hand or generated by `@effect/graphql-generator`. + * + * Includes the generator's lenient decoders, {@link otherTypename} and + * {@link enumLiterals}. + * + * @stability experimental + * @since 4.0.0 + */ +import * as internal from "../internal/graphql.ts" +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import { hasProperty } from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import type { GraphQLClientError } from "./GraphQLClientError.ts" +import type * as GraphQLMiddleware from "./GraphQLMiddleware.ts" + +const TypeId = "~effect/graphql/GraphQL" + +/** + * The three kinds of GraphQL operation. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type Kind = "query" | "mutation" | "subscription" + +/** + * An operation document split into the pieces needed to rename and alias it + * without a GraphQL parser. The generator emits one for every query and + * mutation. + * + * **Details** + * + * A `body` is a {@link DocumentTemplate.Parts} array: literal text at even + * indices and, at odd indices, the variable names and fragment names that + * would be renamed if the operation were repeated under a prefix. Joining the + * pieces gives the printed document, which is what the operation sends. + * + * A fragment whose `body` has a name in it is per-slice: it references a + * variable, directly or through a fragment it spreads. Every other fragment is + * shared. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DocumentTemplate { + /** The variable definitions, in document order. */ + readonly variables: ReadonlyArray + /** The operation's directives, if it has any. */ + readonly directives?: DocumentTemplate.Parts | undefined + /** The root selections, in document order. */ + readonly selections: ReadonlyArray + /** The fragments the operation uses, in the order they follow it. */ + readonly fragments: ReadonlyArray +} + +/** + * The pieces of a {@link DocumentTemplate}. + * + * @stability experimental + * @since 4.0.0 + */ +export declare namespace DocumentTemplate { + /** + * Literal text at even indices and names to rename at odd indices. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ + export type Parts = ReadonlyArray + + /** + * A variable definition: `$name:` followed by `body`, the type, default + * value and directives as printed. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ + export interface Variable { + readonly name: string + readonly body: string + } + + /** + * A root selection. For a field, `body` is everything after its name and + * the response key is `alias ?? name`. A fragment spread or inline fragment + * has no `name`, and `body` is the whole selection. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ + export interface Selection { + readonly alias?: string | undefined + readonly name?: string | undefined + readonly body: Parts + } + + /** + * A fragment definition: `fragment` and `name`, followed by `body`, which + * starts at its type condition. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ + export interface Fragment { + readonly name: string + readonly body: Parts + } +} + +/** + * A named GraphQL document with variable and result schemas and middleware. + * Variables are encoded before sending; results are decoded from `data`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Operation< + out K extends Kind, + out Name extends string, + out Variables extends Schema.Top, + out Result extends Schema.Top, + out Middleware = never +> extends Pipeable { + readonly [TypeId]: typeof TypeId + readonly kind: K + readonly name: Name + /** The printed document, sent as-is. It is never parsed at runtime. */ + readonly document: string + /** The template the document was joined from, if it was given as one. */ + readonly template: DocumentTemplate | undefined + readonly variables: Variables + readonly result: Result + /** Middleware attached to this operation, outermost first. */ + readonly middlewares: ReadonlyArray + readonly "~middleware"?: Middleware + /** + * Returns a copy of this operation with `middleware` attached. Operation + * middleware runs inside any group middleware; within the operation, + * middleware run in the order they were attached. + */ + middleware( + middleware: M + ): Operation +} + +/** + * Any operation, with its type parameters erased. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Any = Operation + +/** + * Extracts the union of middleware attached to an operation. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Middleware = Op extends Operation ? M : never + +/** + * The decoded variables type of an operation, which is what a client method + * takes. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Variables = Op["variables"]["Type"] + +/** + * The decoded result type of an operation. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Result = Op["result"]["Type"] + +/** + * The error type of a client method for an operation: `GraphQLClientError` + * plus the errors of every attached middleware. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Error = GraphQLClientError | GraphQLMiddleware.Error> + +/** + * The services a client method for an operation requires: the Schemas' + * encoding and decoding services plus the requirements of every attached + * middleware. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Services = + | Op["variables"]["EncodingServices"] + | Op["result"]["DecodingServices"] + | GraphQLMiddleware.Requires> + +/** + * The type of an operation after attaching middleware `M`. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type AddMiddleware = Op extends Operation + ? Operation + : never + +/** + * Tests whether a value is an {@link Operation}. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isOperation = (u: unknown): u is Any => hasProperty(u, TypeId) + +const Proto = { + [TypeId]: TypeId, + pipe() { + return pipeArguments(this, arguments) + }, + middleware(this: Any, middleware: GraphQLMiddleware.AnyService) { + return makeProto({ ...this, middlewares: [...this.middlewares, middleware] }) + } +} + +// The middleware type of an operation is phantom and its Schemas are erased to +// `Schema.Top` here, so the typed constructors below state their operation +// type with one assertion each. +const makeProto = (fields: Omit): Any => + Object.assign(Object.create(Proto), fields) + +const makeOperation = (kind: K) => +< + const Name extends string, + Result extends Schema.Top, + Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}> +>( + name: Name, + options: { + readonly document: string | DocumentTemplate + readonly variables?: Variables | undefined + readonly result: Result + } +): Operation : Variables, Result> => + makeProto({ + kind, + name, + document: typeof options.document === "string" + ? options.document + : internal.joinTemplate(kind, name, options.document), + template: typeof options.document === "string" ? undefined : options.document, + variables: options.variables === undefined + ? Schema.Struct({}) + : Schema.isSchema(options.variables) + ? options.variables + : Schema.Struct(options.variables as Schema.Struct.Fields), + result: options.result, + middlewares: [] + }) as Operation : Variables, Result> + +/** + * Defines a query. The client accepts decoded variables and encodes them + * through their Schema, including custom scalar codecs. `variables` accepts + * struct fields or a Schema; omit it for an operation without variables. + * `document` accepts printed text or a {@link DocumentTemplate}, joined once + * when the operation is defined. + * + * **Example** (A query with variables) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL } from "effect/graphql" + * + * const RepoIssues = GraphQL.query("RepoIssues", { + * document: "query RepoIssues($owner:String!,$name:String!){repository(owner:$owner,name:$name){issues(first:10){nodes{number title}}}}", + * variables: { owner: Schema.String, name: Schema.String }, + * result: Schema.Struct({ + * repository: Schema.NullOr(Schema.Struct({ + * issues: Schema.Struct({ + * nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Schema.Struct({ number: Schema.Int, title: Schema.String })))) + * }) + * })) + * }) + * }) + * + * RepoIssues.kind // => "query" + * RepoIssues.name // => "RepoIssues" + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const query: < + const Name extends string, + Result extends Schema.Top, + Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}> +>( + name: Name, + options: { + readonly document: string | DocumentTemplate + readonly variables?: Variables | undefined + readonly result: Result + } +) => Operation<"query", Name, Variables extends Schema.Struct.Fields ? Schema.Struct : Variables, Result> = + makeOperation("query") + +/** + * Defines a mutation. Same shape as {@link query}. + * + * **Example** (A mutation) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL } from "effect/graphql" + * + * const AddComment = GraphQL.mutation("AddComment", { + * document: "mutation AddComment($subjectId:ID!,$body:String!){addComment(input:{subjectId:$subjectId,body:$body}){clientMutationId}}", + * variables: { subjectId: Schema.String, body: Schema.String }, + * result: Schema.Struct({ addComment: Schema.NullOr(Schema.Struct({ clientMutationId: Schema.NullOr(Schema.String) })) }) + * }) + * + * AddComment.kind // => "mutation" + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const mutation: < + const Name extends string, + Result extends Schema.Top, + Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}> +>( + name: Name, + options: { + readonly document: string | DocumentTemplate + readonly variables?: Variables | undefined + readonly result: Result + } +) => Operation< + "mutation", + Name, + Variables extends Schema.Struct.Fields ? Schema.Struct : Variables, + Result +> = makeOperation("mutation") + +/** + * Defines a subscription. The client method for a subscription returns a + * `Stream` of decoded events instead of an `Effect`. + * + * **Example** (A subscription) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL } from "effect/graphql" + * + * const IssueUpdated = GraphQL.subscription("IssueUpdated", { + * document: "subscription IssueUpdated($id:ID!){issueUpdated(id:$id){title}}", + * variables: { id: Schema.String }, + * result: Schema.Struct({ issueUpdated: Schema.Struct({ title: Schema.String }) }) + * }) + * + * IssueUpdated.kind // => "subscription" + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const subscription: < + const Name extends string, + Result extends Schema.Top, + Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}> +>( + name: Name, + options: { + readonly document: string | DocumentTemplate + readonly variables?: Variables | undefined + readonly result: Result + } +) => Operation< + "subscription", + Name, + Variables extends Schema.Struct.Fields ? Schema.Struct : Variables, + Result +> = makeOperation("subscription") + +/** + * The `__typename` Schema for the "every other type" member of a generated + * union. + * + * **Details** + * + * The type excludes selected names, allowing `__typename` narrowing. The + * decoder accepts any other string, including types added by the server + * later. Selected names are rejected so malformed selected results cannot + * fall through to the catch-all member. + * + * **Example** (The catch-all member of a union) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL } from "effect/graphql" + * + * type Node = "Issue" | "PullRequest" | "Repository" + * + * const OtherNode = Schema.Struct({ + * __typename: GraphQL.otherTypename()(["Issue"]), + * id: Schema.String + * }) + * + * Schema.decodeUnknownSync(OtherNode)({ __typename: "PullRequest", id: "1" }).__typename // => "PullRequest" + * Schema.decodeUnknownSync(OtherNode)({ __typename: "AddedLater", id: "2" }).__typename // => "AddedLater" + * Schema.is(OtherNode)({ __typename: "Issue", id: "3" }) // => false + * ``` + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const otherTypename = () => +>( + selected: Selected +): Schema.refine, Schema.String> => { + const rejected = new Set(selected) + // Type boundary: decoding is lenient by design. The guard passes any string + // other than the selected names, including names the server adds later, but + // the type only lists the names known when the code was generated. + return Schema.refine>( + (name): name is Exclude => !rejected.has(name), + { + title: "otherTypename", + message: `Expected a __typename other than ${selected.map((s) => `"${s}"`).join(", ")}` + } + )(Schema.String) +} + +/** + * The Schema for a GraphQL enum in a result position. + * + * **Details** + * + * It is typed as the declared literals but decodes any string, so a value the + * server adds later does not fail the whole query. Inputs stay strict: the + * generator uses `Schema.Literals` for enums in variables and input objects. + * + * **Example** (An enum that tolerates new values) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL } from "effect/graphql" + * + * const IssueState = GraphQL.enumLiterals(["OPEN", "CLOSED"]) + * + * Schema.decodeUnknownSync(IssueState)("OPEN") // => "OPEN" + * Schema.decodeUnknownSync(IssueState)("MERGED") // => "MERGED" + * Schema.is(IssueState)(42) // => false + * ``` + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const enumLiterals = >( + literals: Literals +): Schema.refine => + // Type boundary: decoding is lenient by design. The guard passes any string, + // including values the server adds later, but the type only lists the values + // known when the code was generated. + Schema.refine((_): _ is Literals[number] => true)(Schema.String).annotate({ + title: `enum(${literals.join(" | ")})`, + description: `One of ${literals.map((l) => `"${l}"`).join(", ")}, or any string the server adds later` + }) diff --git a/packages/effect/src/graphql/GraphQLClient.ts b/packages/effect/src/graphql/GraphQLClient.ts new file mode 100644 index 000000000..98ff5eb54 --- /dev/null +++ b/packages/effect/src/graphql/GraphQLClient.ts @@ -0,0 +1,924 @@ +/** + * A typed client with one method per operation in a `GraphQLGroup`. Queries + * and mutations return an `Effect`; subscriptions return a `Stream`. The + * client encodes variables, runs middleware, calls the `GraphQLProtocol` in + * context and decodes results. + * + * Retryable subscription failures restart the middleware chain on the + * `subscriptionRetry` schedule. {@link pages} and {@link items} provide + * cursor pagination. {@link makeTest} builds a mock client for tests, and + * {@link layerTest} provides it as a service layer. See `GraphQLProtocol` + * for transport options. + * + * @stability experimental + * @since 4.0.0 + */ +import * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import { identity } from "../Function.ts" +import * as Headers from "../http/Headers.ts" +import * as UrlParams from "../http/UrlParams.ts" +import * as internalGraphQL from "../internal/graphql.ts" +import { getStackTraceLimit, setStackTraceLimit } from "../internal/stackTraceLimit.ts" +import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" +import type * as Pull from "../Pull.ts" +import * as Schedule from "../Schedule.ts" +import * as Schema from "../Schema.ts" +import * as Stream from "../Stream.ts" +import * as Struct from "../Struct.ts" +import type * as Types from "../Types.ts" +import type * as GraphQL from "./GraphQL.ts" +import { + DecodeError, + GraphQLClientError, + type GraphQLError, + isGraphQLClientError, + PaginationError +} from "./GraphQLClientError.ts" +import type * as GraphQLGroup from "./GraphQLGroup.ts" +import type * as GraphQLMiddleware from "./GraphQLMiddleware.ts" +import { ExecutionResult, GraphQLProtocol, type GraphQLRequest } from "./GraphQLProtocol.ts" + +/** + * Decoded `data` and response `errors` for a `{ partial: true }` call. + * The errors array may be empty. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface PartialResult { + readonly data: A + readonly errors: ReadonlyArray +} + +type Args = {} extends Variables ? [variables?: Variables, options?: Options] + : [variables: Variables, options?: Options] + +type ArgsWithOptions = {} extends Variables ? [variables: Variables | undefined, options: Options] + : [variables: Variables, options: Options] + +/** + * The client method for a query or mutation. + * + * **Details** + * + * Called as `(variables?, options?)`. Without `partial`, or with + * `partial: false`, it returns the decoded result and fails with a + * `ResponseError` on any `errors`. With `partial: true` it returns + * `{ data, errors }`. `partial` must be a literal `true` or `false`, not + * an arbitrary `boolean`. + * + * An operation without variables is called as `client.Viewer()` or, with + * options, `client.Viewer(undefined, { headers })`. + * + * `headers` and `urlParams` are sent by the HTTP transport, ignored over + * graphql-ws, and visible to middleware either way as + * `GraphQLRequest.headers` and `GraphQLRequest.urlParams`. Header options + * accept `Headers.Input` and are normalized to `Headers.Headers` before + * middleware runs. `context` provides + * services for this call only, and removes them from the method's + * requirements. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface OperationMethod { + /** The operation this method runs. */ + readonly operation: Op + ( + ...args: ArgsWithOptions, { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined + readonly context?: Context.Context | undefined + readonly partial: true + }> + ): Effect.Effect>, GraphQL.Error, Exclude, R2>> + ( + ...args: Args, { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined + readonly context?: Context.Context | undefined + readonly partial?: false | undefined + }> + ): Effect.Effect, GraphQL.Error, Exclude, R2>> +} + +/** + * The client method for a subscription. It returns a `Stream` of decoded + * events; an event with `errors` fails the stream with a `ResponseError`, + * unless that error is retryable and the client resubscribes (see + * {@link make}). `headers`, `urlParams` and `context` work as for + * {@link OperationMethod}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface SubscriptionMethod { + /** The operation this method runs. */ + readonly operation: Op + ( + ...args: Args, { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined + readonly context?: Context.Context | undefined + }> + ): Stream.Stream, GraphQL.Error, Exclude, R2>> +} + +/** + * The client method type for an operation, by its kind. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type Method = Op["kind"] extends "subscription" ? SubscriptionMethod + : OperationMethod + +/** + * A client: one method per operation, keyed by operation name. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type GraphQLClient = { + readonly [Op in Ops as Op["name"]]: Method +} + +/** + * Derives the client type for all operations in a `GraphQLGroup`. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type FromGroup = GraphQLClient> + +/** + * The default subscription retry schedule: exponential from 500 milliseconds + * with factor 1.5, capped at 5 seconds, and never shorter than the server's + * `retryAfter`. + * + * @stability experimental + * @category constants + * @since 4.0.0 + */ +export const defaultSubscriptionRetry: Schedule.Schedule = Schedule.exponential( + "500 millis", + 1.5 +).pipe( + Schedule.setInputType(), + Schedule.modifyDelay(({ duration, input }) => { + const capped = Duration.min(duration, Duration.seconds(5)) + return Effect.succeed(input.retryAfter === undefined ? capped : Duration.max(capped, input.retryAfter)) + }) +) + +/** + * Builds a client from a group, requiring `GraphQLProtocol` and every + * middleware service attached to the group or its operations. + * + * **Details** + * + * Group middleware runs outside operation middleware. Within each level, + * middleware run in the order attached. + * + * `subscriptionRetry` decides how a subscription is retried after it fails + * with a retryable error: usually a `TransportError`, but also a + * `ResponseError` whose response carried a valid `Retry-After`, including an + * event with `errors` on a graphql-sse stream whose response had that header. + * It defaults to {@link defaultSubscriptionRetry}. Fatal errors are never + * retried, whatever the schedule. A retry starts a new subscription, so the + * server may send events again that the stream already emitted. + * + * **Example** (A client as a service) + * + * ```ts import.meta.vitest + * import { Context, Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * + * // What the generator emits for viewer.graphql + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * const ViewerGraphQLGroup = GraphQLGroup.make(Viewer) + * + * class GitHub extends Context.Service()("app/GitHub", { + * make: GraphQLClient.make(ViewerGraphQLGroup) + * }) {} + * + * // A scripted transport; use GraphQLProtocol.layerHttp({ url }) for a real server + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: () => Effect.succeed({ body: { data: { viewer: { login: "tim" } } } }), + * subscribe: () => Stream.empty + * }) + * + * const program = GitHub.use((github) => github.Viewer()).pipe( + * Effect.provide(Layer.effect(GitHub, GitHub.make).pipe(Layer.provide(Scripted))) + * ) + * + * await Effect.runPromise(program) // => { viewer: { login: "tim" } } + * ``` + * + * **Example** (Partial results) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * + * const Repos = GraphQL.query("Repos", { + * document: "query Repos{a:repository(owner:\"x\",name:\"a\"){name} b:repository(owner:\"x\",name:\"b\"){name}}", + * result: Schema.Struct({ + * a: Schema.NullOr(Schema.Struct({ name: Schema.String })), + * b: Schema.NullOr(Schema.Struct({ name: Schema.String })) + * }) + * }) + * + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: () => + * Effect.succeed({ + * body: { + * data: { a: { name: "a" }, b: null }, + * errors: [{ message: "Could not resolve to a Repository", path: ["b"] }] + * } + * }), + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Repos)) + * // The default call fails with a ResponseError because `errors` is non-empty + * const failed = yield* Effect.flip(client.Repos()) + * // With partial: true the decoded data comes back together with the errors + * const { data, errors } = yield* client.Repos(undefined, { partial: true }) + * return [failed.reason._tag, data.a?.name, errors[0].path?.[0]] + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => ["ResponseError", "a", "b"] + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const make = ( + group: GraphQLGroup.GraphQLGroup, + options?: { + readonly subscriptionRetry?: Schedule.Schedule | undefined + } | undefined +): Effect.Effect< + GraphQLClient, + never, + GraphQLProtocol | GraphQLMiddleware.Identifier> +> => + Effect.gen(function*() { + const protocol = yield* GraphQLProtocol + const context = yield* Effect.context() + const retryPolicy = retryableOnly(options?.subscriptionRetry ?? defaultSubscriptionRetry) + return fromGroup(group, (operation) => + makeMethod(operation, middlewareChain(context, group, operation), protocol, retryPolicy)) + }) + +// Type boundary: a mapping over the operations cannot prove the mapped type's +// name/kind relationship. +const fromGroup = ( + group: GraphQLGroup.GraphQLGroup, + f: (operation: Ops) => unknown +): GraphQLClient => + Object.fromEntries(group.operations.map((operation) => [operation.name, f(operation)])) as GraphQLClient + +/** + * The middleware implementations an operation runs through, outermost first, + * typed with the errors and requirements its middleware declared. + */ +type MiddlewareChain = ReadonlyArray< + GraphQLMiddleware.Implementation< + GraphQLMiddleware.Error>, + GraphQLMiddleware.Requires> + > +> + +// Runtime tags erase middleware types; `make` requires all their identifiers. +const middlewareChain = ( + context: Context.Context, + group: GraphQLGroup.GraphQLGroup, + operation: Op +): MiddlewareChain => [...group.middlewares, ...operation.middlewares].map((tag) => Context.getUnsafe(context, tag)) + +// Steps `schedule` only for retryable `GraphQLClientError`s. Middleware +// errors and fatal errors end the retries before reaching it, so a custom +// schedule can rely on its input being a `GraphQLClientError`. +const retryableOnly = ( + schedule: Schedule.Schedule +): Schedule.Schedule => + Schedule.fromStep(Effect.map( + Schedule.toStep(schedule), + (step) => + (now: number, input: unknown): Pull.Pull<[Output | undefined, Duration.Duration], Error, Output | undefined, Env> => + isGraphQLClientError(input) && input.isRetryable ? step(now, input) : Cause.done(undefined) + )) + +// `status` and `headers` are transport metadata, never read from the body. +const decodeExecutionResult = Schema.decodeUnknownEffect(ExecutionResult.mapFields(Struct.omit(["status", "headers"]))) + +interface CallOptions { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined + readonly context?: Context.Context | undefined + readonly partial?: boolean | undefined +} + +// Type boundary: a call without `context` has `R2 = never` unless the caller +// names `R2` explicitly, so an empty context stands in for `Context`. +const callContext = (options: CallOptions | undefined): Context.Context => + options?.context ?? (Context.empty() as Context.Context) + +const makeMethod = ( + operation: Op, + chain: MiddlewareChain, + protocol: GraphQLProtocol["Service"], + retryPolicy: Schedule.Schedule +): OperationMethod | SubscriptionMethod => { + const { decodePartial, decodeStrict, encodeVariables, fail, schemaFail } = internalGraphQL.makeCodec(operation) + + const notGraphQL = schemaFail((description) => + new DecodeError({ description: `Not a GraphQL response: ${description}` }) + ) + + const buildRequest = (variables: GraphQL.Variables | undefined, options: CallOptions | undefined) => + encodeVariables(variables).pipe( + Effect.map((encoded): GraphQLRequest => ({ + kind: operation.kind, + query: operation.document, + operationName: operation.name, + variables: encoded, + headers: Headers.fromInput(options?.headers), + urlParams: options?.urlParams === undefined ? UrlParams.empty : UrlParams.fromInput(options.urlParams) + })) + ) + + const decodeResponse = ({ body, ...metadata }: { + readonly body: unknown + readonly status?: number | undefined + readonly headers?: Headers.Headers | undefined + }) => + decodeExecutionResult(body).pipe( + Effect.map((result): ExecutionResult => ({ ...result, ...metadata })), + Effect.catch(notGraphQL) + ) + + // Type boundary: a middleware sees the errors of the middleware further in + // as `Types.unhandled` (as in `RpcMiddleware`), and none of their + // requirements. Those errors are part of `GraphQL.Error`, so the chain + // restates its type here. Force POST and the resolved URL apply only at the + // transport boundary. + const execute = ( + index: number, + request: GraphQLRequest, + forcePost: boolean, + resolvedUrl: internalGraphQL.ResolvedUrl + ): Effect.Effect, GraphQLMiddleware.Requires>> => + index === chain.length + ? protocol.execute(request).pipe( + forcePost ? Effect.provideService(internalGraphQL.ForcePost, true) : identity, + resolvedUrl === undefined ? identity : Effect.provideService(internalGraphQL.ResolvedUrl, resolvedUrl), + Effect.mapError(fail), + Effect.flatMap(decodeResponse) + ) + : chain[index].execute({ + operation, + request, + next: (request) => + execute(index + 1, request, forcePost, resolvedUrl) as Effect.Effect< + ExecutionResult, + GraphQLClientError | Types.unhandled + > + }) as Effect.Effect, GraphQLMiddleware.Requires>> + + // Hands a resolver batch's URL to the transport only, so calls made by + // middleware resolve their own. + const run = (request: GraphQLRequest, forcePost: boolean) => + internalGraphQL.ResolvedUrl.use((resolved) => + resolved === undefined + ? execute(0, request, forcePost, undefined) + : execute(0, request, forcePost, resolved).pipe(Effect.provideService(internalGraphQL.ResolvedUrl, undefined)) + ) + + const subscribe = ( + index: number, + request: GraphQLRequest + ): Stream.Stream, GraphQLMiddleware.Requires>> => + index === chain.length + ? protocol.subscribe(request).pipe( + Stream.mapError(fail), + Stream.mapEffect(decodeResponse) + ) + : chain[index].subscribe({ + operation, + request, + next: (request) => + subscribe(index + 1, request) as Stream.Stream + }) as Stream.Stream, GraphQLMiddleware.Requires>> + + if (operation.kind === "subscription") { + const subscriptionMethod = (...[variables, options]: Args, CallOptions>) => + Stream.unwrap(Effect.map(buildRequest(variables, options), (request) => subscribe(0, request))).pipe( + Stream.mapEffect(decodeStrict), + Stream.retry(retryPolicy), + Stream.provideContext(callContext(options)) + ) + return Object.assign(subscriptionMethod, { operation }) + } + + function method( + ...args: ArgsWithOptions, CallOptions & { readonly partial: true }> + ): Effect.Effect>, GraphQL.Error, Exclude, R2>> + function method( + ...args: Args, CallOptions & { readonly partial?: false | undefined }> + ): Effect.Effect, GraphQL.Error, Exclude, R2>> + function method( + variables?: GraphQL.Variables, + options?: CallOptions + ): Effect.Effect< + GraphQL.Result | PartialResult>, + GraphQL.Error, + Exclude, R2> + > { + return buildRequest(variables, options).pipe( + Effect.flatMap((request) => run(request, false)), + Effect.flatMap( + (result): Effect.Effect< + GraphQL.Result | PartialResult>, + GraphQLClientError, + Op["result"]["DecodingServices"] + > => options?.partial === true ? decodePartial(result) : decodeStrict(result) + ), + Effect.provideContext(callContext(options)) + ) + } + internalGraphQL.rawExecutors.set(method, { execute: (request) => run(request, true), url: protocol.url }) + return Object.assign(method, { operation }) +} + +/** + * The `pageInfo` a paged connection must select. + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export interface PageInfo { + readonly hasNextPage: boolean + readonly endCursor: string | null +} + +/** + * What the `connection` getter of {@link pages} must return. + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export interface Connection { + readonly pageInfo: PageInfo +} + +/** + * What the `connection` getter of {@link items} must return: a connection + * that also selects `nodes`. + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export interface NodesConnection extends Connection { + readonly nodes: ReadonlyArray | null +} + +/** + * The type `variables` is given when the operation has no `$after` variable. + * Nothing is assignable to it, so the mistake is a compile error at the call. + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export interface MissingAfterVariable { + readonly "effect/graphql": + "GraphQLClient.pages requires the operation to declare a nullable `$after: String` variable" +} + +/** + * The type `variables` is given when the operation's `after` variable is + * required or does not accept a `string`. The helper leaves `after` out on the + * first page and sends the previous `endCursor` afterwards, so it can only + * drive an optional variable that takes a string. + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export interface InvalidAfterVariable { + readonly "effect/graphql": "GraphQLClient.pages requires the `after` variable to be optional and to accept a string" +} + +/** + * The `variables` accepted by {@link pages} and {@link items}: the method's + * variables without `after`, which the helper supplies. + * + * **Details** + * + * The operation's `after` variable must be optional and accept a `string`. + * `after?: string | null`, from a nullable `$after: String`, and + * `after?: string`, from a `$after: String!` with a default value, are both + * accepted. A required `after` gives {@link InvalidAfterVariable}, as does one + * that does not accept a `string`; no `after` at all gives + * {@link MissingAfterVariable}. + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export type PagingVariables = "after" extends keyof Variables + ? {} extends Pick ? string extends Variables["after"] ? Omit + : InvalidAfterVariable + : InvalidAfterVariable + : MissingAfterVariable + +/** + * Pages forward through a cursor connection, emitting one connection per + * page. + * + * **Details** + * + * Requires an optional string `$after` variable (see {@link PagingVariables}). + * The first request omits `after`; subsequent requests use the previous + * `endCursor`. Pages are fetched only when pulled, so `Stream.take` stops + * further requests. Middleware runs on each page. + * + * `connection` extracts the connection from each result. `options` is + * forwarded to every call but cannot include `partial`: errors fail the stream. + * Its `headers` accept `Headers.Input`, just as for operation calls. + * + * The stream ends after a page whose `hasNextPage` is `false`. A `null` + * connection on the first page gives an empty stream; on a later page it + * fails with a `PaginationError`, as does `hasNextPage: true` with a `null` + * or unchanged `endCursor`. + * + * Root fields that come with every page, such as GitHub's `rateLimit`, are for + * middleware to read from the raw `ExecutionResult`. + * + * **Example** (Paging issues, two per page) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * + * const RepoIssues = GraphQL.query("RepoIssues", { + * document: "query RepoIssues($owner:String!,$name:String!,$after:String){repository(owner:$owner,name:$name){issues(first:2,after:$after){pageInfo{hasNextPage endCursor}nodes{number}}}}", + * variables: { owner: Schema.String, name: Schema.String, after: Schema.optional(Schema.NullOr(Schema.String)) }, + * result: Schema.Struct({ + * repository: Schema.NullOr(Schema.Struct({ + * issues: Schema.Struct({ + * pageInfo: Schema.Struct({ hasNextPage: Schema.Boolean, endCursor: Schema.NullOr(Schema.String) }), + * nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Schema.Struct({ number: Schema.Int })))) + * }) + * })) + * }) + * }) + * + * // A scripted server with two pages + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => { + * const after = (request.variables as { after?: string }).after + * const page = after === undefined + * ? { pageInfo: { hasNextPage: true, endCursor: "c1" }, nodes: [{ number: 1 }, { number: 2 }] } + * : { pageInfo: { hasNextPage: false, endCursor: null }, nodes: [{ number: 3 }] } + * return Effect.succeed({ body: { data: { repository: { issues: page } } } }) + * }, + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoIssues)) + * const pages = GraphQLClient.pages(client.RepoIssues, { + * variables: { owner: "Effect-TS", name: "effect" }, + * connection: (result) => result.repository?.issues + * }) + * return yield* Stream.runCollect(Stream.map(pages, (page) => page.pageInfo.endCursor)) + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => ["c1", null] + * ``` + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export const pages = ( + method: OperationMethod, + options: { + readonly variables: PagingVariables> + readonly connection: (result: GraphQL.Result) => C | null | undefined + readonly options?: { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined + readonly context?: Context.Context | undefined + } | undefined + } +): Stream.Stream, Exclude, R2>> => { + // Type boundary: `method`'s strict overload, with `variables` widened. + // `PagingVariables` has already checked that `options.variables` plus an + // optional string `after` are the operation's variables, but the overloads' + // conditional parameter types over a generic `Op` cannot carry that here. + const call = method as unknown as ( + variables: unknown, + options: CallOptions | undefined + ) => Effect.Effect, GraphQL.Error, Exclude, R2>> + // The state is the cursor to send; the first page is fetched without one. + return Stream.paginate(undefined as string | undefined, (after) => + Effect.flatMap( + call(after === undefined ? options.variables : { ...options.variables, after }, options.options), + (result): Effect.Effect, Option.Option], GraphQLClientError> => { + const paginationError = (description: string) => + Effect.fail( + new GraphQLClientError({ + operation: method.operation.name, + reason: new PaginationError({ description, cursor: after ?? null }) + }) + ) + const connection = options.connection(result) + if (connection === null || connection === undefined) { + return after === undefined + ? Effect.succeed([[], Option.none()]) + : paginationError("The connection was null on a page after the first") + } + const { endCursor, hasNextPage } = connection.pageInfo + if (!hasNextPage) return Effect.succeed([[connection], Option.none()]) + if (endCursor === null) return paginationError("hasNextPage is true but endCursor is null") + if (endCursor === after) return paginationError(`endCursor "${endCursor}" did not advance`) + return Effect.succeed([[connection], Option.some(endCursor)]) + } + )) +} + +/** + * Like {@link pages}, but emits decoded entries from `nodes`, including + * `null` entries. Use `Stream.filter(Predicate.isNotNull)` to drop them. + * For connections selecting only `edges`, use {@link pages} with + * `Stream.flatMap`. + * + * **Example** (Flattening issue numbers across pages) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * + * const RepoIssues = GraphQL.query("RepoIssues", { + * document: "query RepoIssues($owner:String!,$name:String!,$after:String){repository(owner:$owner,name:$name){issues(first:2,after:$after){pageInfo{hasNextPage endCursor}nodes{number}}}}", + * variables: { owner: Schema.String, name: Schema.String, after: Schema.optional(Schema.NullOr(Schema.String)) }, + * result: Schema.Struct({ + * repository: Schema.NullOr(Schema.Struct({ + * issues: Schema.Struct({ + * pageInfo: Schema.Struct({ hasNextPage: Schema.Boolean, endCursor: Schema.NullOr(Schema.String) }), + * nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Schema.Struct({ number: Schema.Int })))) + * }) + * })) + * }) + * }) + * + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => { + * const after = (request.variables as { after?: string }).after + * const page = after === undefined + * ? { pageInfo: { hasNextPage: true, endCursor: "c1" }, nodes: [{ number: 1 }, null] } + * : { pageInfo: { hasNextPage: false, endCursor: null }, nodes: [{ number: 3 }] } + * return Effect.succeed({ body: { data: { repository: { issues: page } } } }) + * }, + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoIssues)) + * const items = GraphQLClient.items(client.RepoIssues, { + * variables: { owner: "Effect-TS", name: "effect" }, + * connection: (result) => result.repository?.issues + * }) + * return yield* Stream.runCollect(Stream.map(items, (issue) => issue?.number ?? null)) + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => [1, null, 3] + * ``` + * + * @stability experimental + * @category paging + * @since 4.0.0 + */ +export const items = ( + method: OperationMethod, + options: { + readonly variables: PagingVariables> + readonly connection: (result: GraphQL.Result) => NodesConnection | null | undefined + readonly options?: { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined + readonly context?: Context.Context | undefined + } | undefined + } +): Stream.Stream, Exclude, R2>> => + Stream.flatMap(pages(method, options), (connection) => Stream.fromArray(connection.nodes ?? [])) + +/** + * The options a {@link makeTest} handler receives: the `headers` and + * `urlParams` the caller passed, unchanged. Headers accept `Headers.Input`; + * unlike real transport requests, test call options are not normalized. + * + * @stability experimental + * @category testing + * @since 4.0.0 + */ +export interface TestCallOptions { + readonly headers?: Headers.Input | undefined + readonly urlParams?: UrlParams.Input | undefined +} + +/** + * A {@link makeTest} handler for one operation. Queries and mutations return + * an `Effect` and subscriptions a `Stream` of the decoded result type. + * + * @stability experimental + * @category testing + * @since 4.0.0 + */ +export type TestHandler = Op["kind"] extends "subscription" ? ( + variables: GraphQL.Variables, + options: TestCallOptions + ) => Stream.Stream, GraphQL.Error> + : ( + variables: GraphQL.Variables, + options: TestCallOptions + ) => Effect.Effect, GraphQL.Error> + +/** + * The handlers given to {@link makeTest}, keyed by operation name. Every + * handler is optional. + * + * @stability experimental + * @category testing + * @since 4.0.0 + */ +export type TestHandlers = { + readonly [Op in Ops as Op["name"]]?: TestHandler +} + +/** + * Builds a client for tests that answers each operation from a handler, + * without a protocol. + * + * **Details** + * + * Handlers receive the caller's `variables` and options unchanged and return + * values in their decoded form. Nothing is encoded, decoded or validated, and + * no middleware runs. Called with `{ partial: true }`, a method returns + * `{ data, errors: [] }`. Subscriptions are not retried. Every method carries + * `.operation`, so {@link pages} and {@link items} work on it. + * + * Calling an operation without a handler dies with an `UnimplementedError` + * defect, so `catchTag` in the code under test cannot swallow a missing stub. + * + * Methods keep the real client's types, including middleware service + * requirements. Tests must provide those services even though mocks do not + * use them. + * + * To test partial data together with `errors`, or the encode and decode path, + * provide a scripted `GraphQLProtocol` layer to {@link make} instead. + * + * **Example** (Replacing a client service in tests) + * + * ```ts import.meta.vitest + * import { Context, Effect, Layer, Schema } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup } from "effect/graphql" + * + * const RepoIssues = GraphQL.query("RepoIssues", { + * document: "query RepoIssues($owner:String!,$name:String!){repository(owner:$owner,name:$name){issues{totalCount}}}", + * variables: { owner: Schema.String, name: Schema.String }, + * result: Schema.Struct({ + * repository: Schema.NullOr(Schema.Struct({ issues: Schema.Struct({ totalCount: Schema.Int }) })) + * }) + * }) + * const GitHubGraphQLGroup = GraphQLGroup.make(RepoIssues) + * + * class GitHub extends Context.Service()("app/GitHub", { + * make: GraphQLClient.make(GitHubGraphQLGroup) + * }) {} + * + * const GitHubTest = Layer.succeed( + * GitHub, + * GraphQLClient.makeTest(GitHubGraphQLGroup)({ + * RepoIssues: ({ name }) => Effect.succeed({ repository: { issues: { totalCount: name.length } } }) + * }) + * ) + * + * const program = GitHub.use((github) => github.RepoIssues({ owner: "Effect-TS", name: "effect" })) + * + * await Effect.runPromise(program.pipe(Effect.provide(GitHubTest))) // => { repository: { issues: { totalCount: 6 } } } + * ``` + * + * @stability experimental + * @category testing + * @since 4.0.0 + */ +export const makeTest = + (group: GraphQLGroup.GraphQLGroup) => + (handlers: TestHandlers): GraphQLClient => { + // Type boundary: the mapped type keys handlers by name, which a lookup by a + // runtime name cannot follow. + const byName = handlers as Record | undefined> + return fromGroup( + group, + (operation) => + makeTestMethod(operation, Object.hasOwn(byName, operation.name) ? byName[operation.name] : undefined) + ) + } + +/** + * Builds a mock layer for a client service using {@link makeTest}. Bind the + * service and group once to expose a `layerTest(handlers)` helper on the service. + * The group fixes the operation types before the handlers are checked. + * + * **Example** (Binding a mock layer to a client service) + * + * ```ts import.meta.vitest + * import { Context, Effect, Schema } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup } from "effect/graphql" + * + * const RepoIssues = GraphQL.query("RepoIssues", { + * document: "query RepoIssues($owner:String!,$name:String!){repository(owner:$owner,name:$name){issues{totalCount}}}", + * variables: { owner: Schema.String, name: Schema.String }, + * result: Schema.Struct({ + * repository: Schema.NullOr(Schema.Struct({ issues: Schema.Struct({ totalCount: Schema.Int }) })) + * }) + * }) + * const GitHubGraphQLGroup = GraphQLGroup.make(RepoIssues) + * + * class GitHub extends Context.Service()("app/GitHub", { + * make: GraphQLClient.make(GitHubGraphQLGroup) + * }) { + * static readonly layerTest = GraphQLClient.layerTest(GitHub, GitHubGraphQLGroup) + * } + * + * const program = GitHub.use((github) => github.RepoIssues({ owner: "Effect-TS", name: "effect" })) + * + * await Effect.runPromise(program.pipe(Effect.provide(GitHub.layerTest({ + * RepoIssues: ({ name }) => Effect.succeed({ repository: { issues: { totalCount: name.length } } }) + * })))) // => { repository: { issues: { totalCount: 6 } } } + * ``` + * + * @see {@link makeTest} for mock behavior and the middleware-services limitation. + * @stability experimental + * @category testing + * @since 4.0.0 + */ +export const layerTest = ( + service: Context.Key, + group: GraphQLGroup.GraphQLGroup & (GraphQLClient extends NoInfer ? unknown : never) +) => +(handlers: TestHandlers): Layer.Layer => + // The group constraint checks that the mock satisfies the service shape. + Layer.succeed(service, makeTest(group)(handlers) as unknown as Service) + +// Handlers are called through these erased signatures; `makeTest` has +// already checked them against the operation. +type ErasedTestHandler = (variables: unknown, options: TestCallOptions) => unknown + +const makeTestMethod = (operation: GraphQL.Any, handler: ErasedTestHandler | undefined) => { + const subscription = operation.kind === "subscription" + if (handler === undefined) { + const unimplemented = () => { + const prevLimit = getStackTraceLimit() + setStackTraceLimit(2) + const error = new Error(`GraphQLClient: Unimplemented operation "${operation.name}"`) + setStackTraceLimit(prevLimit) + error.name = "UnimplementedError" + return subscription ? Stream.die(error) : Effect.die(error) + } + return Object.assign(unimplemented, { operation }) + } + const call = (variables: unknown, options: (TestCallOptions & { readonly partial?: boolean }) | undefined) => { + const run = () => handler(variables === undefined ? {} : variables, options ?? {}) + if (subscription) return Stream.suspend(run as () => Stream.Stream) + const result = Effect.suspend(run as () => Effect.Effect) + return options?.partial === true + ? Effect.map(result, (data): PartialResult => ({ data, errors: [] })) + : result + } + return Object.assign(call, { operation }) +} diff --git a/packages/effect/src/graphql/GraphQLClientError.ts b/packages/effect/src/graphql/GraphQLClientError.ts new file mode 100644 index 000000000..8935736a7 --- /dev/null +++ b/packages/effect/src/graphql/GraphQLClientError.ts @@ -0,0 +1,542 @@ +/** + * Serializable GraphQL client errors with an operation name and a + * `Schema.TaggedError` reason. + * + * @stability experimental + * @since 4.0.0 + */ +import * as Duration from "../Duration.ts" +import type * as Headers from "../http/Headers.ts" +import type * as HttpClientError from "../http/HttpClientError.ts" +import { HeadersSchema } from "../internal/graphqlHeaders.ts" +import { hasProperty } from "../Predicate.ts" +import * as Schema from "../Schema.ts" + +const TypeId = "~effect/graphql/GraphQLClientError" + +/** + * One entry of a GraphQL response's `errors` array, as described by the + * GraphQL specification. + * + * **Details** + * + * `path[0]` is the response key the error belongs to, which is the alias when + * the document used one, so callers can match an error to the field it hit. + * Unknown top-level fields are preserved as JSON values, including GitHub's + * `type` field. + * + * **Example** (Finding which alias an error belongs to) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQLClientError } from "effect/graphql" + * + * const error = Schema.decodeUnknownSync(GraphQLClientError.GraphQLError)({ + * message: "Could not resolve to a Repository", + * path: ["missing", "issues"], + * type: "NOT_FOUND" + * }) + * + * error.path?.[0] // => "missing" + * ``` + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const GraphQLError = Schema.StructWithRest( + Schema.Struct({ + message: Schema.String, + locations: Schema.optional(Schema.Array(Schema.Struct({ line: Schema.Number, column: Schema.Number }))), + path: Schema.optional(Schema.Array(Schema.Union([Schema.String, Schema.Number]))), + extensions: Schema.optional(Schema.Record(Schema.String, Schema.Json)) + }), + [Schema.Record(Schema.String, Schema.Json)] +) + +/** + * The decoded type of {@link GraphQLError}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type GraphQLError = typeof GraphQLError.Type + +/** + * The server answered with a non-empty `errors` array. + * + * **Details** + * + * Queries and mutations fail even when partial `data` is present, since a + * field error's `null` cannot be distinguished from a genuine `null`. Use + * `{ partial: true }` to receive decoded `{ data, errors }` instead. + * + * On this error, `data` is raw. `GraphQLError.path[0]` identifies the response + * key (or alias). Non-2xx HTTP responses with a GraphQL body also use this + * reason rather than `TransportError`. + * + * `status` and `headers` are the HTTP status and response headers, as the + * transport reported them and middleware left them. Headers use + * `Headers.Headers` at runtime and encode as a plain string record. + * Decoding restores the normalized header collection. Both are unset over + * graphql-ws, and `status` is unset for graphql-sse events. `retryAfter` + * parses the `Retry-After` response header as whole seconds or an HTTP date, + * measured from `Date.now()` when the error is constructed. `isRetryable` is + * true for HTTP status `429` or `500`–`599`, and for any response with a + * valid `Retry-After`, whatever its status. It is false otherwise, including + * when there is no status. + * + * For queries and mutations, callers opt into retries with `Effect.retry`, + * and should wait for `retryAfter` when it is set. With + * `GraphQLClient.pages`, retry each page's call, not the stream: + * `Stream.retry` starts again from the first page. Subscriptions retry a + * retryable `ResponseError` on their own, by resubscribing. graphql-sse events + * carry the stream response's headers, so an event with `errors` is retried + * when that response had a valid `Retry-After`, and ends the stream + * otherwise. + * + * **Example** (Inspecting the errors of a failed call) + * + * ```ts import.meta.vitest + * import { GraphQLClientError } from "effect/graphql" + * + * const reason = new GraphQLClientError.ResponseError({ + * errors: [{ message: "Not found", path: ["repository"] }], + * data: { repository: null }, + * status: 200 + * }) + * + * reason.isRetryable // => false + * reason.status // => 200 + * reason.errors.map((error) => error.path?.[0]) // => ["repository"] + * ``` + * + * **Example** (Retrying a call on retryable errors) + * + * ```ts import.meta.vitest + * import { Effect } from "effect" + * import { GraphQLClientError } from "effect/graphql" + * + * const call = Effect.fail(new GraphQLClientError.GraphQLClientError({ + * operation: "Viewer", + * reason: new GraphQLClientError.ResponseError({ + * errors: [{ message: "Query timed out" }], + * status: 502 + * }) + * })) + * + * const retried = Effect.retry(call, { + * while: (error) => error.reason.isRetryable, + * times: 3 + * }) + * ``` + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class ResponseError + extends Schema.TaggedError("effect/graphql/GraphQLClientError/ResponseError")("ResponseError", { + errors: Schema.NonEmptyArray(GraphQLError), + data: Schema.optional(Schema.Json), + extensions: Schema.optional(Schema.Record(Schema.String, Schema.Json)), + status: Schema.optional(Schema.Int), + headers: Schema.optional(HeadersSchema) + }) +{ + /** + * `true` for HTTP status `429` or `500`–`599`, or when the response has a + * `Retry-After` header that parses, at any status, including a graphql-sse + * event whose stream response had one; `false` otherwise, including when + * there is no status and no `Retry-After`. Use this as the `while` predicate + * of `Effect.retry` on the call, and {@link retryAfter} as the delay when it + * is set. Subscriptions resubscribe on it. + * + * @since 4.0.0 + */ + get isRetryable(): boolean { + if (this.retryAfter !== undefined) return true + return this.status !== undefined && isRetryableStatus(this.status) + } + + readonly #retryAfter = parseRetryAfter(this.headers?.["retry-after"], Date.now()) + + /** + * The `Retry-After` response header parsed as whole seconds or an HTTP + * date. An HTTP date is measured from `Date.now()` when the error is + * constructed, so every read returns the same delay. Missing or invalid + * values yield `undefined`. `Effect.retry` does not apply this delay + * automatically. + * + * @since 4.0.0 + */ + get retryAfter(): Duration.Duration | undefined { + return this.#retryAfter + } + + override get message(): string { + return this.errors.map((error) => error.message).join("; ") + } +} + +const retryableStandardCloseCodes: ReadonlySet = new Set([1000, 1001, 1005, 1006, 1012, 1013, 1014]) + +const fatalGraphQLWsCloseCodes: ReadonlySet = new Set([4400, 4401, 4403, 4406, 4409, 4429, 4500]) + +/** + * Tests whether a WebSocket close code is fatal, meaning a `TransportError` + * carrying it is not retryable. + * + * **Details** + * + * Fatal codes are every standard `1xxx` code except `1000`, `1001`, `1005`, + * `1006`, `1012`, `1013` and `1014`, plus the graphql-ws codes `4400` (bad + * request), `4401` (unauthorized), `4403` (forbidden), `4406` (subprotocol + * not acceptable), `4409` (subscriber already exists), `4429` (too many + * initialisation requests) and `4500` (internal server error). Any other code + * is retryable. + * + * `4403` is fatal because reconnecting with the same credentials is refused + * again. + * + * **Example** (Checking close codes) + * + * ```ts import.meta.vitest + * import { GraphQLClientError } from "effect/graphql" + * + * GraphQLClientError.isFatalCloseCode(1006) // => false + * GraphQLClientError.isFatalCloseCode(1011) // => true + * GraphQLClientError.isFatalCloseCode(4401) // => true + * ``` + * + * @stability experimental + * @category predicates + * @since 4.0.0 + */ +export const isFatalCloseCode = (code: number): boolean => + code >= 1000 && code < 2000 ? !retryableStandardCloseCodes.has(code) : fatalGraphQLWsCloseCodes.has(code) + +const isRetryableStatus = (status: number): boolean => status === 429 || (status >= 500 && status < 600) + +/** + * Parses a `Retry-After` header value, either whole seconds or an HTTP date, + * into a `Duration` measured from `now` (epoch milliseconds). Anything else, + * such as `1.5` or `-1`, yields `undefined`. + */ +const parseRetryAfter = (value: string | undefined, now: number): Duration.Duration | undefined => { + if (value === undefined) return undefined + const trimmed = value.trim() + if (/^\d+$/.test(trimmed)) { + return Duration.seconds(Number(trimmed)) + } + // Every HTTP date names a day or month; `Date.parse` alone also accepts + // bare numbers such as `1.5` or `-1`. + if (!/[a-z]/i.test(trimmed)) return undefined + const date = Date.parse(trimmed) + if (Number.isNaN(date)) return undefined + return Duration.millis(Math.max(0, date - now)) +} + +/** + * The client could not get a GraphQL response from the server: a network + * failure, an HTTP response that is not a GraphQL response, or a closed + * WebSocket connection. + * + * **Details** + * + * - `status` and `headers` are only set for an HTTP response that is not a + * GraphQL response. Headers use `Headers.Headers` at runtime and encode + * as a plain string record; decoding restores the header collection. + * - `retryAfter` comes from the `Retry-After` header (whole seconds or an + * HTTP date) and is encoded as milliseconds. When it is set, the error is + * retryable at any status, unless the close code is fatal. + * - `closeCode` is set when a WebSocket connection closed. See + * {@link isFatalCloseCode} for the codes that are not retryable. + * - `fatal: true` marks a failure that retrying cannot fix, such as an + * operation on a graphql-ws transport whose scope has closed. + * - `cause` holds the original error, such as the `HttpClientError`. Once + * encoded it shrinks to `{ name, message }`, the standard `Schema.Defect` + * behaviour. + * + * **Example** (Retryability of transport failures) + * + * ```ts import.meta.vitest + * import { GraphQLClientError } from "effect/graphql" + * + * new GraphQLClientError.TransportError({ description: "ECONNREFUSED" }).isRetryable // => true + * new GraphQLClientError.TransportError({ description: "503", status: 503 }).isRetryable // => true + * new GraphQLClientError.TransportError({ description: "404", status: 404 }).isRetryable // => false + * new GraphQLClientError.TransportError({ description: "closed", closeCode: 4401 }).isRetryable // => false + * new GraphQLClientError.TransportError({ description: "shut down", fatal: true }).isRetryable // => false + * ``` + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class TransportError + extends Schema.TaggedError("effect/graphql/GraphQLClientError/TransportError")("TransportError", { + description: Schema.String, + status: Schema.optional(Schema.Int), + headers: Schema.optional(HeadersSchema), + retryAfter: Schema.optional(Schema.DurationFromMillis), + closeCode: Schema.optional(Schema.Int), + fatal: Schema.optional(Schema.Boolean), + cause: Schema.optional(Schema.Defect()) + }) +{ + /** + * `true` for a network failure (no `status`), a `429` or `500`–`599` + * status, a `retryAfter` at any status, and any close code that is not + * fatal (see {@link isFatalCloseCode}). An error marked `fatal` or with a + * fatal close code is never retryable. When retrying, wait for + * `retryAfter` when it is set. + * + * @since 4.0.0 + */ + get isRetryable(): boolean { + if (this.fatal === true) return false + if (this.closeCode !== undefined && isFatalCloseCode(this.closeCode)) return false + if (this.retryAfter !== undefined) return true + return this.status === undefined || isRetryableStatus(this.status) + } + + override get message(): string { + return this.description + } + + /** + * Converts an `HttpClientError` into a `TransportError`, reading `status`, + * `headers` (as `Headers.Headers`) and `Retry-After` from the response + * when there is one. + * + * **Details** + * + * `Retry-After` is read as whole seconds or an HTTP date; any other value + * is ignored. An HTTP date is measured from `options.now` (epoch + * milliseconds), which defaults to `Date.now()`. Transports pass the Effect + * clock's time when the response arrives. + * + * @since 4.0.0 + */ + static fromHttpClientError( + error: HttpClientError.HttpClientError, + options?: { readonly now?: number | undefined } + ): TransportError { + const response = error.response + return new TransportError({ + description: error.message, + status: response?.status, + headers: response?.headers, + retryAfter: parseRetryAfter(response?.headers["retry-after"], options?.now ?? Date.now()), + cause: error + }) + } +} + +/** + * The variables Schema rejected the input, so nothing was sent. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class EncodeError + extends Schema.TaggedError("effect/graphql/GraphQLClientError/EncodeError")("EncodeError", { + description: Schema.String + }) +{ + /** + * An `EncodeError` is never retryable. + * + * @since 4.0.0 + */ + get isRetryable(): boolean { + return false + } + + override get message(): string { + return this.description + } +} + +/** + * The response was not a GraphQL response, or `data` did not match the result + * Schema. + * + * **Details** + * + * `data: null` without `errors` also produces a `DecodeError`, since + * `ResponseError` requires a non-empty errors array. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DecodeError + extends Schema.TaggedError("effect/graphql/GraphQLClientError/DecodeError")("DecodeError", { + description: Schema.String + }) +{ + /** + * A `DecodeError` is never retryable. + * + * @since 4.0.0 + */ + get isRetryable(): boolean { + return false + } + + override get message(): string { + return this.description + } +} + +/** + * Cursor paging could not continue. Only `GraphQLClient.pages` and + * `GraphQLClient.items` raise it, when a connection becomes `null` on a page + * after the first, or when `hasNextPage` is `true` but `endCursor` is `null` + * or did not advance. + * + * **Details** + * + * `cursor` is the last cursor that was sent, `null` on the first page. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class PaginationError + extends Schema.TaggedError("effect/graphql/GraphQLClientError/PaginationError")("PaginationError", { + description: Schema.String, + cursor: Schema.NullOr(Schema.String) + }) +{ + /** + * A `PaginationError` is never retryable. + * + * @since 4.0.0 + */ + get isRetryable(): boolean { + return false + } + + override get message(): string { + return this.description + } +} + +/** + * The union of every reason a {@link GraphQLClientError} can carry. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type Reason = ResponseError | TransportError | EncodeError | DecodeError | PaginationError + +const httpReason = (reason: Reason): ResponseError | TransportError | undefined => + reason._tag === "ResponseError" || reason._tag === "TransportError" ? reason : undefined + +/** + * The error every `GraphQLClient` method fails with. It names the operation + * and carries one of the five reasons; `cause` is the reason itself. + * + * **Example** (Retrying only what is retryable) + * + * ```ts import.meta.vitest + * import { Duration, Schema } from "effect" + * import { GraphQLClientError } from "effect/graphql" + * + * const error = new GraphQLClientError.GraphQLClientError({ + * operation: "Viewer", + * reason: new GraphQLClientError.TransportError({ + * description: "503 Service Unavailable", + * status: 503, + * retryAfter: Duration.seconds(30) + * }) + * }) + * + * error.isRetryable // => true + * error.retryAfter // => Duration.seconds(30) + * + * // Errors round-trip through their Schema + * const encoded = Schema.encodeSync(GraphQLClientError.GraphQLClientError)(error) + * Schema.decodeUnknownSync(GraphQLClientError.GraphQLClientError)(encoded).reason._tag // => "TransportError" + * ``` + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class GraphQLClientError extends Schema.TaggedError(TypeId)("GraphQLClientError", { + operation: Schema.String, + reason: Schema.Union([ResponseError, TransportError, EncodeError, DecodeError, PaginationError]) +}) { + /** + * Marks this value as a GraphQL client error for runtime guards. + * + * @stability experimental + * @since 4.0.0 + */ + readonly [TypeId] = TypeId + + /** + * The reason, exposed as the error's cause. + * + * @since 4.0.0 + */ + override readonly cause = this.reason + + /** + * Delegates to the reason's `isRetryable`. + * + * @since 4.0.0 + */ + get isRetryable(): boolean { + return this.reason.isRetryable + } + + /** + * The `Retry-After` hint of a `ResponseError` or `TransportError` reason, + * if any. Use it as the delay when retrying on {@link isRetryable}. + * + * @since 4.0.0 + */ + get retryAfter(): Duration.Duration | undefined { + return httpReason(this.reason)?.retryAfter + } + + /** + * The HTTP status of a `ResponseError` or `TransportError` reason, if any. + * + * @since 4.0.0 + */ + get status(): number | undefined { + return httpReason(this.reason)?.status + } + + /** + * The response headers of a `ResponseError` or `TransportError` reason, if + * any. + * + * @since 4.0.0 + */ + get headers(): Headers.Headers | undefined { + return httpReason(this.reason)?.headers + } + + override get message(): string { + return `${this.operation}: ${this.reason._tag}: ${this.reason.message}` + } +} + +/** + * Tests whether a value is a {@link GraphQLClientError}. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isGraphQLClientError = (u: unknown): u is GraphQLClientError => hasProperty(u, TypeId) diff --git a/packages/effect/src/graphql/GraphQLGroup.ts b/packages/effect/src/graphql/GraphQLGroup.ts new file mode 100644 index 000000000..009724cd3 --- /dev/null +++ b/packages/effect/src/graphql/GraphQLGroup.ts @@ -0,0 +1,167 @@ +/** + * Groups of GraphQL operations, merged to build a client. Group middleware + * runs outside operation middleware. `@effect/graphql-generator` emits one + * group per document file. + * + * @stability experimental + * @since 4.0.0 + */ +import { type Pipeable, pipeArguments } from "../Pipeable.ts" +import { hasProperty } from "../Predicate.ts" +import type * as GraphQL from "./GraphQL.ts" +import type * as GraphQLMiddleware from "./GraphQLMiddleware.ts" + +const TypeId = "~effect/graphql/GraphQLGroup" + +/** + * A set of operations plus the middleware attached at the group level. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface GraphQLGroup extends Pipeable { + readonly [TypeId]: typeof TypeId + readonly operations: ReadonlyArray + /** Middleware attached to the group, outermost first. */ + readonly middlewares: ReadonlyArray + /** + * Attaches a middleware to every operation in the group. Group middleware + * runs outside operation middleware; several group middleware run in the + * order attached. + */ + middleware(middleware: M): GraphQLGroup> +} + +/** + * Any group, with its operations erased. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Any = GraphQLGroup + +/** + * Extracts the union of operations in a group. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Operations = G extends GraphQLGroup ? Ops : never + +/** + * Tests whether a value is a {@link GraphQLGroup}. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isGraphQLGroup = (u: unknown): u is Any => hasProperty(u, TypeId) + +const Proto = { + [TypeId]: TypeId, + pipe() { + return pipeArguments(this, arguments) + }, + middleware(this: Any, middleware: GraphQLMiddleware.AnyService) { + return makeProto(this.operations, [...this.middlewares, middleware]) + } +} + +// The operation and middleware types of a group are phantom: at runtime a +// group holds erased `GraphQL.Any` operations and `AnyService` tags, so the +// typed constructors below state their group type with one assertion each. +const makeProto = ( + operations: ReadonlyArray, + middlewares: ReadonlyArray +): Any => { + const byName = new Map() + for (const operation of operations) { + const existing = byName.get(operation.name) + if (existing !== undefined && existing !== operation) { + throw new Error(`GraphQLGroup: duplicate operation name "${operation.name}"`) + } + byName.set(operation.name, operation) + } + return Object.assign(Object.create(Proto), { operations: Array.from(byName.values()), middlewares }) +} + +/** + * Builds a group from operations. Operation names must be unique within a + * group, because they become the client's method names. + * + * **Example** (One group per `.graphql` file) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL, GraphQLGroup } from "effect/graphql" + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * const ViewerGraphQLGroup = GraphQLGroup.make(Viewer) + * + * ViewerGraphQLGroup.operations.map((op) => op.name) // => ["Viewer"] + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const make = >(...operations: Ops): GraphQLGroup => + makeProto(operations, []) as GraphQLGroup + +/** + * Merges groups into one. Middleware attached to a source group stays + * attached to that group's operations; middleware attached to the merged + * group afterwards runs outside all of it. + * + * **Example** (Auth on one file, logging on everything) + * + * ```ts import.meta.vitest + * import { Schema } from "effect" + * import { GraphQL, GraphQLGroup, GraphQLMiddleware } from "effect/graphql" + * + * class Auth extends GraphQLMiddleware.Service()("app/Auth") {} + * class Logging extends GraphQLMiddleware.Service()("app/Logging") {} + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * const Rate = GraphQL.query("Rate", { + * document: "query Rate{rateLimit{remaining}}", + * result: Schema.Struct({ rateLimit: Schema.Struct({ remaining: Schema.Int }) }) + * }) + * + * const all = GraphQLGroup.merge(GraphQLGroup.make(Viewer).middleware(Auth), GraphQLGroup.make(Rate)) + * .middleware(Logging) + * + * all.operations.map((op) => op.name) // => ["Viewer", "Rate"] + * all.middlewares.map((m) => m.key) // => ["app/Logging"] + * all.operations[0].middlewares.map((m) => m.key) // => ["app/Auth"] + * ``` + * + * @stability experimental + * @category combinators + * @since 4.0.0 + */ +export const merge = >( + ...groups: Groups +): GraphQLGroup> => + makeProto( + groups.flatMap((group) => + group.middlewares.length === 0 + ? group.operations + : group.operations.map((operation): GraphQL.Any => + Object.assign(Object.create(Object.getPrototypeOf(operation)), operation, { + middlewares: [...group.middlewares, ...operation.middlewares] + }) + ) + ), + [] + ) as GraphQLGroup> diff --git a/packages/effect/src/graphql/GraphQLMiddleware.ts b/packages/effect/src/graphql/GraphQLMiddleware.ts new file mode 100644 index 000000000..88d37f487 --- /dev/null +++ b/packages/effect/src/graphql/GraphQLMiddleware.ts @@ -0,0 +1,253 @@ +/** + * GraphQL-level middleware provided as `Context.Service` implementations. + * Middleware can rewrite requests and inspect raw results, including response + * extensions and HTTP headers. Provide implementations with `Layer.succeed` + * or `Layer.effect`. + * + * @stability experimental + * @since 4.0.0 + */ +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Stream from "../Stream.ts" +import type * as Types from "../Types.ts" +import type * as GraphQL from "./GraphQL.ts" +import type { GraphQLClientError } from "./GraphQLClientError.ts" +import type { ExecutionResult, GraphQLRequest } from "./GraphQLProtocol.ts" + +const TypeId = "~effect/graphql/GraphQLMiddleware" + +/** + * Middleware handlers for queries, mutations and subscriptions. Both + * `execute` and `subscribe` are required. + * + * **Details** + * + * `execute` runs once per query or mutation. `subscribe` runs once per + * subscription attempt (the client re-runs the whole chain when it retries a + * subscription) and can map or tap the event stream. + * + * Both receive the operation being run, the request so far, and `next`, which + * runs the rest of the chain with the given request and returns the raw + * `ExecutionResult` (or, for `subscribe`, the stream of them). Its `status` + * and `headers` are the HTTP status and response headers the transport + * reported, if any; a middleware may rewrite them, and a failed call's + * `ResponseError` carries the final values. Request and response headers + * are `Headers.Headers` values; use `Headers.set` to rewrite them. + * + * `next` fails with `GraphQLClientError`, or with the error of a middleware + * further in, which is typed as `Types.unhandled`, as in `RpcMiddleware`. + * Narrow it, for example with `Effect.catchTag("GraphQLClientError", ...)`, + * before reading `reason`. + * + * Use {@link mapRequest} when the middleware only needs to transform the + * request. + * + * **Example** (Reading a rate-limit header) + * + * ```ts import.meta.vitest + * import { Effect } from "effect" + * import { GraphQLMiddleware } from "effect/graphql" + * import { Headers } from "effect/http" + * + * const rateLimit: GraphQLMiddleware.Implementation = { + * execute: ({ next, request }) => + * Effect.tap(next(request), (result) => + * Effect.log(result.headers === undefined ? undefined : Headers.get(result.headers, "x-ratelimit-remaining")) + * ), + * subscribe: ({ next, request }) => next(request) + * } + * ``` + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Implementation { + readonly execute: (options: { + readonly operation: GraphQL.Any + readonly request: GraphQLRequest + readonly next: (request: GraphQLRequest) => Effect.Effect + }) => Effect.Effect + readonly subscribe: (options: { + readonly operation: GraphQL.Any + readonly request: GraphQLRequest + readonly next: (request: GraphQLRequest) => Stream.Stream + }) => Stream.Stream +} + +/** + * The class returned by {@link Service}: a `Context.Service` for an + * {@link Implementation}, carrying the declared `error` and `requires` types. + * + * @stability experimental + * @category services + * @since 4.0.0 + */ +export interface ServiceClass extends Context.Service> { + new(_: never): Context.ServiceClass.Shape> & { + readonly [TypeId]: { readonly error: E; readonly requires: R } + } + readonly [TypeId]: { readonly error: E; readonly requires: R } + readonly key: Key +} + +/** + * Any middleware service class, with its error and requirement types erased. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export interface AnyService extends Context.Service> { + readonly [TypeId]: { readonly error: any; readonly requires: any } +} + +/** + * Extracts the service identifier of a middleware, which is what + * `GraphQLClient.make` requires. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Identifier = M extends Context.Service ? I : never + +/** + * Extracts the error type a middleware declared. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Error = M extends { readonly [TypeId]: { readonly error: infer E } } ? E : never + +/** + * Extracts the services a middleware declared it requires per call. + * + * @stability experimental + * @category utility types + * @since 4.0.0 + */ +export type Requires = M extends { readonly [TypeId]: { readonly requires: infer R } } ? R : never + +/** + * Declares a middleware tag. `requires` are services the implementation needs + * on every call, and `error` is what it can fail with; both show up on the + * requirements and error type of every client method the middleware is + * attached to. + * + * **Example** (An auth middleware that needs the current user) + * + * ```ts import.meta.vitest + * import { Context, Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLMiddleware, GraphQLProtocol } from "effect/graphql" + * import { Headers } from "effect/http" + * + * class CurrentUser extends Context.Service()("CurrentUser") {} + * class TokenExpired extends Schema.TaggedError()("TokenExpired", {}) {} + * + * class Auth extends GraphQLMiddleware.Service()("app/Auth") {} + * + * const AuthLive = Layer.succeed( + * Auth, + * GraphQLMiddleware.mapRequest(Effect.fnUntraced(function*(request) { + * const user = yield* CurrentUser + * if (user.token === "") return yield* new TokenExpired() + * return { ...request, headers: Headers.set(request.headers, "authorization", `Bearer ${user.token}`) } + * })) + * ) + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => Effect.succeed({ body: { data: { viewer: { login: request.headers.authorization } } } }), + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer).middleware(Auth)) + * // CurrentUser is required by the method because Auth is attached + * return yield* client.Viewer().pipe(Effect.provideService(CurrentUser, { token: "abc" })) + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide([Scripted, AuthLive]))) // => { viewer: { login: "Bearer abc" } } + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const Service = < + Self, + Config extends { + readonly requires?: any + readonly error?: any + } = {} +>(): (key: Key) => ServiceClass< + Self, + Key, + Config extends { readonly error: infer E } ? E : never, + Config extends { readonly requires: infer R } ? R : never +> => +(key: string) => { + function ServiceClass() {} + Object.setPrototypeOf(ServiceClass, Object.getPrototypeOf(Context.Service(key))) + return Object.assign(ServiceClass, { key, [TypeId]: TypeId }) as any +} + +/** + * Builds an {@link Implementation} that transforms requests for queries, + * mutations and subscriptions. Use it to set headers, auth or extensions. + * + * **Example** (Adding a header to every request) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLMiddleware, GraphQLProtocol } from "effect/graphql" + * import { Headers } from "effect/http" + * + * class RequestId extends GraphQLMiddleware.Service()("app/RequestId") {} + * + * const RequestIdLive = Layer.succeed( + * RequestId, + * GraphQLMiddleware.mapRequest((request) => + * Effect.succeed({ ...request, headers: Headers.set(request.headers, "x-request-id", "abc") }) + * ) + * ) + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => + * Effect.succeed({ body: { data: { viewer: { login: request.headers["x-request-id"] } } } }), + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer).middleware(RequestId)) + * return yield* client.Viewer() + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide([Scripted, RequestIdLive]))) // => { viewer: { login: "abc" } } + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const mapRequest = ( + f: (request: GraphQLRequest, operation: GraphQL.Any) => Effect.Effect +): Implementation => ({ + execute: ({ next, operation, request }) => Effect.flatMap(f(request, operation), next), + subscribe: ({ next, operation, request }) => Stream.unwrap(Effect.map(f(request, operation), next)) +}) diff --git a/packages/effect/src/graphql/GraphQLProtocol.ts b/packages/effect/src/graphql/GraphQLProtocol.ts new file mode 100644 index 000000000..2985d62db --- /dev/null +++ b/packages/effect/src/graphql/GraphQLProtocol.ts @@ -0,0 +1,1072 @@ +/** + * Transports for `GraphQLClient`. They accept encoded requests and return raw + * response bodies with available HTTP status and headers, without schema + * decoding or middleware. + * + * - {@link layerHttp} uses HTTP POST (or GET for queries) and graphql-sse + * subscriptions, with an optional graphql-ws subscription transport. + * - {@link layerWebSocket} multiplexes all operations over graphql-ws. + * + * {@link makeHttp} and {@link makeWebSocket} construct the same transports as effects. + * + * @stability experimental + * @since 4.0.0 + */ +import * as Cause from "../Cause.ts" +import * as Clock from "../Clock.ts" +import * as Context from "../Context.ts" +import * as Deferred from "../Deferred.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Sse from "../encoding/Sse.ts" +import * as Fiber from "../Fiber.ts" +import { constant } from "../Function.ts" +import * as Headers from "../http/Headers.ts" +import * as HttpClient from "../http/HttpClient.ts" +import * as HttpClientError from "../http/HttpClientError.ts" +import * as HttpClientRequest from "../http/HttpClientRequest.ts" +import * as HttpClientResponse from "../http/HttpClientResponse.ts" +import * as UrlParams from "../http/UrlParams.ts" +import * as internalGraphQL from "../internal/graphql.ts" +import { HeadersSchema } from "../internal/graphqlHeaders.ts" +import * as Layer from "../Layer.ts" +import * as Queue from "../Queue.ts" +import * as Result from "../Result.ts" +import * as Schema from "../Schema.ts" +import type * as Scope from "../Scope.ts" +import * as Socket from "../socket/Socket.ts" +import * as Stream from "../Stream.ts" +import type { Kind } from "./GraphQL.ts" +import { GraphQLError, TransportError } from "./GraphQLClientError.ts" + +/** + * The request a transport sends. Middleware sees and may rewrite it before it + * reaches the transport. + * + * **Details** + * + * `kind` is the operation type, which the HTTP transport uses to send queries + * as `GET` when `queryMethod: "GET"` is set. `variables` are already encoded + * through the operation's variables Schema. + * + * `headers` and `urlParams` are applied by the HTTP transport, to queries, + * mutations and graphql-sse subscriptions alike. A graphql-ws transport + * ignores both, because graphql-ws has no per-operation headers or URL. + * Request headers are normalized, immutable `Headers.Headers` values. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface GraphQLRequest { + readonly kind: Kind + readonly query: string + readonly operationName: string + readonly variables: unknown + readonly extensions?: Record | undefined + readonly headers: Headers.Headers + readonly urlParams: UrlParams.UrlParams +} + +/** + * A raw GraphQL result before schema decoding. Middleware receive it from + * `next` and can read response `extensions`, such as rate-limit costs. + * + * **Details** + * + * `status` and `headers` are transport metadata, not part of the GraphQL + * response body: the client decodes the body without them and then copies + * what the transport reported. Middleware may read or rewrite them, and a + * failed call's `ResponseError` carries the final values. Headers use + * `Headers.Headers` at runtime and encode as a plain string record. + * + * @stability experimental + * @category schemas + * @since 4.0.0 + */ +export const ExecutionResult = Schema.Struct({ + data: Schema.optional(Schema.Unknown), + errors: Schema.optional(Schema.Array(GraphQLError)), + extensions: Schema.optional(Schema.Record(Schema.String, Schema.Json)), + status: Schema.optional(Schema.Int), + headers: Schema.optional(HeadersSchema) +}) + +/** + * The decoded type of {@link ExecutionResult}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type ExecutionResult = typeof ExecutionResult.Type + +/** + * The transport service. `execute` answers a query or mutation with the raw + * JSON body, and `subscribe` turns a subscription into a stream of raw events. + * Both fail only with `TransportError`; GraphQL `errors` are left in the body + * for the client to interpret. + * + * **Details** + * + * Each answer is `{ body, status?, headers? }`. The HTTP transport sets + * `headers` on every answer, including graphql-sse events, and `status` on + * all but graphql-sse events. Headers are `Headers.Headers` values. + * graphql-ws leaves both unset. + * + * **Example** (A scripted transport for tests) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: () => Effect.succeed({ body: { data: { viewer: { login: "tim" } } } }), + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer)) + * return yield* client.Viewer() + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => { viewer: { login: "tim" } } + * ``` + * + * @stability experimental + * @category services + * @since 4.0.0 + */ +export class GraphQLProtocol extends Context.Service Effect.Effect<{ + readonly body: unknown + readonly status?: number | undefined + readonly headers?: Headers.Headers | undefined + }, TransportError> + readonly subscribe: (request: GraphQLRequest) => Stream.Stream<{ + readonly body: unknown + readonly status?: number | undefined + readonly headers?: Headers.Headers | undefined + }, TransportError> + /** + * Resolves the transport URL for resolver batching. Set by `layerHttp`; + * optional for custom transports. + */ + readonly url?: + | ((options: { readonly kind: Kind; readonly operationName: string }) => Effect.Effect) + | undefined +}>()("effect/graphql/GraphQLProtocol") {} + +/** + * The URL of an HTTP transport: fixed, resolved for each operation from the + * calling context, `kind` and `operationName`, or an `Effect` with no errors + * or requirements. See {@link layerHttp}. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type Url = + | string + | Effect.Effect + | ((options: { + readonly context: Context.Context + readonly kind: Kind + readonly operationName: string + }) => string) + +const graphqlResponseContentType = "application/graphql-response+json" +const jsonContentType = "application/json" +const eventStreamContentType = "text/event-stream" + +const isObject = (u: unknown): u is Record => typeof u === "object" && u !== null && !Array.isArray(u) + +// A gateway's `{ "errors": "Throttled" }` is not a GraphQL response, so its +// status and Retry-After still apply. +const isGraphQLBody = (body: unknown): boolean => + isObject(body) && ("data" in body || "errors" in body) && + (!("errors" in body) || Array.isArray(body.errors)) && + (!("data" in body) || body.data === null || isObject(body.data)) + +// The JSON body of a POST and the payload of a graphql-ws `subscribe`. +// `extensions` is dropped from the JSON when unset. +const requestBody = (request: GraphQLRequest) => ({ + query: request.query, + operationName: request.operationName, + variables: request.variables, + extensions: request.extensions +}) + +const postRequest = (url: string, request: GraphQLRequest, accept: string) => + HttpClientRequest.post(url).pipe( + HttpClientRequest.setHeaders(request.headers), + HttpClientRequest.setHeader("accept", accept), + HttpClientRequest.appendUrlParams(request.urlParams), + HttpClientRequest.bodyJsonUnsafe(requestBody(request)) + ) + +const graphqlGetParams = new Set(["query", "operationName", "variables", "extensions"]) + +// Remove all protocol params from the configured URL and caller params before +// adding the request's values, including when optional fields are unset. Split +// the URL without resolving it so relative URLs remain supported. +const getRequest = (url: string, request: GraphQLRequest, accept: string) => { + const hashIndex = url.indexOf("#") + const hash = hashIndex === -1 ? "" : url.slice(hashIndex) + const withoutHash = hashIndex === -1 ? url : url.slice(0, hashIndex) + const queryIndex = withoutHash.indexOf("?") + const baseUrl = queryIndex === -1 ? withoutHash : withoutHash.slice(0, queryIndex) + const urlParams = [ + ...UrlParams.fromInput(new URLSearchParams(queryIndex === -1 ? "" : withoutHash.slice(queryIndex + 1))), + ...request.urlParams + ].filter(([key]) => !graphqlGetParams.has(key)) + const params: Record = { query: request.query, operationName: request.operationName } + if (request.variables !== undefined) params.variables = JSON.stringify(request.variables) + if (request.extensions !== undefined) params.extensions = JSON.stringify(request.extensions) + return HttpClientRequest.get(baseUrl + hash).pipe( + HttpClientRequest.setHeaders(request.headers), + HttpClientRequest.setHeader("accept", accept), + HttpClientRequest.appendUrlParams(urlParams), + HttpClientRequest.setUrlParams(params) + ) +} + +// Reads the clock when the error arrives, so an HTTP-date `Retry-After` is +// measured from when the response was received. +const fromHttpClientError = + (clock: Clock.Clock) => + (effect: Effect.Effect): Effect.Effect => + Effect.catch(effect, (error) => + Effect.fail( + HttpClientError.isHttpClientError(error) + ? TransportError.fromHttpClientError(error, { now: clock.currentTimeMillisUnsafe() }) + : error + )) + +// Media types are case-insensitive. +const contentTypeOf = (response: HttpClientResponse.HttpClientResponse): string => + (response.headers["content-type"] ?? "").toLowerCase() + +const readResponse = ( + response: HttpClientResponse.HttpClientResponse +): Effect.Effect => { + const contentType = contentTypeOf(response) + // The GraphQL-over-HTTP media type is authoritative, whatever the status. + if (contentType.includes(graphqlResponseContentType)) { + return response.json + } + // Legacy servers answer with application/json; only a body with the + // GraphQL keys counts as a GraphQL response. + if (contentType.includes(jsonContentType)) { + return Effect.flatMap(response.json, (body) => + isGraphQLBody(body) + ? Effect.succeed(body) + : Effect.as(HttpClientResponse.filterStatusOk(response), body)) + } + return Effect.flatMap( + HttpClientResponse.filterStatusOk(response), + () => + Effect.fail( + new TransportError({ + description: `Expected a GraphQL response, received ${ + contentType === "" ? "no Content-Type" : `Content-Type ${contentType}` + }`, + status: response.status, + headers: response.headers + }) + ) + ) +} + +const readAnswer = (response: HttpClientResponse.HttpClientResponse) => + Effect.map(readResponse(response), (body) => ({ body, status: response.status, headers: response.headers })) + +const errorMessage = (cause: unknown): string => cause instanceof Error ? cause.message : String(cause) + +// The graphql-sse distinct-mode events of a `text/event-stream` body. The +// stream ends on a `complete` event, with or without a `data:` line. A body +// that ends without one means the connection was lost, so it fails with a +// retryable `TransportError`. A `retry:` line is kept and reported as +// `retryAfter` if the body fails or ends early. +const sseEvents = ( + response: HttpClientResponse.HttpClientResponse +): Stream.Stream<{ readonly body: unknown; readonly headers: Headers.Headers }, TransportError> => + Stream.suspend(() => { + const headers = response.headers + let retryAfter: Duration.Duration | undefined + let complete = false + let events: Array = [] + const parser = Sse.makeParser((event) => { + if (event._tag === "Retry") { + retryAfter = event.duration + } else { + events.push(event) + } + }, { dispatchEmptyNamedEvents: true }) + return response.stream.pipe( + Stream.decodeText(), + Stream.mapError((cause) => + new TransportError({ description: `The graphql-sse stream failed: ${cause.message}`, retryAfter, cause }) + ), + Stream.mapEffect((text) => { + const error = parser.feed(text) + if (error !== undefined) { + return Effect.fail(new TransportError({ description: error.message, cause: error })) + } + const batch = events + events = [] + return Effect.succeed(batch) + }), + Stream.flattenIterable, + Stream.takeWhile((event) => { + if (event.event === "complete") complete = true + return !complete + }), + Stream.filter((event) => event.event === "next"), + Stream.mapEffect((event) => + Effect.try({ + try: () => ({ body: JSON.parse(event.data) as unknown, headers }), + catch: (cause) => + new TransportError({ description: `Invalid graphql-sse next event: ${errorMessage(cause)}`, cause }) + }) + ), + Stream.concat(Stream.suspend(() => + complete ? Stream.empty : Stream.fail( + new TransportError({ description: "The graphql-sse stream ended without a complete event", retryAfter }) + ) + )) + ) + }) + +interface HttpOptions { + readonly url: Url + readonly queryMethod?: "GET" | "POST" | undefined + readonly subscriptions?: { + readonly webSocket: WebSocketOptions + } | undefined +} + +const makeHttpWith = (options: HttpOptions): Effect.Effect< + GraphQLProtocol["Service"], + never, + HttpClient.HttpClient | Socket.WebSocketConstructor | Scope.Scope | R +> => { + const resolve = urlResolver(options.url) + // A resolver batch carries the URL it was grouped by. + const urlOf = (request: GraphQLRequest) => + internalGraphQL.ResolvedUrl.use((resolved) => + resolved?.resolve === resolve ? Effect.succeed(resolved.url) : resolve(request) + ) + const http = Clock.clockWith((clock) => + HttpClient.HttpClient.useSync((client) => { + const execute = (request: GraphQLRequest) => + Effect.flatMap(Effect.zip(Effect.service(internalGraphQL.ForcePost), urlOf(request)), ([forcePost, url]) => { + const toRequest = options.queryMethod === "GET" && request.kind === "query" && !forcePost + ? getRequest + : postRequest + return client.execute( + toRequest(url, request, `${graphqlResponseContentType}, ${jsonContentType}`) + ).pipe( + Effect.flatMap(readAnswer), + fromHttpClientError(clock) + ) + }) + const subscribe = (request: GraphQLRequest) => + Stream.unwrap(Effect.flatMap(urlOf(request), (url) => + client.execute(postRequest(url, request, eventStreamContentType)).pipe( + Effect.flatMap((response) => { + const contentType = contentTypeOf(response) + if (contentType.includes(eventStreamContentType) && response.status >= 200 && response.status < 300) { + return Effect.succeed(sseEvents(response)) + } + // A server that rejects the subscription answers with a single + // GraphQL response, which the client reads like a query's. + return Effect.map(readAnswer(response), Stream.succeed) + }), + fromHttpClientError(clock) + ))) + return GraphQLProtocol.of({ execute, subscribe, url: resolve }) + }) + ) + const webSocket = options.subscriptions?.webSocket + return webSocket === undefined + ? http + : Effect.zipWith( + http, + makeWebSocket(webSocket), + (http, ws) => GraphQLProtocol.of({ execute: http.execute, subscribe: ws.subscribe, url: http.url }) + ) +} + +const urlResolver = (url: Url): NonNullable => { + if (typeof url === "string") return constant(Effect.succeed(url)) + if (Effect.isEffect(url)) return constant(url) + return ({ kind, operationName }) => + Effect.contextWith((context: Context.Context) => Effect.succeed(url({ context, kind, operationName }))) +} + +/** + * Builds the HTTP transport as an effect, for composing transports by hand. + * Most code uses {@link layerHttp}. + * + * **Details** + * + * Takes the same options as {@link layerHttp}, including `queryMethod`. + * Without `subscriptions`, subscriptions use graphql-sse distinct mode on + * `url`. With `subscriptions.webSocket`, `url` must be a string, subscriptions + * go over graphql-ws instead, and the effect also builds the graphql-ws + * transport, so it needs a `Socket.WebSocketConstructor` and a `Scope`. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const makeHttp: { + (options: { + readonly url: string + readonly queryMethod?: "GET" | "POST" | undefined + readonly subscriptions: { + readonly webSocket: { + readonly url: string + readonly headers?: Headers.Input | undefined + readonly connectionParams?: Effect.Effect | undefined, unknown, R> | undefined + readonly connectionAckTimeout?: Duration.Input | undefined + readonly keepAlive?: Duration.Input | false | undefined + readonly idleTimeout?: Duration.Input | undefined + } + } + }): Effect.Effect< + GraphQLProtocol["Service"], + never, + HttpClient.HttpClient | Socket.WebSocketConstructor | Scope.Scope | R + > + (options: { + readonly url: Url + readonly queryMethod?: "GET" | "POST" | undefined + readonly subscriptions?: undefined + }): Effect.Effect< + GraphQLProtocol["Service"], + never, + HttpClient.HttpClient + > +} = makeHttpWith + +/** + * The HTTP transport: queries and mutations are sent as `POST` with a JSON + * body to `url`, using the `HttpClient` in context. Subscriptions use + * graphql-sse distinct mode on the same URL, or graphql-ws when + * `subscriptions.webSocket` is set. + * + * **Details** + * + * With `queryMethod: "GET"` (default `"POST"`), queries are sent as `GET` + * with no body: `query` and `operationName` become URL params, plus + * `variables` and `extensions` as JSON when set. Mutations are always + * `POST`, and so are `GraphQLResolver.aliased` batches. A long query is not + * retried as `POST`. + * + * A request's `headers` and `urlParams`, from the call options or from + * middleware, are applied to every HTTP request. A query string already on + * `url` is kept. On `GET`, the request's `urlParams` come before the GraphQL + * params, which replace any caller param with the same name. + * + * Every response's HTTP `status` is passed to the client as + * `ExecutionResult.status`, and a call that fails with GraphQL `errors` + * reports it as `ResponseError.status`. Response headers are passed the + * same way, as `ExecutionResult.headers` and `ResponseError.headers`. + * + * A response is a GraphQL response when its `Content-Type` is + * `application/graphql-response+json`, whatever the status, or when it is + * `application/json` and the body has a list of `errors` or a `data` object + * (or `null`). Media types match case-insensitively. Anything else + * is a `TransportError` with the response `status` and `headers`, and a + * `Retry-After` header (whole seconds or an HTTP date, measured from when the + * response arrived) becomes `retryAfter`. + * + * A subscription is a `POST` with `Accept: text/event-stream`. Each `next` + * event is emitted with the response headers but no status, and the stream + * ends on a `complete` event, with or without a `data:` line. A body that + * fails, or ends without a `complete` event, is a retryable `TransportError` + * with a server `retry:` line as its `retryAfter`, so the client's + * `subscriptionRetry` resubscribes. A non-stream answer, such as a `400` with + * `errors`, is read like a query's response. Interrupting the stream aborts + * the request. + * + * With `subscriptions: { webSocket }`, subscriptions go over graphql-ws as in + * {@link layerWebSocket}, and the layer needs a `Socket.WebSocketConstructor`. + * + * To route each query, mutation and graphql-sse subscription, `url` can be a + * function of the calling fiber's context (including the call's `context` + * option), `kind` and `operationName`, or an `Effect` with no errors + * or requirements that runs in that context. Use a defaulted + * `Context.Reference` to select the server. Reading a missing service with + * `Context.get` is a defect, not a `TransportError`. Neither form can be + * combined with `subscriptions.webSocket`: the socket URL is fixed at + * connection time. + * + * **Gotchas** + * + * `GraphQLResolver` resolves each lookup's URL once, before middleware run and + * before the batched request exists, then sends each batch to the URL it was + * grouped by. A single call resolves it in the transport, after middleware. + * So the URL must depend only on the context and the operation, and + * middleware must not change `kind` or `operationName` or provide services + * the URL reads. + * + * `RequestResolver.withCache` and `RequestResolver.persisted` key on the lookup, + * not the URL, so results can leak across servers. For caching, use a separate + * client and resolver per server, for example with `LayerMap`. + * + * **Example** (Building a client over HTTP) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * import { HttpClient, HttpClientResponse } from "effect/http" + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * // A stand-in for a real HttpClient layer such as FetchHttpClient.layer + * const FakeServer = Layer.succeed( + * HttpClient.HttpClient, + * HttpClient.make((request) => + * Effect.succeed(HttpClientResponse.fromWeb( + * request, + * new Response(JSON.stringify({ data: { viewer: { login: "tim" } } }), { + * headers: { "content-type": "application/graphql-response+json" } + * }) + * )) + * ) + * ) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer)) + * return yield* client.Viewer() + * }).pipe( + * Effect.provide(GraphQLProtocol.layerHttp({ url: "https://api.github.com/graphql" })), + * Effect.provide(FakeServer) + * ) + * + * await Effect.runPromise(program) // => { viewer: { login: "tim" } } + * ``` + * + * **Example** (Queries over GET with a per-call URL param) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * import { HttpClient, HttpClientResponse } from "effect/http" + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * // A stand-in server that answers with the method and the `preview` param + * const FakeServer = Layer.succeed( + * HttpClient.HttpClient, + * HttpClient.make((request, url) => + * Effect.succeed(HttpClientResponse.fromWeb( + * request, + * new Response( + * JSON.stringify({ data: { viewer: { login: `${request.method} ${url.searchParams.get("preview")}` } } }), + * { headers: { "content-type": "application/graphql-response+json" } } + * ) + * )) + * ) + * ) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer)) + * return yield* client.Viewer(undefined, { urlParams: { preview: "draft-token" } }) + * }).pipe( + * Effect.provide(GraphQLProtocol.layerHttp({ url: "https://cms.example.com/graphql", queryMethod: "GET" })), + * Effect.provide(FakeServer) + * ) + * + * await Effect.runPromise(program) // => { viewer: { login: "GET draft-token" } } + * ``` + * + * **Example** (One client for github.com, GHE.com and GitHub Enterprise Server) + * + * ```ts import.meta.vitest + * import { Context, Effect, Layer, Schema } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol } from "effect/graphql" + * import { HttpClient, HttpClientResponse } from "effect/http" + * + * const Viewer = GraphQL.query("Viewer", { + * document: "query Viewer{viewer{login}}", + * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + * }) + * + * const GitHubHost = Context.Reference("GitHubHost", { defaultValue: () => "github.com" }) + * + * const Protocol = GraphQLProtocol.layerHttp({ + * url: ({ context }) => { + * const host = Context.get(context, GitHubHost) + * if (host === "github.com") return "https://api.github.com/graphql" + * if (host.endsWith(".ghe.com")) return `https://api.${host}/graphql` + * return `https://${host}/api/graphql` + * } + * }) + * + * // Echo the request URL instead of calling GitHub. + * const FakeServer = Layer.succeed( + * HttpClient.HttpClient, + * HttpClient.make((request, url) => + * Effect.succeed(HttpClientResponse.fromWeb( + * request, + * new Response(JSON.stringify({ data: { viewer: { login: url.href } } }), { + * headers: { "content-type": "application/graphql-response+json" } + * }) + * )) + * ) + * ) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer)) + * const dotcom = yield* client.Viewer() + * const cloud = yield* client.Viewer().pipe(Effect.provideService(GitHubHost, "acme.ghe.com")) + * const server = yield* client.Viewer(undefined, { context: Context.make(GitHubHost, "github.example.com") }) + * return [dotcom, cloud, server].map((result) => result.viewer.login) + * }).pipe( + * Effect.provide(Protocol), + * Effect.provide(FakeServer) + * ) + * + * await Effect.runPromise(program) // => ["https://api.github.com/graphql", "https://api.acme.ghe.com/graphql", "https://github.example.com/api/graphql"] + * ``` + * + * **Example** (Subscriptions over graphql-ws, everything else over POST) + * + * ```ts import.meta.vitest + * import { Effect } from "effect" + * import { GraphQLProtocol } from "effect/graphql" + * + * const token = Effect.succeed("secret") + * + * // Provide an HttpClient and a Socket.WebSocketConstructor, such as + * // FetchHttpClient.layer and NodeSocket.layerWebSocketConstructor + * export const Protocol = GraphQLProtocol.layerHttp({ + * url: "https://example.com/graphql", + * subscriptions: { + * webSocket: { + * url: "wss://example.com/graphql", + * connectionParams: Effect.map(token, (token) => ({ authorization: `Bearer ${token}` })) + * } + * } + * }) + * ``` + * + * @stability experimental + * @category layers + * @since 4.0.0 + */ +export const layerHttp: { + (options: { + readonly url: string + readonly queryMethod?: "GET" | "POST" | undefined + readonly subscriptions: { + readonly webSocket: { + readonly url: string + readonly headers?: Headers.Input | undefined + readonly connectionParams?: Effect.Effect | undefined, unknown, R> | undefined + readonly connectionAckTimeout?: Duration.Input | undefined + readonly keepAlive?: Duration.Input | false | undefined + readonly idleTimeout?: Duration.Input | undefined + } + } + }): Layer.Layer + (options: { + readonly url: Url + readonly queryMethod?: "GET" | "POST" | undefined + readonly subscriptions?: undefined + }): Layer.Layer< + GraphQLProtocol, + never, + HttpClient.HttpClient + > +} = ( + options: HttpOptions +): Layer.Layer => + Layer.effect(GraphQLProtocol, makeHttpWith(options)) + +interface WebSocketOptions { + readonly url: string + readonly headers?: Headers.Input | undefined + readonly connectionParams?: Effect.Effect | undefined, unknown, R> | undefined + readonly connectionAckTimeout?: Duration.Input | undefined + readonly keepAlive?: Duration.Input | false | undefined + readonly idleTimeout?: Duration.Input | undefined +} + +interface WsMessage { + readonly type: string + readonly id?: string | undefined + readonly payload?: unknown +} + +// One WebSocket connection. `error` is set once it is lost, and the operations +// attached to it fail with it. +interface Connection { + readonly ack: Deferred.Deferred + error: TransportError | undefined + receivedFrame: boolean + fiber: Fiber.Fiber | undefined +} + +interface Operation { + readonly queue: Queue.Queue<{ readonly body: unknown }, TransportError | Cause.Done> + readonly connection: Connection +} + +const textDecoder = new TextDecoder() + +// As in graphql-ws's `validateMessage`, `error` carries at least one entry with +// a `message`. +const isFormattedErrors = (u: unknown): boolean => + Array.isArray(u) && u.length > 0 && u.every((error) => isObject(error) && "message" in error) + +const connectionError = (cause: Cause.Cause): TransportError => { + const found = Cause.findError(cause) + if (Result.isFailure(found)) { + return new TransportError({ description: "The graphql-ws connection was closed" }) + } + const error = found.success + if (error instanceof TransportError) { + return error + } + if (Socket.isSocketError(error) && error.reason._tag === "SocketCloseError") { + return new TransportError({ + description: `The graphql-ws connection closed with ${error.reason.message}`, + closeCode: error.reason.code, + cause: error + }) + } + return new TransportError({ description: `The graphql-ws connection failed: ${errorMessage(error)}`, cause: error }) +} + +/** + * Builds the graphql-ws transport as an effect, for composing transports by + * hand. Most code uses {@link layerWebSocket}, which describes the options. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const makeWebSocket = ( + options: { + readonly url: string + readonly headers?: Headers.Input | undefined + readonly connectionParams?: Effect.Effect | undefined, unknown, R> | undefined + readonly connectionAckTimeout?: Duration.Input | undefined + readonly keepAlive?: Duration.Input | false | undefined + readonly idleTimeout?: Duration.Input | undefined + } +): Effect.Effect => + Effect.gen(function*() { + const socket = yield* Socket.makeWebSocket(options.url, { + protocols: "graphql-transport-ws", + headers: options.headers === undefined ? undefined : Headers.fromInput(options.headers) + }) + const writer = yield* socket.writer + const scope = yield* Effect.scope + const context = yield* Effect.context() + const connectionParams = options.connectionParams === undefined + ? Effect.succeed(undefined) + : options.connectionParams.pipe( + Effect.provideContext(context), + Effect.mapError((cause) => + new TransportError({ description: `graphql-ws connectionParams failed: ${errorMessage(cause)}`, cause }) + ) + ) + const ackTimeout = options.connectionAckTimeout ?? Duration.seconds(10) + const keepAlive = options.keepAlive ?? Duration.seconds(10) + const idleTimeout = Duration.fromInputUnsafe(options.idleTimeout ?? 0) + + const operations = new Map() + let connection: Connection | undefined + let nextId = 0 + let active = 0 + let idleGeneration = 0 + + const send = (message: WsMessage) => + writer.write(JSON.stringify(message)).pipe( + Effect.mapError((cause) => connectionError(Cause.fail(cause))) + ) + + const lose = (conn: Connection, error: TransportError) => { + if (conn.error !== undefined) return + conn.error = error + if (connection === conn) connection = undefined + Deferred.doneUnsafe(conn.ack, Effect.fail(error)) + for (const [id, operation] of operations) { + if (operation.connection !== conn) continue + operations.delete(id) + Queue.failCauseUnsafe(operation.queue, Cause.fail(error)) + } + } + + // An invalid server message closes the connection with 4400, which is + // fatal for every operation on it. + const invalid = (reason: string, cause?: unknown) => + Effect.andThen( + Effect.ignore(writer.write(new Socket.CloseEvent(4400, "Invalid message"))), + Effect.fail( + new TransportError({ description: `Invalid graphql-ws message: ${reason}`, closeCode: 4400, cause }) + ) + ) + + const handle = (conn: Connection, frame: string | Uint8Array): Effect.Effect => { + // Any frame answers a keep-alive ping; an invalid one drops the connection anyway. + conn.receivedFrame = true + let message: WsMessage + try { + message = JSON.parse(typeof frame === "string" ? frame : textDecoder.decode(frame)) + } catch (cause) { + return invalid(errorMessage(cause), cause) + } + if (!isObject(message)) { + return invalid("expected an object") + } + switch (message.type) { + case "connection_ack": + case "ping": + case "pong": { + if (message.payload != null && !isObject(message.payload)) { + return invalid(`${message.type} with a payload that is not an object`) + } + if (message.type === "connection_ack") { + Deferred.doneUnsafe(conn.ack, Effect.void) + } + return message.type === "ping" ? send({ type: "pong" }) : Effect.void + } + case "next": + case "error": + case "complete": { + const id = message.id + if (typeof id !== "string" || id === "") { + return invalid(`${message.type} without an id`) + } + if (message.type === "next" && !isObject(message.payload)) { + return invalid("next with a payload that is not an object") + } + if (message.type === "error" && !isFormattedErrors(message.payload)) { + return invalid("error with a payload that is not a list of GraphQL errors") + } + const operation = operations.get(id) + // Unknown ids, including operations this client already completed, are dropped. + if (operation === undefined || operation.connection !== conn) return Effect.void + if (message.type === "next") { + Queue.offerUnsafe(operation.queue, { body: message.payload }) + return Effect.void + } + operations.delete(id) + if (message.type === "error") { + // The server rejected the operation; the client fails it with a ResponseError. + Queue.offerUnsafe(operation.queue, { body: { errors: message.payload } }) + } + Queue.endUnsafe(operation.queue) + return Effect.void + } + default: { + return invalid(`unknown type ${JSON.stringify(message.type)}`) + } + } + } + + const pinger = Effect.fnUntraced(function*(conn: Connection) { + if (keepAlive === false) return yield* Effect.never + while (true) { + yield* Effect.sleep(keepAlive) + if (!conn.receivedFrame) { + return yield* Effect.fail( + new TransportError({ description: "The graphql-ws server did not answer the keep-alive ping" }) + ) + } + conn.receivedFrame = false + yield* send({ type: "ping" }) + } + }) + + const run = Effect.fnUntraced( + function*(conn: Connection) { + const { pull } = yield* socket.reader + const init = Effect.flatMap(connectionParams, (payload) => send({ type: "connection_init", payload })) + const read = Effect.forever( + Effect.flatMap(pull, (frames) => Effect.forEach(frames, (frame) => handle(conn, frame), { discard: true })) + ) + const acknowledged = Deferred.await(conn.ack).pipe( + Effect.timeoutOrElse({ + duration: ackTimeout, + orElse: () => + Effect.fail(new TransportError({ description: "The graphql-ws server did not send connection_ack" })) + }) + ) + // Reading starts with `connectionParams`, so a close while it runs still + // fails the waiting operations. The ack timeout starts once + // `connection_init` is sent. + yield* Effect.all([read, init.pipe(Effect.andThen(acknowledged), Effect.andThen(pinger(conn)))], { + concurrency: 2, + discard: true + }) + }, + // Runs before the socket closes, so no write waits on a dead socket. + (effect, conn) => Effect.onError(effect, (cause) => Effect.sync(() => lose(conn, connectionError(cause)))), + (effect) => Effect.ignore(Effect.scoped(effect)) + ) + + const close = (conn: Connection) => { + lose(conn, new TransportError({ description: "The graphql-ws connection was closed" })) + return conn.fiber === undefined ? Effect.void : Fiber.interrupt(conn.fiber) + } + + const acquireConnection = Effect.suspend(() => { + if (connection !== undefined) return Effect.succeed(connection) + const conn: Connection = { + ack: Deferred.makeUnsafe(), + error: undefined, + receivedFrame: true, + fiber: undefined + } + connection = conn + return Effect.forkIn(run(conn), scope).pipe( + Effect.map((fiber) => { + conn.fiber = fiber + return conn + }) + ) + }) + + const register = Effect.suspend(() => { + // A connection forked into a closed scope is interrupted before it + // starts, so new operations fail instead of waiting for an ack. + if (scope.state._tag === "Closed") { + return Effect.fail( + new TransportError({ description: "The graphql-ws transport's scope is closed", fatal: true }) + ) + } + active++ + idleGeneration++ + return Effect.map(acquireConnection, (conn) => [String(++nextId), conn] as const) + }) + + const closeIfIdle = Effect.suspend(() => { + if (--active > 0 || connection === undefined) return Effect.void + const conn = connection + if (Duration.isZero(idleTimeout)) return close(conn) + const generation = ++idleGeneration + const closeIfStillIdle = Effect.suspend(() => + generation === idleGeneration && connection === conn ? close(conn) : Effect.void + ) + return Effect.asVoid(Effect.forkIn(Effect.delay(closeIfStillIdle, idleTimeout), scope)) + }) + + const release = (id: string, conn: Connection) => + Effect.suspend(() => + Effect.andThen( + // Only a subscribed operation the server has not completed is still in `operations`. + operations.delete(id) && conn.error === undefined + ? Effect.ignore(send({ id, type: "complete" })) + : Effect.void, + closeIfIdle + ) + ) + + const operation = (request: GraphQLRequest): Stream.Stream<{ readonly body: unknown }, TransportError> => + Stream.unwrap(Effect.gen(function*() { + const [id, conn] = yield* Effect.acquireRelease(register, ([id, conn]) => release(id, conn)) + yield* Deferred.await(conn.ack) + if (conn.error !== undefined) { + return yield* Effect.fail(conn.error) + } + const queue = yield* Queue.unbounded<{ readonly body: unknown }, TransportError | Cause.Done>() + operations.set(id, { queue, connection: conn }) + // Per-call `request.headers` and `request.urlParams` are ignored: + // graphql-ws has no per-operation headers or URL. + yield* send({ id, type: "subscribe", payload: requestBody(request) }) + return Stream.fromQueue(queue) + })) + + const execute = (request: GraphQLRequest): Effect.Effect<{ readonly body: unknown }, TransportError> => + Effect.flatMap(Stream.runHead(operation(request)), (result) => + result._tag === "Some" + ? Effect.succeed(result.value) + : Effect.fail(new TransportError({ description: "The graphql-ws operation completed without a result" }))) + + return GraphQLProtocol.of({ execute, subscribe: operation }) + }) + +/** + * The graphql-ws transport: every operation goes over one WebSocket using the + * `graphql-transport-ws` subprotocol, with the `Socket.WebSocketConstructor` + * in context. + * + * **Details** + * + * The socket connects lazily and multiplexes operations by id. It closes after + * `idleTimeout` (default 0) with no active operations, or with code `1000` + * when the layer's scope closes. + * + * - Operations wait for `connection_ack`. `connectionAckTimeout` (default + * 10 seconds) starts after sending `connection_init`. + * - `connectionParams` runs on every connection, allowing token refresh. + * Failure or a socket close during evaluation is a `TransportError`. + * - Handshake `headers` accept `Headers.Input` and are normalized before + * reaching the socket. They require a Node or Bun WebSocket constructor; + * browsers cannot set them. Per-operation `headers` and `urlParams` are ignored. + * - Results carry no HTTP `status`. + * - `keepAlive` (default 10 seconds, or `false` to disable) sends `ping`. + * No server frame in the following interval causes a retryable failure. + * Server `ping` receives `pong`. + * - Lost connections fail every active operation with a `TransportError` + * carrying the close code. See `GraphQLClientError.isFatalCloseCode` for + * non-retryable codes. The transport never replays operations; the client + * retries subscriptions and the next operation reconnects. + * - Once the layer's scope has closed, every new operation fails at once + * with a non-retryable `TransportError` marked `fatal`. + * - Messages follow graphql-ws validation. Invalid messages close the socket + * with fatal code `4400`; valid messages for unknown ids are ignored. + * - Server `error` messages become `ResponseError` values. Interrupting a + * subscription sends `complete`. + * + * **Example** (A client over graphql-ws) + * + * ```ts import.meta.vitest + * import { Effect, Layer } from "effect" + * import { GraphQLProtocol } from "effect/graphql" + * import type { Socket } from "effect/socket" + * + * // Provide a Socket.WebSocketConstructor, such as + * // Socket.layerWebSocketConstructorGlobal in a browser + * export const Protocol: Layer.Layer = + * GraphQLProtocol.layerWebSocket({ + * url: "wss://example.com/graphql", + * connectionParams: Effect.succeed({ authorization: "Bearer secret" }), + * idleTimeout: "30 seconds" + * }) + * ``` + * + * @stability experimental + * @category layers + * @since 4.0.0 + */ +export const layerWebSocket = ( + options: { + readonly url: string + readonly headers?: Headers.Input | undefined + readonly connectionParams?: Effect.Effect | undefined, unknown, R> | undefined + readonly connectionAckTimeout?: Duration.Input | undefined + readonly keepAlive?: Duration.Input | false | undefined + readonly idleTimeout?: Duration.Input | undefined + } +): Layer.Layer => + Layer.effect(GraphQLProtocol, makeWebSocket(options)) diff --git a/packages/effect/src/graphql/GraphQLResolver.ts b/packages/effect/src/graphql/GraphQLResolver.ts new file mode 100644 index 000000000..07699bc95 --- /dev/null +++ b/packages/effect/src/graphql/GraphQLResolver.ts @@ -0,0 +1,728 @@ +/** + * Batch lookups through a GraphQL client method with `RequestResolver`. + * {@link ordered} matches results by position; {@link findById} matches by + * key; {@link aliased} repeats an operation under aliases in one request. + * {@link request} turns any of them into `(key) => Effect`. + * + * Client middleware still run. When a method needs services, such as a + * middleware's `requires`, `batchKey` keeps lookups with different services + * in separate batches. Compose `RequestResolver.setDelay` and + * `RequestResolver.batchN` with `.pipe` to control batching. + * + * Lookups with different protocol URLs are batched separately. See + * `GraphQLProtocol.layerHttp` for middleware and caching constraints. + * + * @stability experimental + * @since 4.0.0 + */ +import * as Arr from "../Array.ts" +import * as Cause from "../Cause.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Exit from "../Exit.ts" +import { dual } from "../Function.ts" +import * as Headers from "../http/Headers.ts" +import * as UrlParams from "../http/UrlParams.ts" +import * as internalGraphQL from "../internal/graphql.ts" +import * as MutableHashMap from "../MutableHashMap.ts" +import * as Request from "../Request.ts" +import * as RequestResolver from "../RequestResolver.ts" +import type * as Types from "../Types.ts" +import type * as GraphQL from "./GraphQL.ts" +import type { OperationMethod, PartialResult } from "./GraphQLClient.ts" +import { DecodeError, type GraphQLClientError, type GraphQLError } from "./GraphQLClientError.ts" +import type { ExecutionResult, GraphQLRequest } from "./GraphQLProtocol.ts" + +// ----------------------------------------------------------------------------- +// Types +// ----------------------------------------------------------------------------- + +type NonNull = Exclude + +type Step = K extends keyof NonNull ? NonNull[K] : never + +// Check one segment at a time for autocomplete without enumerating all paths. +// The path must end at a list and cannot cross one. +type CheckPath = NonNull extends ReadonlyArray ? readonly [] + : P extends readonly [infer Head, ...infer Tail] + ? readonly [Head extends keyof NonNull ? Head : keyof NonNull, ...CheckPath, Tail>] + : readonly [keyof NonNull] + +type Path = P extends CheckPath ? P : CheckPath + +type AtPath = P extends readonly [infer Head, ...infer Tail] ? AtPath, Tail> : A + +type Item = NonNull> extends ReadonlyArray ? NonNull : never + +type Typenames = Types.IsUnion extends true ? + I extends { readonly __typename: infer T extends string } ? T : never + : never + +type Narrow = [T] extends [never] ? I : Extract + +type BatchKey = [GraphQL.Services] extends [never] ? { readonly batchKey?: never } + : { readonly batchKey: (context: Context.Context>) => unknown } + +interface KeyRequest extends Request.Request { + readonly key: K +} + +// Requests are structural, so two requests with equal keys are one request. +const makeRequest = (key: K): KeyRequest => Request.of>()({ key }) + +// ----------------------------------------------------------------------------- +// Running +// ----------------------------------------------------------------------------- + +/** + * Looks up a key with an {@link ordered}, {@link findById} or {@link aliased} + * resolver, using `request(resolver)(key)` or `request(key, resolver)`. + * + * **Details** + * + * Concurrent lookups are batched. Keys equal under `Equal` and `Hash` + * are sent once per batch and share the result. + * + * **Example** (Looking up repositories by id) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol, GraphQLResolver } from "effect/graphql" + * + * const RepoNodes = GraphQL.query("RepoNodes", { + * document: "query RepoNodes($ids:[ID!]!){nodes(ids:$ids){... on Repository{name}}}", + * variables: { ids: Schema.Array(Schema.String) }, + * result: Schema.Struct({ nodes: Schema.Array(Schema.NullOr(Schema.Struct({ name: Schema.String }))) }) + * }) + * + * let calls = 0 + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => { + * calls++ + * const { ids } = request.variables as { ids: Array } + * return Effect.succeed({ body: { data: { nodes: ids.map((id) => ({ name: `repo-${id}` })) } } }) + * }, + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes)) + * const getRepo = GraphQLResolver.request(GraphQLResolver.ordered(client.RepoNodes, { + * variables: (ids: ReadonlyArray) => ({ ids }), + * path: ["nodes"] + * })) + * const repos = yield* Effect.forEach(["1", "2", "1"], getRepo, { concurrency: "unbounded" }) + * return [repos.map((repo) => repo.name), calls] + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => [["repo-1", "repo-2", "repo-1"], 1] + * ``` + * + * @stability experimental + * @category running + * @since 4.0.0 + */ +export const request: { + ( + resolver: RequestResolver.RequestResolver> + ): (key: K) => Effect.Effect + (key: K, resolver: RequestResolver.RequestResolver>): Effect.Effect +} = dual( + 2, + (key: K, resolver: RequestResolver.RequestResolver>): Effect.Effect => + Effect.request(makeRequest(key), resolver) +) + +// ----------------------------------------------------------------------------- +// Resolvers +// ----------------------------------------------------------------------------- + +/** + * Batches lookups for fields such as `nodes(ids:)` that return one entry per + * key in input order. + * + * **Details** + * + * `variables` maps batch keys to operation variables; annotate its parameter + * to set the key type. `path` selects a list through object fields, with + * autocomplete at each level. For `__typename` unions, `typename` narrows + * the success type and treats other types as missing. + * + * Null entries, typename mismatches and null parents fail with + * `Cause.NoSuchElementError`. GraphQL errors whose paths start with + * `[...path, i]` fail only entry `i`; its `ResponseError` contains those errors + * and the response status. Other GraphQL errors fail the batch with the same + * `ResponseError` as a non-partial call. Transport, encode and decode errors + * also fail the batch. A list length mismatch fails with `DecodeError`. + * + * `pageInfo.hasNextPage: true` on the list's parent fails the batch with + * `DecodeError` to avoid reporting unreturned entries as missing. Set the + * operation's page size at least as large as `RequestResolver.batchN`. + * + * A batch uses one lookup's services. Methods requiring services also require + * `batchKey`; only lookups with keys equal under `Equal` share a batch. Return + * the value that distinguishes those services, for example + * `(context) => Context.get(context, CurrentUser).tenantId`. Methods without + * services use one batch and do not accept `batchKey`. + * + * **Gotchas** + * + * Don't return the whole context from `batchKey`. It also holds the caller's + * span, so most lookups would get a batch of their own. + * + * A middleware that reads a `Context.Reference` with a default value doesn't + * add it to the method's services. `batchKey` isn't required for it, and + * lookups with different values can share a batch. + * + * **Example** (Looking up repositories in a `Node` union) + * + * ```ts import.meta.vitest + * import { Cause, Effect, Layer, RequestResolver, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol, GraphQLResolver } from "effect/graphql" + * + * const Repository = Schema.Struct({ __typename: Schema.Literal("Repository"), nameWithOwner: Schema.String }) + * const User = Schema.Struct({ __typename: Schema.Literal("User"), login: Schema.String }) + * + * const RepoNodes = GraphQL.query("RepoNodes", { + * document: "query RepoNodes($ids:[ID!]!){nodes(ids:$ids){__typename ...on Repository{nameWithOwner} ...on User{login}}}", + * variables: { ids: Schema.Array(Schema.String) }, + * result: Schema.Struct({ nodes: Schema.Array(Schema.NullOr(Schema.Union([Repository, User]))) }) + * }) + * + * const store: Record = { + * R_1: { __typename: "Repository", nameWithOwner: "Effect-TS/effect" }, + * U_1: { __typename: "User", login: "tim" } + * } + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => { + * const { ids } = request.variables as { ids: Array } + * return Effect.succeed({ body: { data: { nodes: ids.map((id) => store[id] ?? null) } } }) + * }, + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes)) + * const RepoById = GraphQLResolver.ordered(client.RepoNodes, { + * variables: (ids: ReadonlyArray) => ({ ids }), + * path: ["nodes"], + * typename: "Repository" + * }).pipe(RequestResolver.batchN(100)) + * const getRepo = GraphQLResolver.request(RepoById) + * return yield* Effect.forEach( + * ["R_1", "U_1", "R_2"], + * (id) => getRepo(id).pipe( + * Effect.map((repo) => repo.nameWithOwner), + * Effect.catchIf(Cause.isNoSuchElementError, () => Effect.succeed("missing")) + * ), + * { concurrency: "unbounded" } + * ) + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => ["Effect-TS/effect", "missing", "missing"] + * ``` + * + * @stability experimental + * @category resolvers + * @since 4.0.0 + */ +export const ordered = < + Op extends GraphQL.Any, + K, + const P extends ReadonlyArray, + T extends Typenames, P>> = never +>( + method: OperationMethod, + options: { + readonly variables: (keys: ReadonlyArray) => GraphQL.Variables + readonly path: Path, P> + readonly typename?: T | undefined + } & BatchKey +): RequestResolver.RequestResolver< + KeyRequest< + K, + Narrow, P>, T>, + GraphQL.Error | Cause.NoSuchElementError, + GraphQL.Services + > +> => { + const path = options.path as ReadonlyArray + const codec = internalGraphQL.makeCodec(method.operation) + return makeResolver( + method, + options.batchKey, + (entries) => + Effect.flatMap(callBatch(method, entries, options.variables), ({ keys, targets, result }) => { + const byIndex = new Map>() + for (const error of result.errors) { + const index = entryIndex(error, path, keys.length) + if (index === undefined) return batchError(codec, result) + const errors = byIndex.get(index) + if (errors === undefined) byIndex.set(index, [error]) + else errors.push(error) + } + const list = findList(result.data, path) + // An error in a non-null item nulls the whole list, so every entry lost its data to it. + if (list === undefined) return result.errors.length > 0 ? batchError(codec, result) : completeAll(targets) + if (list.truncated) return Effect.fail(codec.fail(truncated(path))) + if (list.items.length !== keys.length) { + return Effect.fail(codec.fail( + new DecodeError({ + description: `Expected ${keys.length} entries at ${path.join(".")}, received ${list.items.length}` + }) + )) + } + const response = internalGraphQL.partialResponses.get(result) + for (let i = 0; i < targets.length; i++) { + const errors = byIndex.get(i) + const item = list.items[i] + targets[i].completeUnsafe( + errors !== undefined + ? Exit.fail(codec.fail( + internalGraphQL.responseError({ status: response?.status, headers: response?.headers }, errors) + )) + : matchesTypename(item, options.typename) + ? Exit.succeed(item) + : constNoSuchElement + ) + } + return Effect.void + }) + ) +} + +/** + * Batches lookups by key for searches and collections that return results + * in any order. + * + * **Details** + * + * Write `key` before `variables` to infer the key type from its return type. + * `variables` maps batch keys to operation variables. Keys are compared with + * `Equal` and `Hash`. `path` and `typename` work as in {@link ordered}; + * `key` receives the narrowed item type. + * + * Missing keys, null parents, pagination and `batchKey` behave as in + * {@link ordered}. + * Any GraphQL error fails the batch with the same `ResponseError` as a + * non-partial call, since error paths cannot be matched to keys. + * + * **Example** (Finding repositories through search) + * + * ```ts import.meta.vitest + * import { Effect, Layer, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol, GraphQLResolver } from "effect/graphql" + * + * const Repository = Schema.Struct({ __typename: Schema.Literal("Repository"), nameWithOwner: Schema.String }) + * const User = Schema.Struct({ __typename: Schema.Literal("User"), login: Schema.String }) + * + * const SearchRepos = GraphQL.query("SearchRepos", { + * document: "query SearchRepos($query:String!){search(query:$query,type:REPOSITORY,first:100){pageInfo{hasNextPage} nodes{__typename ...on Repository{nameWithOwner} ...on User{login}}}}", + * variables: { query: Schema.String }, + * result: Schema.Struct({ + * search: Schema.Struct({ + * pageInfo: Schema.Struct({ hasNextPage: Schema.Boolean }), + * nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Schema.Union([Repository, User])))) + * }) + * }) + * }) + * + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => { + * const { query } = request.variables as { query: string } + * const nodes = query.split(" ").map((term) => ({ __typename: "Repository", nameWithOwner: term.slice(5) })) + * return Effect.succeed({ body: { data: { search: { pageInfo: { hasNextPage: false }, nodes: nodes.reverse() } } } }) + * }, + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const client = yield* GraphQLClient.make(GraphQLGroup.make(SearchRepos)) + * const RepoByName = GraphQLResolver.findById(client.SearchRepos, { + * key: (repo) => repo.nameWithOwner, + * variables: (names) => ({ query: names.map((name) => `repo:${name}`).join(" ") }), + * path: ["search", "nodes"], + * typename: "Repository" + * }) + * const repos = yield* Effect.forEach( + * ["Effect-TS/effect", "Effect-TS/website"], + * GraphQLResolver.request(RepoByName), + * { concurrency: "unbounded" } + * ) + * return repos.map((repo) => repo.nameWithOwner) + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => ["Effect-TS/effect", "Effect-TS/website"] + * ``` + * + * @stability experimental + * @category resolvers + * @since 4.0.0 + */ +export const findById = < + Op extends GraphQL.Any, + K, + const P extends ReadonlyArray, + T extends Typenames, P>> = never +>( + method: OperationMethod, + options: { + readonly variables: (keys: ReadonlyArray) => GraphQL.Variables + readonly path: Path, P> + readonly key: (item: Narrow, P>, T>) => K + readonly typename?: T | undefined + } & BatchKey +): RequestResolver.RequestResolver< + KeyRequest< + K, + Narrow, P>, T>, + GraphQL.Error | Cause.NoSuchElementError, + GraphQL.Services + > +> => { + const path = options.path as ReadonlyArray + const codec = internalGraphQL.makeCodec(method.operation) + return makeResolver( + method, + options.batchKey, + (entries) => + Effect.flatMap(callBatch(method, entries, options.variables), ({ keys, targets, result }) => { + if (result.errors.length > 0) return batchError(codec, result) + const list = findList(result.data, path) + if (list === undefined) return completeAll(targets) + if (list.truncated) return Effect.fail(codec.fail(truncated(path))) + const pending = MutableHashMap.empty>() + for (let i = 0; i < keys.length; i++) MutableHashMap.set(pending, keys[i], targets[i]) + for (const item of list.items) { + if (!matchesTypename(item, options.typename)) continue + const key = options.key(item as Narrow, P>, T>) + const target = MutableHashMap.get(pending, key) + if (target._tag === "None") continue + MutableHashMap.remove(pending, key) + target.value.completeUnsafe(Exit.succeed(item)) + } + MutableHashMap.forEach(pending, (target) => target.completeUnsafe(constNoSuchElement)) + return Effect.void + }) + ) +} + +/** + * Batches calls to a generated query or mutation by repeating the operation + * under aliases in one request. + * + * **When to use** + * + * Use when the API has no list-key field, such as GitHub's pull requests by + * repository and number. Otherwise, {@link ordered} or {@link findById} send + * a smaller query. + * + * **Details** + * + * Use {@link request} to get `(variables) => Effect`. Variables that encode + * to the same JSON share a slice; each slice uses the operation's result Schema. + * Throws when the resolver is built if the operation has no document + * template, if an operation directive references a variable, or if a root + * selection is a fragment spread or inline fragment, since none of these can + * be repeated under aliases. + * + * Batches use `POST` even with `queryMethod: "GET"`. The default batch size + * is 10; change it with `RequestResolver.batchN`. Middleware run once per + * batch, and `batchKey` works as in {@link ordered}. Mock and custom methods + * are called once per slice instead. + * + * Errors with a matching `path[0]` fail only that slice, with the alias prefix + * removed from the error path. Errors without a matching path, transport errors + * and middleware failures fail the batch. + * + * **Gotchas** + * + * An error under a non-null root field can null the entire response, failing + * every slice. GitHub's `repository` and `pullRequest` are nullable. + * + * Middleware that hash queries must use `request.query`, not + * `operation.document`. Trusted-document allowlists don't support batching; + * query hashing does, since the same operation and slice count produce the + * same document. + * + * Mutations are allowed, but the caller cannot control root-field order. + * + * **Example** (Looking up pull requests by number) + * + * ```ts import.meta.vitest + * import { Effect, Layer, RequestResolver, Schema, Stream } from "effect" + * import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLProtocol, GraphQLResolver } from "effect/graphql" + * + * // Generated from: + * // query PullRequest($owner: String!, $name: String!, $number: Int!) { + * // repository(owner: $owner, name: $name) { pullRequest(number: $number) { title } } + * // } + * const PullRequest = GraphQL.query("PullRequest", { + * document: { + * variables: [ + * { name: "owner", body: "String!" }, + * { name: "name", body: "String!" }, + * { name: "number", body: "Int!" } + * ], + * selections: [{ + * name: "repository", + * body: ["(owner:$", "owner", ",name:$", "name", "){pullRequest(number:$", "number", "){title}}"] + * }], + * fragments: [] + * }, + * variables: { owner: Schema.String, name: Schema.String, number: Schema.Int }, + * result: Schema.Struct({ + * repository: Schema.NullOr(Schema.Struct({ + * pullRequest: Schema.NullOr(Schema.Struct({ title: Schema.String })) + * })) + * }) + * }) + * + * // A stand-in server that answers every aliased slice + * const queries: Array = [] + * const Scripted = Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + * execute: (request) => { + * queries.push(request.query) + * const data: Record = {} + * for (const [key, number] of Object.entries(request.variables as Record)) { + * const prefix = /^(_\d+_)number$/.exec(key)?.[1] + * if (prefix !== undefined) data[`${prefix}repository`] = { pullRequest: { title: `PR ${number}` } } + * } + * return Effect.succeed({ body: { data } }) + * }, + * subscribe: () => Stream.empty + * }) + * + * const program = Effect.gen(function*() { + * const github = yield* GraphQLClient.make(GraphQLGroup.make(PullRequest)) + * const PullRequests = GraphQLResolver.aliased(github.PullRequest).pipe(RequestResolver.batchN(20)) + * const getPullRequest = GraphQLResolver.request(PullRequests) + * const results = yield* Effect.forEach( + * [1, 2, 3], + * (number) => getPullRequest({ owner: "Effect-TS", name: "effect", number }), + * { concurrency: "unbounded" } + * ) + * return [results.map((result) => result.repository?.pullRequest?.title), queries.length] + * }) + * + * await Effect.runPromise(program.pipe(Effect.provide(Scripted))) // => [["PR 1", "PR 2", "PR 3"], 1] + * ``` + * + * @stability experimental + * @category resolvers + * @since 4.0.0 + */ +export const aliased = ( + method: OperationMethod, + ...[options]: {} extends BatchKey ? [options?: BatchKey] : [options: BatchKey] +): RequestResolver.RequestResolver> => { + const operation = method.operation + const template = operation.template + if (template === undefined) { + throw new Error(`GraphQLResolver.aliased: operation ${operation.name} has no document template`) + } + const unsliceable = internalGraphQL.templateUnsliceableReason(template) + if (unsliceable !== undefined) { + throw new Error(`GraphQLResolver.aliased: cannot batch operation ${operation.name}: ${unsliceable}`) + } + const codec = internalGraphQL.makeCodec(operation) + // The registered hook uses this operation's middleware errors and services. + const execute = internalGraphQL.rawExecutors.get(method)?.execute as + | ((request: GraphQLRequest) => Effect.Effect, GraphQL.Services>) + | undefined + // Generic conditional parameters prevent calling the strict overload directly. + const call = method as unknown as ( + variables: GraphQL.Variables + ) => Effect.Effect, GraphQL.Error, GraphQL.Services> + + return makeResolver>(method, options?.batchKey, (entries) => + Effect.flatMap( + Effect.forEach(entries, (entry) => Effect.exit(codec.encodeVariables(entry.request.key))), + (encoded) => { + const slices: Array> = [] + const byJson = new Map>() + for (let i = 0; i < entries.length; i++) { + const exit = encoded[i] + if (exit._tag === "Failure") { + entries[i].completeUnsafe(exit) + continue + } + const json = internalGraphQL.canonicalJson(exit.value) + const slice = byJson.get(json) + if (slice !== undefined) { + slice.entries.push(entries[i]) + continue + } + const created = { variables: entries[i].request.key, encoded: exit.value, entries: [entries[i]] } + byJson.set(json, created) + slices.push(created) + } + const complete = (slice: Slice, effect: Effect.Effect, GraphQL.Error, R>) => + Effect.map(Effect.exit(effect), (exit) => { + for (const entry of slice.entries) entry.completeUnsafe(exit) + }) + if (execute === undefined) { + return Effect.forEach(slices, (slice) => complete(slice, call(slice.variables)), { discard: true }) + } + if (!Arr.isReadonlyArrayNonEmpty(slices)) return Effect.void + const prefixes = Arr.map(slices, (_, i) => `_${i}_`) + return execute({ + kind: operation.kind, + query: internalGraphQL.renderSlices(operation.kind, operation.name, template, prefixes), + operationName: operation.name, + variables: Object.fromEntries( + slices.flatMap((slice, i) => + Object.entries(slice.encoded ?? {}).map(([name, value]) => [prefixes[i] + name, value]) + ) + ), + headers: Headers.empty, + urlParams: UrlParams.empty + }).pipe( + Effect.flatMap((result) => { + const results = internalGraphQL.splitSlices(result, template, prefixes) + return results === undefined + ? codec.failResponse(result) + : Effect.forEach(slices, (slice, i) => complete(slice, codec.decodeStrict(results[i])), { discard: true }) + }) + ) + } + )).pipe(RequestResolver.batchN(10)) +} + +type AliasedRequest = KeyRequest< + GraphQL.Variables, + GraphQL.Result, + GraphQL.Error, + GraphQL.Services +> + +interface Slice { + readonly variables: GraphQL.Variables + readonly encoded: unknown + readonly entries: Array>> +} + +// ----------------------------------------------------------------------------- +// Internals +// ----------------------------------------------------------------------------- + +const constNoSuchElement = Exit.fail(new Cause.NoSuchElementError()) + +/** + * Resolves each entry's URL in its own context, then splits the entries by URL + * and `batchKey`. Each batch runs in its first entry's context, carrying the + * URL it was grouped by, so the transport doesn't resolve it again. + */ +const makeResolver = ( + method: OperationMethod, + batchKey: ((context: Context.Context) => unknown) | undefined, + runAll: (entries: Arr.NonEmptyArray>) => Effect.Effect, any> +): RequestResolver.RequestResolver => { + const resolve = internalGraphQL.rawExecutors.get(method)?.url + const operation = { kind: method.operation.kind, operationName: method.operation.name } + const urlOf = (entry: Request.Entry) => + resolve === undefined ? Effect.succeed(undefined) : Effect.provideContext(resolve(operation), entry.context) + return RequestResolver.make((entries) => + Effect.flatMap(Effect.forEach(entries, urlOf, { concurrency: "unbounded" }), (urls) => { + // Arrays are compared structurally, so equal keys share a batch. + const batches = MutableHashMap.empty< + readonly [string | undefined, unknown], + Arr.NonEmptyArray> + >() + entries.forEach((entry, i) => { + const key = [urls[i], batchKey?.(entry.context)] as const + const batch = MutableHashMap.get(batches, key) + if (batch._tag === "Some") batch.value.push(entry) + else MutableHashMap.set(batches, key, [entry]) + }) + return Effect.forEach(batches, ([[url], entries]) => { + const context: Context.Context = entries[0].context + return Effect.provideContext( + runAll(entries), + url === undefined ? context : Context.add(context, internalGraphQL.ResolvedUrl, { resolve, url }) + ) + }, { concurrency: "unbounded", discard: true }) + }) + ) +} + +/** + * Calls the method with deduplicated keys. Each target completes all entries + * sharing its key. + */ +const callBatch = ( + method: OperationMethod, + entries: Arr.NonEmptyArray>>, + variables: (keys: ReadonlyArray) => GraphQL.Variables +) => { + const keys: Array = [] + const targets: Array>> = [] + const indices = MutableHashMap.empty() + for (const entry of entries) { + const index = MutableHashMap.get(indices, entry.request.key) + if (index._tag === "None") { + MutableHashMap.set(indices, entry.request.key, keys.length) + keys.push(entry.request.key) + targets.push(entry) + continue + } + const previous = targets[index.value] + targets[index.value] = { + ...previous, + completeUnsafe(exit) { + previous.completeUnsafe(exit) + entry.completeUnsafe(exit) + } + } + } + // Generic conditional parameters prevent calling the partial overload directly. + const call = method as unknown as ( + variables: GraphQL.Variables, + options: { readonly partial: true } + ) => Effect.Effect>, GraphQL.Error, GraphQL.Services> + return Effect.map(call(variables(keys), { partial: true }), (result) => ({ keys, targets, result })) +} + +// Fails the batch with the response's errors and transport metadata. +const batchError = ( + codec: { readonly failResponse: (result: ExecutionResult) => Effect.Effect }, + result: PartialResult +) => codec.failResponse(internalGraphQL.partialResponses.get(result) ?? { errors: result.errors }) + +const truncated = (path: ReadonlyArray) => + new DecodeError({ + description: `pageInfo.hasNextPage is true next to ${ + path.join(".") + }, so results beyond the page would be reported missing. Make the page size at least the RequestResolver.batchN size` + }) + +const completeAll = (targets: ReadonlyArray>) => { + for (const target of targets) target.completeUnsafe(constNoSuchElement) + return Effect.void +} + +const entryIndex = (error: GraphQLError, path: ReadonlyArray, length: number): number | undefined => { + const errorPath = error.path + if (errorPath === undefined || errorPath.length <= path.length) return undefined + for (let i = 0; i < path.length; i++) { + if (errorPath[i] !== path[i]) return undefined + } + const index = errorPath[path.length] + return typeof index === "number" && Number.isInteger(index) && index >= 0 && index < length ? index : undefined +} + +const findList = ( + data: unknown, + path: ReadonlyArray +): { readonly items: ReadonlyArray; readonly truncated: boolean } | undefined => { + // Path guarantees object fields leading to a list. + let parent = data as Record | null | undefined + for (let i = 0; i < path.length - 1; i++) parent = parent?.[path[i]] + const items = parent?.[path[path.length - 1]] as ReadonlyArray | null | undefined + if (items === null || items === undefined) return undefined + return { items, truncated: parent!.pageInfo?.hasNextPage === true } +} + +const matchesTypename = (item: unknown, typename: string | undefined): boolean => + item !== null && item !== undefined && + (typename === undefined || (item as { readonly __typename?: unknown }).__typename === typename) diff --git a/packages/effect/src/graphql/index.ts b/packages/effect/src/graphql/index.ts new file mode 100644 index 000000000..a065eee13 --- /dev/null +++ b/packages/effect/src/graphql/index.ts @@ -0,0 +1,48 @@ +/** + * @stability experimental + * @since 4.0.0 + */ + +// @barrel: Auto-generated exports. Do not edit manually. + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQL from "./GraphQL.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQLClient from "./GraphQLClient.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQLClientError from "./GraphQLClientError.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQLGroup from "./GraphQLGroup.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQLMiddleware from "./GraphQLMiddleware.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQLProtocol from "./GraphQLProtocol.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as GraphQLResolver from "./GraphQLResolver.ts" diff --git a/repos/effect/packages/effect/src/http-api/HttpApi.ts b/packages/effect/src/http-api/HttpApi.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApi.ts rename to packages/effect/src/http-api/HttpApi.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts b/packages/effect/src/http-api/HttpApiBuilder.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiBuilder.ts rename to packages/effect/src/http-api/HttpApiBuilder.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiClient.ts b/packages/effect/src/http-api/HttpApiClient.ts similarity index 99% rename from repos/effect/packages/effect/src/http-api/HttpApiClient.ts rename to packages/effect/src/http-api/HttpApiClient.ts index 6bf1a5bfc..e5e2287a3 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiClient.ts +++ b/packages/effect/src/http-api/HttpApiClient.ts @@ -23,6 +23,7 @@ import * as HttpClientError from "../http/HttpClientError.ts" import * as HttpClientRequest from "../http/HttpClientRequest.ts" import type * as HttpClientResponse from "../http/HttpClientResponse.ts" import * as HttpMethod from "../http/HttpMethod.ts" +import * as internalUrlParams from "../http/internal/urlParams.ts" import * as UrlParams from "../http/UrlParams.ts" import * as InternalRecord from "../internal/record.ts" import * as Predicate from "../Predicate.ts" @@ -746,7 +747,7 @@ export const urlBuilder = (api: Api, options?: { ) for (const [key, value] of urlParams.params) { if (value !== undefined) { - url.searchParams.append(key, value) + url.searchParams.append(key, internalUrlParams.unwrap(value)) } } return url.toString() diff --git a/repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts b/packages/effect/src/http-api/HttpApiEndpoint.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiEndpoint.ts rename to packages/effect/src/http-api/HttpApiEndpoint.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiError.ts b/packages/effect/src/http-api/HttpApiError.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiError.ts rename to packages/effect/src/http-api/HttpApiError.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiGroup.ts b/packages/effect/src/http-api/HttpApiGroup.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiGroup.ts rename to packages/effect/src/http-api/HttpApiGroup.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts b/packages/effect/src/http-api/HttpApiMiddleware.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiMiddleware.ts rename to packages/effect/src/http-api/HttpApiMiddleware.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiScalar.ts b/packages/effect/src/http-api/HttpApiScalar.ts similarity index 97% rename from repos/effect/packages/effect/src/http-api/HttpApiScalar.ts rename to packages/effect/src/http-api/HttpApiScalar.ts index ebb51b8c1..eb585fa80 100644 --- a/repos/effect/packages/effect/src/http-api/HttpApiScalar.ts +++ b/packages/effect/src/http-api/HttpApiScalar.ts @@ -60,6 +60,11 @@ export type ScalarConfig = { layout?: "modern" | "classic" /** URL to a request proxy for the API client */ proxyUrl?: string + /** Authentication preferences for the API client */ + authentication?: { + /** OpenAPI security scheme names to select by default */ + preferredSecurityScheme?: string | Array> | null + } /** Browser JavaScript function expression used by Scalar for documents and test requests */ customFetch?: string /** Whether to show the sidebar */ diff --git a/repos/effect/packages/effect/src/http-api/HttpApiSchema.ts b/packages/effect/src/http-api/HttpApiSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiSchema.ts rename to packages/effect/src/http-api/HttpApiSchema.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts b/packages/effect/src/http-api/HttpApiSecurity.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiSecurity.ts rename to packages/effect/src/http-api/HttpApiSecurity.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts b/packages/effect/src/http-api/HttpApiSwagger.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiSwagger.ts rename to packages/effect/src/http-api/HttpApiSwagger.ts diff --git a/repos/effect/packages/effect/src/http-api/HttpApiTest.ts b/packages/effect/src/http-api/HttpApiTest.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/HttpApiTest.ts rename to packages/effect/src/http-api/HttpApiTest.ts diff --git a/repos/effect/packages/effect/src/http-api/OpenApi.ts b/packages/effect/src/http-api/OpenApi.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/OpenApi.ts rename to packages/effect/src/http-api/OpenApi.ts diff --git a/repos/effect/packages/effect/src/http-api/index.ts b/packages/effect/src/http-api/index.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/index.ts rename to packages/effect/src/http-api/index.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/html.ts b/packages/effect/src/http-api/internal/html.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/html.ts rename to packages/effect/src/http-api/internal/html.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts b/packages/effect/src/http-api/internal/httpApiScalar.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/httpApiScalar.ts rename to packages/effect/src/http-api/internal/httpApiScalar.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/httpApiSwagger.ts b/packages/effect/src/http-api/internal/httpApiSwagger.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/httpApiSwagger.ts rename to packages/effect/src/http-api/internal/httpApiSwagger.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/mediaType.ts b/packages/effect/src/http-api/internal/mediaType.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/mediaType.ts rename to packages/effect/src/http-api/internal/mediaType.ts diff --git a/repos/effect/packages/effect/src/http-api/internal/path.ts b/packages/effect/src/http-api/internal/path.ts similarity index 100% rename from repos/effect/packages/effect/src/http-api/internal/path.ts rename to packages/effect/src/http-api/internal/path.ts diff --git a/repos/effect/packages/effect/src/http/Cookies.ts b/packages/effect/src/http/Cookies.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Cookies.ts rename to packages/effect/src/http/Cookies.ts diff --git a/repos/effect/packages/effect/src/http/Etag.ts b/packages/effect/src/http/Etag.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Etag.ts rename to packages/effect/src/http/Etag.ts diff --git a/repos/effect/packages/effect/src/http/FetchHttpClient.ts b/packages/effect/src/http/FetchHttpClient.ts similarity index 91% rename from repos/effect/packages/effect/src/http/FetchHttpClient.ts rename to packages/effect/src/http/FetchHttpClient.ts index 2ea1026c3..347058f54 100644 --- a/repos/effect/packages/effect/src/http/FetchHttpClient.ts +++ b/packages/effect/src/http/FetchHttpClient.ts @@ -115,12 +115,15 @@ const fetch: HttpClient.HttpClient = HttpClient.make((request, url, signal, fibe * **Gotchas** * * Fetch behavior comes from the runtime's implementation, so CORS, cookies, - * redirects, abort handling, and streaming support can vary by platform. Stream - * request bodies are sent as Web streams with `duplex: "half"`, and any - * `content-length` header is removed before calling `fetch`. + * redirects, abort handling, and streaming support can vary by platform. Trace + * propagation is on by default, and a browser preflight fails unless the + * target allows `b3` and `traceparent`. Stream request bodies are sent as Web + * streams with `duplex: "half"`, and any `content-length` header is removed + * before calling `fetch`. * * @see {@link Fetch} for supplying the fetch implementation used by this layer * @see {@link RequestInit} for default `RequestInit` options applied before request-specific fields + * @see {@link HttpClient.TracerPropagationEnabled} for leaving `b3` and `traceparent` off a browser request * * @stability unstable * @category layers diff --git a/repos/effect/packages/effect/src/http/FindMyWay.ts b/packages/effect/src/http/FindMyWay.ts similarity index 100% rename from repos/effect/packages/effect/src/http/FindMyWay.ts rename to packages/effect/src/http/FindMyWay.ts diff --git a/repos/effect/packages/effect/src/http/FindMyWay/internal/queryString.ts b/packages/effect/src/http/FindMyWay/internal/queryString.ts similarity index 100% rename from repos/effect/packages/effect/src/http/FindMyWay/internal/queryString.ts rename to packages/effect/src/http/FindMyWay/internal/queryString.ts diff --git a/repos/effect/packages/effect/src/http/FindMyWay/internal/router.ts b/packages/effect/src/http/FindMyWay/internal/router.ts similarity index 99% rename from repos/effect/packages/effect/src/http/FindMyWay/internal/router.ts rename to packages/effect/src/http/FindMyWay/internal/router.ts index a8751a3bb..d14dad66c 100644 --- a/repos/effect/packages/effect/src/http/FindMyWay/internal/router.ts +++ b/packages/effect/src/http/FindMyWay/internal/router.ts @@ -338,13 +338,6 @@ class RouterImpl implements Router.Router { path = path.replace(FULL_PATH_REGEXP, "/") } - // This must be run before sanitizeUrl as the resulting function - // .sliceParameter must be constructed with same URL string used - // throughout the rest of this function. - if (this.options.ignoreDuplicateSlashes) { - path = removeDuplicateSlashes(path) - } - let sanitizedUrl try { sanitizedUrl = safeDecodeURI(path) @@ -355,6 +348,10 @@ class RouterImpl implements Router.Router { return undefined } + if (this.options.ignoreDuplicateSlashes) { + path = removeDuplicateSlashes(path) + } + if (this.options.ignoreTrailingSlash) { path = trimLastSlash(path) } @@ -860,7 +857,8 @@ function makeCreateParams( } } -function getClosingParenthensePosition(path: string, idx: number) { +/** @internal */ +export function getClosingParenthensePosition(path: string, idx: number) { // `path.indexOf()` will always return the first position of the closing parenthese, // but it's inefficient for grouped or wrong regexp expressions. // see issues #62 and #63 for more info diff --git a/repos/effect/packages/effect/src/http/Headers.ts b/packages/effect/src/http/Headers.ts similarity index 96% rename from repos/effect/packages/effect/src/http/Headers.ts rename to packages/effect/src/http/Headers.ts index 0ec53bbcd..876932a4a 100644 --- a/repos/effect/packages/effect/src/http/Headers.ts +++ b/packages/effect/src/http/Headers.ts @@ -406,10 +406,11 @@ export const isRedactedName = ( name: string, patterns: ReadonlyArray ): boolean => { + const lowerName = name.toLowerCase() for (let i = 0; i < patterns.length; i++) { const pattern = patterns[i] if (typeof pattern === "string") { - if (pattern.toLowerCase() === name.toLowerCase()) { + if (pattern.toLowerCase() === lowerName) { return true } } else if (name.search(pattern) !== -1) { @@ -424,9 +425,9 @@ export const isRedactedName = ( * * **Details** * - * 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. + * Defaults include `authorization`, `cookie`, `set-cookie` and `x-api-key`. + * A custom list replaces the defaults. Matching uses `isRedactedName`. + * Use `UrlParams.CurrentRedactedNames` for query parameters. * * @stability unstable * @category services @@ -439,10 +440,6 @@ export const CurrentRedactedNames = Context.Reference< "authorization", "cookie", "set-cookie", - "x-api-key", - "AWSAccessKeyId", - "Signature", - "sig", - "X-Goog-Signature" + "x-api-key" ] }) diff --git a/repos/effect/packages/effect/src/http/HttpBody.ts b/packages/effect/src/http/HttpBody.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpBody.ts rename to packages/effect/src/http/HttpBody.ts diff --git a/repos/effect/packages/effect/src/http/HttpClient.ts b/packages/effect/src/http/HttpClient.ts similarity index 93% rename from repos/effect/packages/effect/src/http/HttpClient.ts rename to packages/effect/src/http/HttpClient.ts index 814dd8113..1c333ed91 100644 --- a/repos/effect/packages/effect/src/http/HttpClient.ts +++ b/packages/effect/src/http/HttpClient.ts @@ -47,6 +47,7 @@ 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" +import * as UrlParams from "./UrlParams.ts" const TypeId = "~effect/http/HttpClient" @@ -727,7 +728,11 @@ export const make = ( (span) => { span.attribute("http.request.method", request.method) const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames) - tracing.addUrlAttributes(span, url) + tracing.addUrlAttributes( + span, + url, + tracing.redactedNames(request.urlParams, fiber.getRef(UrlParams.CurrentRedactedNames)) + ) const headerFilter = fiber.getRef(TracerHeaderFilter) tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames) request = fiber.getRef(TracerPropagationEnabled) @@ -952,6 +957,9 @@ export const retry: { * Specifying a `while` predicate allows you to consider other errors as * transient, and is ignored in "response-only" mode. * + * A `Retry-After` header on a transient response, or on the response of a + * status code error, sets the minimum delay before the next attempt. + * * @stability unstable * @category error handling * @since 4.0.0 @@ -1025,14 +1033,15 @@ export const retryTransient: { ): HttpClient.With => { const isOnlySchedule = Schedule.isSchedule(options) const retryOn = isOnlySchedule ? "errors-and-responses" : options.retryOn ?? "errors-and-responses" - const schedule = isOnlySchedule ? options : options.schedule - const passthroughSchedule = schedule && Schedule.passthrough(schedule) + const schedule = withRetryAfter( + isOnlySchedule ? options : options.schedule ?? Schedule.forever + ) const times = isOnlySchedule ? undefined : options.times return transformResponse( self, flow( retryOn === "errors-only" ? identity : Effect.repeat({ - schedule: passthroughSchedule!, + schedule, times, while: isTransientResponse }), @@ -1150,6 +1159,8 @@ export declare namespace WithRateLimiter { * It can update limits by inspecting common rate limit response headers and * automatically retries HTTP `429` responses (or `HttpClientError` values * wrapping a `429` response) by forcing the retry back through the limiter. + * When a response reports the remaining budget, requests wait for the + * reported reset once that budget is exhausted. * * **Gotchas** * @@ -1200,14 +1211,36 @@ export const withRateLimiter: { return initialState } + const budgets = new Map() + const getBudget = (key: string): RateLimitBudget => { + let budget = budgets.get(key) + if (budget === undefined) { + budget = { sent: 0, observed: 0, remaining: 0, resetAt: 0 } + budgets.set(key, budget) + } + return budget + } + const onResponse = options.disableResponseInspection ? undefined - : (clock: Clock, key: string, headers: Headers.Headers, tokens: number) => { + : (clock: Clock, key: string, headers: Headers.Headers, tokens: number, sent: number) => { const current = getState(key) const next = parseRateLimiterState(current, clock, headers, tokens, headerNames) if (next.limit !== current.limit || !Duration.equals(next.window, current.window)) { states.set(key, next) } + const remaining = parseRateLimitRemaining(headers, headerNames) + const budget = getBudget(key) + // responses to earlier requests carry stale counts + if (remaining === undefined || sent <= budget.observed) { + return + } + const resetAfter = parseRateLimitWindow(clock, headers, headerNames) + budget.observed = sent + budget.remaining = remaining + if (resetAfter !== undefined) { + budget.resetAt = clock.currentTimeMillisUnsafe() + Duration.toMillis(resetAfter) + } } return transform(self, function loop(effect, request, retries = 0): Effect.Effect< @@ -1219,6 +1252,15 @@ export const withRateLimiter: { const clock = fiber.getRef(Clock) const key = resolveKey(request) const tokens = Math.max(resolveTokens(request), 1) + const budget = getBudget(key) + const now = clock.currentTimeMillisUnsafe() + // requests admitted after the observed one are not reflected in its count + if (budget.resetAt > now && budget.remaining - (budget.sent - budget.observed) < tokens) { + return Effect.flatMap(Effect.sleep(budget.resetAt - now), () => loop(effect, request, retries)) + } + // counted at admission, so requests already admitted are sent even if a + // later response exhausts the budget + const sent = budget.sent += tokens const current = getState(key) const canRetry = options.times === undefined || retries < options.times function retry(retryAfter: Duration.Duration | undefined) { @@ -1230,11 +1272,14 @@ export const withRateLimiter: { response: HttpClientResponse.HttpClientResponse, adaptive: RateLimiter.AdaptiveConsumeResult | undefined ) => { - onResponse?.(clock, key, response.headers, tokens) + onResponse?.(clock, key, response.headers, tokens, sent) if (options.disableResponseInspection || response.status !== 429) { return Effect.succeed(undefined) } - const retryAfter = parseRetryAfter(clock, getHeader(response.headers, ...headerNames.retryAfter)) + const retryAfter = parseRetryAfter( + clock.currentTimeMillisUnsafe(), + getHeader(response.headers, ...headerNames.retryAfter) + ) if (retryAfter === undefined) { return Effect.succeed(undefined) } @@ -1348,6 +1393,17 @@ interface RateLimiterState { readonly initial: boolean } +interface RateLimitBudget { + /** Total tokens admitted for the key. */ + sent: number + /** The `sent` total of the request whose remaining count was last applied. */ + observed: number + /** The remaining count reported in response to the `observed` request. */ + remaining: number + /** When the server budget resets, in epoch milliseconds. */ + resetAt: number +} + const parseRateLimiterState = ( state: RateLimiterState, clock: Clock, @@ -1403,21 +1459,24 @@ const parseRateLimitWindow = ( } const parseRetryAfter = ( - clock: Clock, + nowMillis: number, value: string | undefined ): Duration.Duration | undefined => { if (value === undefined) { return undefined } - const numeric = parseNumberHeader(value) - if (numeric !== undefined) { - return Duration.max(Duration.seconds(numeric), Duration.millis(1)) + const trimmed = value.trim() + if (/^\d+(?:\.\d+)?$/.test(trimmed)) { + const numeric = Number(trimmed) + return Number.isFinite(numeric) + ? Duration.max(Duration.seconds(numeric), Duration.millis(1)) + : undefined } - const parsedDate = Date.parse(value) + const parsedDate = Date.parse(trimmed) if (Number.isNaN(parsedDate)) { return undefined } - const millis = parsedDate - clock.currentTimeMillisUnsafe() + const millis = parsedDate - nowMillis if (millis <= 0) { return Duration.millis(1) } @@ -1695,6 +1754,18 @@ export const TracerHeaderFilter = Context.Reference< /** * Context reference that controls whether outgoing client spans are propagated to request headers. * + * **Details** + * + * When this is `true`, the client writes the compact B3 `b3` header and the + * W3C `traceparent` header. The default is on. The client span is still + * created when this is `false`. + * + * **Gotchas** + * + * A browser preflight fails when the target does not list `b3` and + * `traceparent` in `Access-Control-Allow-Headers`. Set this to `false` for + * those requests. + * * @stability unstable * @category services * @since 4.0.0 @@ -1879,6 +1950,28 @@ const isTransientHttpError = (error: unknown) => (error.reason._tag === "TransportError" || (error.reason._tag === "StatusCodeError" && isTransientResponse(error.reason.response))) +const retryAfterOf = (input: unknown, nowMillis: number): Duration.Duration | undefined => { + const response = Error.isHttpClientError(input) + ? input.response + : Predicate.hasProperty(input, HttpClientResponse.TypeId) + ? input as HttpClientResponse.HttpClientResponse + : undefined + return parseRetryAfter(nowMillis, response?.headers["retry-after"]) +} + +const withRetryAfter = ( + schedule: Schedule.Schedule +): Schedule.Schedule => + Schedule.fromStep( + Effect.map(Schedule.toStep(schedule), (step) => (now: number, input: unknown) => { + const retryAfter = retryAfterOf(input, now) + if (retryAfter === undefined) { + return step(now, input) + } + return Effect.map(step(now, input), ([output, delay]) => [output, Duration.max(delay, retryAfter)]) + }) + ) + const isTooManyRequestsHttpClientError = ( error: unknown ): error is Error.HttpClientError & { readonly reason: Error.StatusCodeError } => diff --git a/repos/effect/packages/effect/src/http/HttpClientError.ts b/packages/effect/src/http/HttpClientError.ts similarity index 95% rename from repos/effect/packages/effect/src/http/HttpClientError.ts rename to packages/effect/src/http/HttpClientError.ts index 5eef2445b..35bdef0c5 100644 --- a/repos/effect/packages/effect/src/http/HttpClientError.ts +++ b/packages/effect/src/http/HttpClientError.ts @@ -15,6 +15,7 @@ import { hasProperty } from "../Predicate.ts" import * as Schema from "../Schema.ts" import type * as HttpClientRequest from "./HttpClientRequest.ts" import type * as ClientResponse from "./HttpClientResponse.ts" +import * as UrlParams from "./UrlParams.ts" const TypeId = "~effect/http/HttpClientError" @@ -88,6 +89,9 @@ const formatReason = (tag: string) => tag.endsWith("Error") ? tag.slice(0, -5) : const formatMessage = (reason: string, description: string | undefined, info: string) => description ? `${reason}: ${description} (${info})` : `${reason} error (${info})` +const formatMethodAndUrl = (request: HttpClientRequest.HttpClientRequest) => + `${request.method} ${UrlParams.redactUrl(request.url)}` + /** * Error describing transport-level failures that occur while sending an HTTP request. * @@ -107,7 +111,7 @@ export class TransportError extends Data.TaggedError("TransportError")<{ * @since 4.0.0 */ get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } /** @@ -140,7 +144,7 @@ export class EncodeError extends Data.TaggedError("EncodeError")<{ * @since 4.0.0 */ get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } /** @@ -173,7 +177,7 @@ export class InvalidUrlError extends Data.TaggedError("InvalidUrlError")<{ * @since 4.0.0 */ get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } /** @@ -207,7 +211,7 @@ export class StatusCodeError extends Data.TaggedError("StatusCodeError")<{ * @since 4.0.0 */ get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } /** @@ -242,7 +246,7 @@ export class DecodeError extends Data.TaggedError("DecodeError")<{ * @since 4.0.0 */ get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } /** @@ -277,7 +281,7 @@ export class EmptyBodyError extends Data.TaggedError("EmptyBodyError")<{ * @since 4.0.0 */ get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } /** diff --git a/repos/effect/packages/effect/src/http/HttpClientRequest.ts b/packages/effect/src/http/HttpClientRequest.ts similarity index 89% rename from repos/effect/packages/effect/src/http/HttpClientRequest.ts rename to packages/effect/src/http/HttpClientRequest.ts index 7b5a20822..68c9e3d13 100644 --- a/repos/effect/packages/effect/src/http/HttpClientRequest.ts +++ b/packages/effect/src/http/HttpClientRequest.ts @@ -30,6 +30,7 @@ import * as Stream from "../Stream.ts" import * as Headers from "./Headers.ts" import * as HttpBody from "./HttpBody.ts" import { hasBody, type HttpMethod } from "./HttpMethod.ts" +import * as compression from "./internal/compressionBody.ts" import * as bodyInternal from "./internal/httpBody.ts" import * as Url from "./Url.ts" import * as UrlParams from "./UrlParams.ts" @@ -104,7 +105,7 @@ const Proto = { return { _id: "HttpClientRequest", method: this.method, - url: this.url, + url: UrlParams.redactUrl(this.url), urlParams: this.urlParams, hash: this.hash, headers: redact(this.headers), @@ -576,14 +577,18 @@ export const updateUrl: { /** * Sets one query parameter, replacing existing values for that parameter name. * + * **Details** + * + * A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans. + * * @stability unstable * @category combinators * @since 4.0.0 */ export const setUrlParam: { - (key: string, value: string): (self: HttpClientRequest) => HttpClientRequest - (self: HttpClientRequest, key: string, value: string): HttpClientRequest -} = dual(3, (self: HttpClientRequest, key: string, value: string): HttpClientRequest => + (key: string, value: string | Redacted.Redacted): (self: HttpClientRequest) => HttpClientRequest + (self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest +} = dual(3, (self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest => makeWith( self.method, self.url, @@ -616,14 +621,18 @@ export const setUrlParams: { /** * Appends one query parameter value without removing existing values for the same name. * + * **Details** + * + * A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans. + * * @stability unstable * @category combinators * @since 4.0.0 */ export const appendUrlParam: { - (key: string, value: string): (self: HttpClientRequest) => HttpClientRequest - (self: HttpClientRequest, key: string, value: string): HttpClientRequest -} = dual(3, (self: HttpClientRequest, key: string, value: string): HttpClientRequest => + (key: string, value: string | Redacted.Redacted): (self: HttpClientRequest) => HttpClientRequest + (self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest +} = dual(3, (self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest => makeWith( self.method, self.url, @@ -711,6 +720,88 @@ export const setBody: { ) }) +/** + * Algorithms supported by Web `CompressionStream` for request body compression. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type CompressionAlgorithm = "gzip" | "deflate" + +/** + * Options for request body compression. + * + * @stability unstable + * @category options + * @since 4.0.0 + */ +export interface CompressionOptions { + /** + * Minimum known body length in bytes to compress. Defaults to `1024`. + * Bodies with unknown length are always compressed. + */ + readonly minSize?: number | undefined +} + +/** + * Compresses a request body using Web `CompressionStream` and sets `Content-Encoding`. + * + * **Details** + * + * Byte-array bodies (including text, JSON, and URL-encoded bodies) are compressed + * into a new `Uint8Array` body, preserving the content type and updating + * `Content-Length`. The compressed body does not retain the original lazy text. + * Stream bodies are compressed lazily and have their content length removed. + * They still require a client runtime that supports streaming request bodies + * (Fetch requires `duplex: "half"`, which Safari and Firefox do not support). + * + * Returns the original request unchanged for empty, FormData, and raw bodies, + * bodies with a known length below `options.minSize`, or requests with any + * existing `Content-Encoding` header, including `identity`. Unknown-length + * streams are always compressed. The default minimum is 1024 bytes because + * gzip can make very small bodies larger. + * + * Requires `CompressionStream` (available in browsers, Node 18+, Bun, and Deno). + * There is no typed error channel: byte compression failures are defects; + * stream failures surface when the body is sent. + * + * To compress every request, use + * `HttpClient.mapRequestEffect(HttpClientRequest.compressBody("gzip"))`. + * `HttpClient.retryTransient` re-runs preprocessing, so this recompresses on + * every retry. For large bodies, compress once before calling `client.execute`. + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const compressBody: { + ( + algorithm: CompressionAlgorithm, + options?: CompressionOptions | undefined + ): (self: HttpClientRequest) => Effect.Effect + ( + self: HttpClientRequest, + algorithm: CompressionAlgorithm, + options?: CompressionOptions | undefined + ): Effect.Effect +} = dual( + (args) => isHttpClientRequest(args[0]), + ( + self: HttpClientRequest, + algorithm: CompressionAlgorithm, + options?: CompressionOptions | undefined + ): Effect.Effect => { + if (self.headers["content-encoding"] !== undefined) { + return Effect.succeed(self) + } + return Effect.map( + compression.compressBody(self.body, algorithm, options), + (body) => body === self.body ? self : setHeader(setBody(self, body), "content-encoding", algorithm) + ) + } +) + /** * Sets a `Uint8Array` request body with an optional content type. * diff --git a/repos/effect/packages/effect/src/http/HttpClientResponse.ts b/packages/effect/src/http/HttpClientResponse.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpClientResponse.ts rename to packages/effect/src/http/HttpClientResponse.ts diff --git a/repos/effect/packages/effect/src/http/HttpEffect.ts b/packages/effect/src/http/HttpEffect.ts similarity index 96% rename from repos/effect/packages/effect/src/http/HttpEffect.ts rename to packages/effect/src/http/HttpEffect.ts index 16e0fb4e2..2de32a8e7 100644 --- a/repos/effect/packages/effect/src/http/HttpEffect.ts +++ b/packages/effect/src/http/HttpEffect.ts @@ -321,10 +321,11 @@ export const toWebHandlerWith = { const withoutBody = request.method === "HEAD" if (!Response.omitsBody(response, withoutBody)) { + // The transferred request scope closes when an abort interrupts the body, even if it is no longer read. response = scopeTransferToStream(response) } ;(request as any)[resolveSymbol]( - Response.toWeb(response, { withoutBody, context }) + Response.toWeb(response, { withoutBody, context, signal: (request.source as globalThis.Request).signal }) ) return Effect.void }, middleware) @@ -340,10 +341,15 @@ export const toWebHandlerWith = { - fiber.interruptUnsafe(undefined, ClientAbort.annotation) - }, { once: true }) + const signal = request.signal + if (fiber.pollUnsafe() === undefined && signal) { + const onAbort = () => fiber.interruptUnsafe(undefined, ClientAbort.annotation) + if (signal.aborted) { + onAbort() + } else { + signal.addEventListener("abort", onAbort, { once: true }) + fiber.addObserver(() => signal.removeEventListener("abort", onAbort)) + } } }) } diff --git a/repos/effect/packages/effect/src/http/HttpIncomingMessage.ts b/packages/effect/src/http/HttpIncomingMessage.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpIncomingMessage.ts rename to packages/effect/src/http/HttpIncomingMessage.ts diff --git a/repos/effect/packages/effect/src/http/HttpMethod.ts b/packages/effect/src/http/HttpMethod.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpMethod.ts rename to packages/effect/src/http/HttpMethod.ts diff --git a/repos/effect/packages/effect/src/http/HttpMiddleware.ts b/packages/effect/src/http/HttpMiddleware.ts similarity index 81% rename from repos/effect/packages/effect/src/http/HttpMiddleware.ts rename to packages/effect/src/http/HttpMiddleware.ts index 0b818c4e1..87b4caf75 100644 --- a/repos/effect/packages/effect/src/http/HttpMiddleware.ts +++ b/packages/effect/src/http/HttpMiddleware.ts @@ -11,6 +11,7 @@ * @stability unstable * @since 4.0.0 */ +import * as ByteSize from "../ByteSize.ts" import * as Cause from "../Cause.ts" import { Clock } from "../Clock.ts" import * as Context from "../Context.ts" @@ -26,6 +27,7 @@ import type { Predicate } from "../Predicate.ts" import type { ReadonlyRecord } from "../Record.ts" import { nativeTracer, ParentSpan, Tracer } from "../Tracer.ts" import * as Headers from "./Headers.ts" +import { hasBody } from "./HttpMethod.ts" import type { CompressionAlgorithm } from "./HttpPlatform.ts" import { HttpPlatform } from "./HttpPlatform.ts" import { causeResponseStripped, ClientAbort, HttpServerError, ResponseError } from "./HttpServerError.ts" @@ -35,9 +37,11 @@ import * as Response from "./HttpServerResponse.ts" import type { HttpServerResponse } from "./HttpServerResponse.ts" import * as TraceContext from "./HttpTraceContext.ts" import * as compressionInternal from "./internal/compression.ts" +import * as decompressionInternal from "./internal/decompression.ts" import * as bodyInternal from "./internal/httpBody.ts" import { appendPreResponseHandlerUnsafe } from "./internal/preResponseHandler.ts" import * as tracing from "./internal/tracing.ts" +import * as UrlParams from "./UrlParams.ts" /** * Middleware that transforms an HTTP server app effect into another HTTP server app effect. @@ -268,42 +272,37 @@ export const tracer: ( } const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames) const headerFilter = fiber.getRef(TracerHeaderFilter) - fiber.currentDispatcher.scheduleTask(() => { - let response: HttpServerResponse - let spanExit: Exit.Exit - if (Exit.isFailure(exit)) { - const [failureResponse, cause] = causeResponseStripped(exit.cause) - response = failureResponse - // 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) + // Complete the span before the request fiber exits so shutdown can flush it. + let response: HttpServerResponse + let spanExit: Exit.Exit + if (Exit.isFailure(exit)) { + const [failureResponse, cause] = causeResponseStripped(exit.cause) + response = failureResponse + // 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) + const url = Request.toURL(request) + if (Option.isSome(url)) { + tracing.addUrlAttributes(span, url.value, fiber.getRef(UrlParams.CurrentRedactedNames)) + } + if (request.headers["user-agent"] !== undefined) { + span.attribute("user_agent.original", request.headers["user-agent"]) } - if (span.sampled) { - span.attribute("http.request.method", request.method) - 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"]) - } - 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) - tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames) + tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames) + if (Option.isSome(request.remoteAddress)) { + span.attribute("client.address", request.remoteAddress.value) } - span.end(endTime, spanExit) - }, 0) + span.attribute("http.response.status_code", response.status) + tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames) + } + span.end(endTime, spanExit) return undefined }) return httpApp @@ -627,6 +626,86 @@ export const compression = ( }) } +/** + * Middleware that decompresses request bodies sent with a `Content-Encoding` + * of `gzip` or `deflate`. + * + * **Details** + * + * The request service is replaced with a view whose body accessors (`stream`, + * `text`, `json`, `arrayBuffer`, `urlParamsBody`, `multipart`) yield the + * decompressed body, and whose headers no longer include `Content-Encoding` + * or `Content-Length`. Decompression is lazy and only happens when the body + * is read. Stacked codings such as `deflate, gzip` are undone in reverse + * order, and `identity` members are ignored. + * + * Requests whose `Content-Encoding` includes any other coding are rejected + * with `415 Unsupported Media Type` and `Accept-Encoding: gzip, deflate`, + * without running the app. Malformed compressed data fails the body read + * with a `RequestParseError`, or a `MultipartError` with reason `Parse` when + * reading multipart data. + * + * The decompressed size is limited to `maxDecompressedSize`, falling back to + * the `MaxBodySize` reference and then to 10 MiB. Exceeding it fails the body + * read with a `RequestParseError`, or a `MultipartError` with reason + * `BodyTooLarge` when reading multipart data. The limit applies to every body + * accessor, including `stream`. + * + * Decompression uses the global `DecompressionStream`. `deflate` means the + * zlib format (RFC 1950); raw deflate data is rejected. + * + * **Gotchas** + * + * `request.source` is still the original, compressed request. + * `HttpServerRequest.toWeb` returns that compressed request only when the + * source is a Web `Request`. On other platforms, such as Node, it builds a new + * `Request` from the decompressed body and the stripped headers. + * + * @stability unstable + * @category compression + * @since 4.0.0 + */ +export const decompression = ( + options?: { + /** + * Maximum number of decompressed body bytes. Defaults to the + * `MaxBodySize` reference, or 10 MiB when that is unset. + */ + readonly maxDecompressedSize?: ByteSize.Input | undefined + } | undefined +): ( + httpApp: Effect.Effect +) => Effect.Effect => { + const maxDecompressedSize = options?.maxDecompressedSize === undefined + ? undefined + : ByteSize.fromInputUnsafe(options.maxDecompressedSize) + return ( + httpApp: Effect.Effect + ): Effect.Effect => + Effect.withFiber((fiber) => { + const request = Context.getUnsafe(fiber.context, HttpServerRequest) + const contentEncoding = request.headers["content-encoding"] + if (contentEncoding === undefined || !hasBody(request.method)) { + return httpApp + } + const codings = decompressionInternal.parseContentEncoding(contentEncoding) + if (codings === undefined) { + return Effect.succeed(Response.empty({ + status: 415, + headers: { "accept-encoding": "gzip, deflate" } + })) + } + if (codings.length === 0) { + return httpApp + } + return Effect.provideService( + httpApp, + HttpServerRequest, + new decompressionInternal.DecompressedRequest(request, codings, maxDecompressedSize) + ) + }) +} + const withVary = (response: HttpServerResponse): HttpServerResponse => { const vary = compressionInternal.varyWith(response.headers, "Accept-Encoding") return Response.setHeader(response, "vary", vary) diff --git a/repos/effect/packages/effect/src/http/HttpPlatform.ts b/packages/effect/src/http/HttpPlatform.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpPlatform.ts rename to packages/effect/src/http/HttpPlatform.ts diff --git a/repos/effect/packages/effect/src/http/HttpRouter.ts b/packages/effect/src/http/HttpRouter.ts similarity index 92% rename from repos/effect/packages/effect/src/http/HttpRouter.ts rename to packages/effect/src/http/HttpRouter.ts index e95adbd04..fb9430af1 100644 --- a/repos/effect/packages/effect/src/http/HttpRouter.ts +++ b/packages/effect/src/http/HttpRouter.ts @@ -24,6 +24,7 @@ import * as Scope from "../Scope.ts" import * as Tracer from "../Tracer.ts" import type * as Types from "../Types.ts" import * as FindMyWay from "./FindMyWay.ts" +import { getClosingParenthensePosition } from "./FindMyWay/internal/router.ts" import * as HttpEffect from "./HttpEffect.ts" import * as HttpIncomingMessage from "./HttpIncomingMessage.ts" import type * as HttpMethod from "./HttpMethod.ts" @@ -123,7 +124,12 @@ export const HttpRouter: Context.Service = Context.Servi * @since 4.0.0 */ export const make = Effect.gen(function*() { - const router = FindMyWay.make>(yield* RouterConfig) + const config = yield* RouterConfig + const ignoreDuplicateSlashes = config.ignoreDuplicateSlashes ?? true + const router = FindMyWay.make<{ + readonly route: Route + readonly prefixSegments: number | undefined + }>(config) const middleware = new Set() const addAll = >>( @@ -147,19 +153,25 @@ export const make = Effect.gen(function*() { ...routes[i], handler: applyMiddleware(routes[i].handler as Effect.Effect) }) + const handler = { + route: route as Route, + prefixSegments: Option.isSome(route.prefix) + ? countSegments(route.prefix.value, ignoreDuplicateSlashes) + : undefined + } if (route.method === "*") { if (route.path.endsWith("/*")) { - router.all(route.path, route as any) - router.all(route.path.slice(0, -2) as any, route as any) + router.all(route.path, handler) + router.all(route.path.slice(0, -2) as any, handler) } else { - router.all(route.path, route as any) + router.all(route.path, handler) } } else { if (route.path.endsWith("/*")) { - router.on(route.method, route.path, route as any) - router.on(route.method, route.path.slice(0, -2) as any, route as any) + router.on(route.method, route.path, handler) + router.on(route.method, route.path.slice(0, -2) as any, handler) } else { - router.on(route.method, route.path, route as any) + router.on(route.method, route.path, handler) } } } @@ -211,12 +223,12 @@ export const make = Effect.gen(function*() { }) ) } - const route = result.handler - if (Option.isSome(route.prefix)) { + const { route, prefixSegments } = result.handler + if (prefixSegments !== undefined) { context = Context.add( context, HttpServerRequest.HttpServerRequest, - sliceRequestUrl(request, route.prefix.value) + sliceRequestUrl(request, prefixSegments, ignoreDuplicateSlashes) ) } context = Context.add(context, HttpServerRequest.ParsedSearchParams, result.searchParams) @@ -251,9 +263,44 @@ export const make = Effect.gen(function*() { }) }) -function sliceRequestUrl(request: HttpServerRequest.HttpServerRequest, prefix: string) { - const prefexLen = prefix.length - return request.modify({ url: request.url.length <= prefexLen ? "/" : request.url.slice(prefexLen) }) +function sliceRequestUrl( + request: HttpServerRequest.HttpServerRequest, + prefixSegments: number, + ignoreDuplicateSlashes: boolean +) { + const url = request.url + let index = 0 + for (let segments = prefixSegments; segments > 0; segments--) { + if (ignoreDuplicateSlashes) { + while (url[index] === "/") index++ + } else if (url[index] === "/") { + index++ + } + while (index < url.length && !"/?;#".includes(url[index])) index++ + } + return request.modify({ url: index < url.length ? url.slice(index) : "/" }) +} + +function countSegments(prefix: string, ignoreDuplicateSlashes: boolean) { + let count = 0 + let inParamName = false + for (let i = 0; i < prefix.length; i++) { + const char = prefix[i] + if (char === "/") { + if (!ignoreDuplicateSlashes || prefix[i - 1] !== "/") count++ + inParamName = false + } else if (char === ":") { + if (prefix[i + 1] === ":") i++ + else inParamName = true + } else if (char === "-" || char === ".") { + inParamName = false + } else if (char === "(" && inParamName) { + // Slashes inside parameter regexes are not segment boundaries. + i = getClosingParenthensePosition(prefix, i) + inParamName = false + } + } + return count } /** @@ -1360,6 +1407,41 @@ export const serve = | Request.On * request arrives, in which case that request waits for the build to finish. * If the build fails, every request rejects with the build error. * + * **Gotchas** + * + * On Cloudflare Workers, build the handler at module scope: fibers started + * inside `fetch` capture that request's async context. Layer construction + * must not require request-time I/O; bindings are available from + * `cloudflare:workers` via its `env` export. + * Prefer request-local work or a Durable Object's supported lifecycle APIs, + * not indefinite background execution. For shared fibers, provide a sync + * scheduler to both layers and requests as below; see `Scheduler.MixedScheduler` + * for its limitations. Bound waits for shared work with request-local timeouts. + * + * **Example** (Opting in to the sync scheduler on Workers) + * + * ```ts + * import { Effect, Layer, Scheduler } from "effect" + * import { HttpRouter, HttpServer, HttpServerResponse } from "effect/http" + * + * const SyncScheduler = Layer.succeed( + * Scheduler.Scheduler, + * new Scheduler.MixedScheduler("sync") + * ) + * const AppLive = HttpRouter.add( + * "GET", + * "/ping", + * Effect.succeed(HttpServerResponse.text("pong")) + * ).pipe(Layer.provide(HttpServer.layerServices)) + * + * // Build once at module scope. The scheduler is also retained for requests. + * const app = HttpRouter.toWebHandler( + * AppLive.pipe(Layer.provideMerge(SyncScheduler)) + * ) + * + * export default { fetch: (request: Request) => app.handler(request) } + * ``` + * * @stability unstable * @category converting * @since 4.0.0 diff --git a/repos/effect/packages/effect/src/http/HttpServer.ts b/packages/effect/src/http/HttpServer.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpServer.ts rename to packages/effect/src/http/HttpServer.ts diff --git a/repos/effect/packages/effect/src/http/HttpServerError.ts b/packages/effect/src/http/HttpServerError.ts similarity index 96% rename from repos/effect/packages/effect/src/http/HttpServerError.ts rename to packages/effect/src/http/HttpServerError.ts index fa9bffdff..f06401d15 100644 --- a/repos/effect/packages/effect/src/http/HttpServerError.ts +++ b/packages/effect/src/http/HttpServerError.ts @@ -23,6 +23,7 @@ import { hasProperty } from "../Predicate.ts" import type * as Request from "./HttpServerRequest.ts" import * as Respondable from "./HttpServerRespondable.ts" import * as Response from "./HttpServerResponse.ts" +import * as UrlParams from "./UrlParams.ts" const TypeId = "~effect/http/HttpServerError" @@ -107,7 +108,7 @@ export class RequestParseError extends Data.TaggedError("RequestParseError")<{ } get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } override get message() { @@ -139,7 +140,7 @@ export class RouteNotFound extends Data.TaggedError("RouteNotFound")<{ override readonly [ErrorReporter.ignore] = true get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } override get message() { @@ -174,7 +175,7 @@ export class InternalError extends Data.TaggedError("InternalError")<{ } get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } override get message() { @@ -214,7 +215,7 @@ export class ResponseError extends Data.TaggedError("ResponseError")<{ } get methodAndUrl() { - return `${this.request.method} ${this.request.url}` + return formatMethodAndUrl(this.request) } override get message() { @@ -275,6 +276,9 @@ export class ClientAbort extends Context.Service()("effect/ht ) } +const formatMethodAndUrl = (request: Request.HttpServerRequest) => + `${request.method} ${UrlParams.redactUrl(request.url)}` + const formatRequestMessage = (reason: string, description: string | undefined, info: string) => { const prefix = `${reason} (${info})` return description ? `${prefix}: ${description}` : prefix diff --git a/repos/effect/packages/effect/src/http/HttpServerRequest.ts b/packages/effect/src/http/HttpServerRequest.ts similarity index 99% rename from repos/effect/packages/effect/src/http/HttpServerRequest.ts rename to packages/effect/src/http/HttpServerRequest.ts index b6e36d01e..32998988b 100644 --- a/repos/effect/packages/effect/src/http/HttpServerRequest.ts +++ b/packages/effect/src/http/HttpServerRequest.ts @@ -512,7 +512,7 @@ class ServerRequestImpl extends Inspectable.Class implements HttpServerRequest { return HttpIncomingMessage.inspect(this, { _id: "HttpServerRequest", method: this.method, - url: this.originalUrl + url: UrlParams.redactUrl(this.originalUrl) }) } modify( @@ -711,7 +711,7 @@ class ClientRequestImpl extends Inspectable.Class implements HttpServerRequest { return HttpIncomingMessage.inspect(this, { _id: "HttpServerRequest", method: this.method, - url: this.originalUrl + url: UrlParams.redactUrl(this.originalUrl) }) } diff --git a/repos/effect/packages/effect/src/http/HttpServerRespondable.ts b/packages/effect/src/http/HttpServerRespondable.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpServerRespondable.ts rename to packages/effect/src/http/HttpServerRespondable.ts diff --git a/repos/effect/packages/effect/src/http/HttpServerResponse.ts b/packages/effect/src/http/HttpServerResponse.ts similarity index 97% rename from repos/effect/packages/effect/src/http/HttpServerResponse.ts rename to packages/effect/src/http/HttpServerResponse.ts index 0479fc34d..3d07cd4ac 100644 --- a/repos/effect/packages/effect/src/http/HttpServerResponse.ts +++ b/packages/effect/src/http/HttpServerResponse.ts @@ -1028,23 +1028,35 @@ export const setStatus: { export const omitsBody = (response: HttpServerResponse, withoutBody = false): boolean => withoutBody || response.status === 204 || response.status === 205 || response.status === 304 +const mergeHeaders = (target: globalThis.Headers, headers: globalThis.Headers): globalThis.Headers => { + for (const [key, value] of headers as any) { + if (key === "set-cookie") { + target.append(key, value) + } else { + target.set(key, value) + } + } + return target +} + /** * Converts an `HttpServerResponse` to a Web `Response`. * * **Details** * * Cookies are appended as `Set-Cookie` headers. Stream bodies are converted using - * the supplied context. + * the supplied context, and are interrupted when `signal` aborts. * * Bodies are omitted for statuses 204, 205, and 304, or when `withoutBody` is true * (for example, for HEAD responses). Omitted raw `ReadableStream` bodies are * cancelled without awaiting completion, and cancellation errors are ignored. * - * A raw `Response` body is returned as-is after the outer headers are merged - * into it: outer headers replace native ones, and cookies are appended. When - * its body is omitted, the bodyless `Response` keeps those merged headers and - * uses the outer status for 204, 205 and 304, or the raw `Response` status - * otherwise. + * A raw `Response` is returned as-is when there are no outer headers or cookies + * to merge. Otherwise, they are merged into a copy without modifying the raw + * response: outer headers replace native ones, and cookies are appended. When + * its body is omitted, the bodyless `Response` keeps those merged + * headers and uses the outer status for 204, 205 and 304, or the raw `Response` + * status otherwise. * * @stability unstable * @category converting @@ -1055,6 +1067,7 @@ export const toWeb = ( options?: { readonly withoutBody?: boolean | undefined readonly context?: Context.Context | undefined + readonly signal?: AbortSignal | undefined } ): Response => { const headers = new globalThis.Headers(response.headers) @@ -1069,15 +1082,13 @@ export const toWeb = ( const withoutBody = outerOmitsBody || options?.withoutBody === true if (body._tag === "Raw" && body.body instanceof Response) { const raw = body.body - for (const [key, value] of headers as any) { - if (key === "set-cookie") { - raw.headers.append(key, value) - } else { - raw.headers.set(key, value) - } - } if (!withoutBody) { - return raw + if (headers.keys().next().done) { + return raw + } + const copy = new Response(raw.body, raw) + mergeHeaders(copy.headers, headers) + return copy } // A bodyless outer status is what the caller chose to send; otherwise the // body is only omitted for HEAD and the raw Response describes the resource. @@ -1085,7 +1096,7 @@ export const toWeb = ( return new Response(undefined, { status: source.status, statusText: source.statusText as string, - headers: raw.headers + headers: mergeHeaders(new globalThis.Headers(raw.headers), headers) }) } if (withoutBody) { @@ -1131,7 +1142,8 @@ export const toWeb = ( return new Response( Stream.toReadableStreamWith( body.stream, - options?.context ?? Context.empty() + options?.context ?? Context.empty(), + { signal: options?.signal } ), { status: response.status, diff --git a/repos/effect/packages/effect/src/http/HttpStaticServer.ts b/packages/effect/src/http/HttpStaticServer.ts similarity index 97% rename from repos/effect/packages/effect/src/http/HttpStaticServer.ts rename to packages/effect/src/http/HttpStaticServer.ts index b1be9c518..1bf300ac9 100644 --- a/repos/effect/packages/effect/src/http/HttpStaticServer.ts +++ b/packages/effect/src/http/HttpStaticServer.ts @@ -13,6 +13,7 @@ import * as Effect from "../Effect.ts" import * as FileSystem from "../FileSystem.ts" import * as Layer from "../Layer.ts" +import * as Option from "../Option.ts" import * as Path from "../Path.ts" import type { PlatformError } from "../PlatformError.ts" import * as HttpPlatform from "./HttpPlatform.ts" @@ -398,16 +399,9 @@ const resolveFilePath = (path: Path.Path, root: string, url: string): string | u if (decodedPath.includes("\u0000")) { return undefined } - const normalizedPath = path.normalize(decodedPath.startsWith("/") ? decodedPath.slice(1) : decodedPath) - if (normalizedPath === ".." || normalizedPath.startsWith(`..${path.sep}`)) { - return undefined - } - const resolvedPath = path.join(root, normalizedPath) - const rootPrefix = root.endsWith(path.sep) ? root : `${root}${path.sep}` - if (resolvedPath !== root && !resolvedPath.startsWith(rootPrefix)) { - return undefined - } - return resolvedPath + // every leading separator is part of the URL, not a filesystem root + const relativePath = decodedPath.replace(path.sep === "\\" ? /^[/\\]+/ : /^\/+/, "") + return Option.getOrUndefined(path.resolveWithin(root, relativePath)) } const toRouteNotFoundError = (request: HttpServerRequest.HttpServerRequest) => diff --git a/repos/effect/packages/effect/src/http/HttpStatus.ts b/packages/effect/src/http/HttpStatus.ts similarity index 100% rename from repos/effect/packages/effect/src/http/HttpStatus.ts rename to packages/effect/src/http/HttpStatus.ts diff --git a/repos/effect/packages/effect/src/http/HttpTraceContext.ts b/packages/effect/src/http/HttpTraceContext.ts similarity index 89% rename from repos/effect/packages/effect/src/http/HttpTraceContext.ts rename to packages/effect/src/http/HttpTraceContext.ts index 0fd64e12f..b61d35164 100644 --- a/repos/effect/packages/effect/src/http/HttpTraceContext.ts +++ b/packages/effect/src/http/HttpTraceContext.ts @@ -49,7 +49,7 @@ export const toHeaders = (span: Tracer.Span): Headers.Headers => onSome: (parent) => `-${parent.spanId}` }) }`, - traceparent: `00-${span.traceId}-${span.spanId}-${span.sampled ? "01" : "00"}` + traceparent: `00-${span.traceId.padStart(32, "0")}-${span.spanId}-${span.sampled ? "01" : "00"}` }) /** @@ -93,7 +93,7 @@ export const b3: FromHeaders = (headers) => { return Option.none() } const parts = headers["b3"].split("-") - if (parts.length < 2) { + if (parts.length < 2 || isB3Ids(parts[0], parts[1]) === false) { return Option.none() } return Option.some(Tracer.externalSpan({ @@ -116,7 +116,10 @@ export const b3: FromHeaders = (headers) => { * @since 4.0.0 */ export const xb3: FromHeaders = (headers) => { - if (!(headers["x-b3-traceid"]) || !(headers["x-b3-spanid"])) { + if ( + !(headers["x-b3-traceid"]) || !(headers["x-b3-spanid"]) || + isB3Ids(headers["x-b3-traceid"], headers["x-b3-spanid"]) === false + ) { return Option.none() } return Option.some(Tracer.externalSpan({ @@ -130,6 +133,9 @@ export const xb3: FromHeaders = (headers) => { const w3cTraceId = /^[0-9a-f]{32}$/i const w3cSpanId = /^[0-9a-f]{16}$/i +const b3TraceId = /^(?:[0-9a-f]{16}|[0-9a-f]{32})$/i +const isB3Ids = (traceId: string, spanId: string): boolean => b3TraceId.test(traceId) && w3cSpanId.test(spanId) + /** * Decodes an external span safely from the W3C `traceparent` header. * diff --git a/repos/effect/packages/effect/src/http/Mime.ts b/packages/effect/src/http/Mime.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Mime.ts rename to packages/effect/src/http/Mime.ts diff --git a/repos/effect/packages/effect/src/http/Multipart.ts b/packages/effect/src/http/Multipart.ts similarity index 97% rename from repos/effect/packages/effect/src/http/Multipart.ts rename to packages/effect/src/http/Multipart.ts index bc6372a02..333ea65f5 100644 --- a/repos/effect/packages/effect/src/http/Multipart.ts +++ b/packages/effect/src/http/Multipart.ts @@ -689,6 +689,22 @@ const defaultWriteFile = (path: string, file: File) => (fs) => Effect.mapError(Stream.run(file.content, fs.sink(path)), toMultipartError) ) +const maxPersistedFileNameBytes = 128 +const fileNameEncoder = new TextEncoder() + +// Keeps the trailing 128 UTF-8 bytes without splitting code points. Empty and dot names fall back to "file". +const persistedFileName = (baseName: string): string => { + const codePoints = Array.from(baseName) + let fileName = "" + let bytes = 0 + for (let i = codePoints.length - 1; i >= 0; i--) { + bytes += fileNameEncoder.encode(codePoints[i]).length + if (bytes > maxPersistedFileNameBytes) break + fileName = codePoints[i] + fileName + } + return fileName === "" || fileName === "." || fileName === ".." ? "file" : fileName +} + /** * Runs a channel of byte chunks and collects all output into a single * `Uint8Array`. @@ -748,7 +764,7 @@ export const toPersisted = ( return Effect.void } const file = part - const fileName = path_.basename(file.name).slice(-128) + const fileName = persistedFileName(path_.basename(file.name)) let path = path_.join(dir, fileName) while (usedPaths.has(path)) { path = path_.join(dir, `${fileIndex++}-${fileName}`) diff --git a/repos/effect/packages/effect/src/http/MultipartParser.ts b/packages/effect/src/http/MultipartParser.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser.ts rename to packages/effect/src/http/MultipartParser.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/HeadersParser.ts b/packages/effect/src/http/MultipartParser/HeadersParser.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/HeadersParser.ts rename to packages/effect/src/http/MultipartParser/HeadersParser.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/Search.ts b/packages/effect/src/http/MultipartParser/Search.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/Search.ts rename to packages/effect/src/http/MultipartParser/Search.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/contentType.ts b/packages/effect/src/http/MultipartParser/internal/contentType.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/contentType.ts rename to packages/effect/src/http/MultipartParser/internal/contentType.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/headers.ts b/packages/effect/src/http/MultipartParser/internal/headers.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/headers.ts rename to packages/effect/src/http/MultipartParser/internal/headers.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/multipart.ts b/packages/effect/src/http/MultipartParser/internal/multipart.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/multipart.ts rename to packages/effect/src/http/MultipartParser/internal/multipart.ts diff --git a/repos/effect/packages/effect/src/http/MultipartParser/internal/search.ts b/packages/effect/src/http/MultipartParser/internal/search.ts similarity index 100% rename from repos/effect/packages/effect/src/http/MultipartParser/internal/search.ts rename to packages/effect/src/http/MultipartParser/internal/search.ts diff --git a/repos/effect/packages/effect/src/http/Template.ts b/packages/effect/src/http/Template.ts similarity index 100% rename from repos/effect/packages/effect/src/http/Template.ts rename to packages/effect/src/http/Template.ts diff --git a/repos/effect/packages/effect/src/http/Url.ts b/packages/effect/src/http/Url.ts similarity index 98% rename from repos/effect/packages/effect/src/http/Url.ts rename to packages/effect/src/http/Url.ts index 63c418160..7c0c9b859 100644 --- a/repos/effect/packages/effect/src/http/Url.ts +++ b/packages/effect/src/http/Url.ts @@ -14,6 +14,7 @@ import * as Data from "../Data.ts" import { dual } from "../Function.ts" import * as Redacted from "../Redacted.ts" import * as Result from "../Result.ts" +import * as internal from "./internal/urlParams.ts" import * as UrlParams from "./UrlParams.ts" /** @@ -49,7 +50,7 @@ export const make = ( for (let i = 0; i < params.params.length; i++) { const [key, value] = params.params[i] if (value !== undefined) { - urlInstance.searchParams.append(key, value) + urlInstance.searchParams.append(key, internal.unwrap(value)) } } if (hash !== undefined) { diff --git a/repos/effect/packages/effect/src/http/UrlParams.ts b/packages/effect/src/http/UrlParams.ts similarity index 60% rename from repos/effect/packages/effect/src/http/UrlParams.ts rename to packages/effect/src/http/UrlParams.ts index a3b8d078e..d5ce11c84 100644 --- a/repos/effect/packages/effect/src/http/UrlParams.ts +++ b/packages/effect/src/http/UrlParams.ts @@ -10,6 +10,7 @@ * @since 4.0.0 */ import * as Arr from "../Array.ts" +import * as Context from "../Context.ts" import * as Equal from "../Equal.ts" import * as Equ from "../Equivalence.ts" import { dual } from "../Function.ts" @@ -21,7 +22,11 @@ import * as Option from "../Option.ts" import type { Pipeable } from "../Pipeable.ts" import { hasProperty } from "../Predicate.ts" import type { ReadonlyRecord } from "../Record.ts" +import * as Redactable from "../Redactable.ts" +import * as Redacted from "../Redacted.ts" import * as Tuple from "../Tuple.ts" +import * as Headers from "./Headers.ts" +import * as internal from "./internal/urlParams.ts" const TypeId = "~effect/http/UrlParams" @@ -30,16 +35,19 @@ const TypeId = "~effect/http/UrlParams" * * **Details** * - * Parameters are stored as ordered string key-value pairs and can contain multiple - * values for the same key. + * Parameters are ordered key-value pairs, and a key can repeat. Each value is a + * string or a `Redacted` string, which is sent unwrapped. Inspection redacts + * `Redacted` values and values whose names match `CurrentRedactedNames`. * * @stability unstable * @category models * @since 4.0.0 */ -export interface UrlParams extends Pipeable, Inspectable, Iterable { +export interface UrlParams + extends Pipeable, Inspectable, Redactable.Redactable, Iterable +{ readonly [TypeId]: typeof TypeId - readonly params: ReadonlyArray + readonly params: ReadonlyArray } /** @@ -74,7 +82,11 @@ type CoercibleRecordInput = CoercibleRecord & { } /** - * Primitive value that can be converted into a URL parameter string. + * Value that can be converted into a URL parameter string. + * + * **Details** + * + * `Redacted` values are stored wrapped and sent unwrapped. * * **Gotchas** * @@ -84,7 +96,7 @@ type CoercibleRecordInput = CoercibleRecord & { * @category models * @since 4.0.0 */ -export type Coercible = string | number | bigint | boolean | null | undefined +export type Coercible = string | number | bigint | boolean | null | undefined | Redacted.Redacted /** * @stability unstable @@ -118,12 +130,15 @@ const Proto = { [Symbol.iterator](this: UrlParams) { return this.params[Symbol.iterator]() }, - toJSON(this: UrlParams): unknown { + [Redactable.symbolRedactable](this: UrlParams, context: Context.Context): unknown { return { _id: "UrlParams", - params: Object.fromEntries(this.params) + params: Object.fromEntries(redact(this, Context.get(context, CurrentRedactedNames))) } }, + toJSON(this: UrlParams): unknown { + return Redactable.redact(this) + }, [Equal.symbol](this: UrlParams, that: UrlParams): boolean { return Equivalence(this, that) }, @@ -133,7 +148,7 @@ const Proto = { } /** - * Creates `UrlParams` from ordered string key-value pairs. + * Creates `UrlParams` from ordered key-value pairs. * * **Details** * @@ -143,7 +158,7 @@ const Proto = { * @category constructors * @since 4.0.0 */ -export const make = (params: ReadonlyArray): UrlParams => { +export const make = (params: ReadonlyArray): UrlParams => { const self = Object.create(Proto) self.params = params return self @@ -156,6 +171,7 @@ export const make = (params: ReadonlyArray): UrlParam * * Primitive values are converted to strings, arrays produce repeated parameters, * nested records use bracket notation, and `undefined` values are omitted. + * `Redacted` values are kept as-is. * * @stability unstable * @category constructors @@ -166,42 +182,44 @@ export const fromInput = (input: Input): UrlParams => { return input } const parsed = fromInputNested(input) - const out: Array<[string, string]> = [] + const out: Array<[string, string | Redacted.Redacted]> = [] for (let i = 0; i < parsed.length; i++) { if (Array.isArray(parsed[i][0])) { - const [keys, value] = parsed[i] as [Array, string] + const [keys, value] = parsed[i] as [Array, string | Redacted.Redacted] out.push([`${keys[0]}[${keys.slice(1).join("][")}]`, value]) } else { - out.push(parsed[i] as [string, string]) + out.push(parsed[i] as [string, string | Redacted.Redacted]) } } return make(out) } -const fromInputNested = (input: Input): Array<[string | Array, any]> => { +const fromInputNested = (input: Input): Array<[string | Array, string | Redacted.Redacted]> => { const entries = typeof (input as any)[Symbol.iterator] === "function" ? Arr.fromIterable(input as Iterable) : Object.entries(input) - const out: Array<[string | Array, string]> = [] + const out: Array<[string | Array, string | Redacted.Redacted]> = [] for (const [key, value] of entries) { if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) { if (value[i] !== undefined) { - out.push([key, String(value[i])]) + out.push([key, coerce(value[i])]) } } - } else if (value !== null && typeof value === "object") { + } else if (value !== null && typeof value === "object" && !Redacted.isRedacted(value)) { const nested = fromInputNested(value as CoercibleRecord) for (const [k, v] of nested) { out.push([[key, ...(typeof k === "string" ? [k] : k)], v]) } } else if (value !== undefined) { - out.push([key, String(value)]) + out.push([key, coerce(value)]) } } return out } +const coerce = (value: Coercible): string | Redacted.Redacted => Redacted.isRedacted(value) ? value : String(value) + /** * Provides an order-sensitive `Equivalence` instance for `UrlParams`. * @@ -219,7 +237,7 @@ export const Equivalence: Equ.Equivalence = Equ.make((a, b ) const arrayEquivalence = Arr.makeEquivalence( - Tuple.makeEquivalence([Equ.strictEqual(), Equ.strictEqual()]) + Tuple.makeEquivalence([Equ.strictEqual(), Equal.asEquivalence()]) ) /** @@ -250,7 +268,7 @@ export const getAll: { (self: UrlParams, key: string): ReadonlyArray => Arr.reduce(self.params, [] as Array, (acc, [k, value]) => { if (k === key) { - acc.push(value) + acc.push(internal.unwrap(value)) } return acc }) @@ -279,7 +297,7 @@ export const getFirst: { 2, (self: UrlParams, key: string): Option.Option => Arr.findFirst(self.params, ([k]) => k === key).pipe( - Option.map(([, value]) => value) + Option.map(([, value]) => internal.unwrap(value)) ) ) @@ -304,7 +322,7 @@ export const getLast: { (self: UrlParams, key: string): Option.Option } = dual(2, (self: UrlParams, key: string): Option.Option => Arr.findLast(self.params, ([k]) => k === key).pipe( - Option.map(([, value]) => value) + Option.map(([, value]) => internal.unwrap(value)) )) /** @@ -326,7 +344,7 @@ export const set: { make( Arr.append( Arr.filter(self.params, ([k]) => k !== key), - [key, String(value)] + [key, coerce(value)] ) )) @@ -390,7 +408,7 @@ export const append: { } = dual(3, (self: UrlParams, key: string, value: Coercible): UrlParams => make(Arr.append( self.params, - [key, String(value)] + [key, coerce(value)] ))) /** @@ -424,11 +442,16 @@ export const remove: { /** * Serializes `UrlParams` to a URL query string without a leading question mark. * + * **Gotchas** + * + * The output contains the unwrapped values of `Redacted` parameters. + * * @stability unstable * @category converting * @since 4.0.0 */ -export const toString = (input: Input): string => new URLSearchParams(fromInput(input).params as any).toString() +export const toString = (input: Input): string => + new URLSearchParams(fromInput(input).params.map(([key, value]) => [key, internal.unwrap(value)])).toString() /** * Builds a `Record` containing all the key-value pairs in the given `UrlParams` @@ -455,7 +478,8 @@ export const toString = (input: Input): string => new URLSearchParams(fromInput( */ export const toRecord = (self: UrlParams): Record> => { const out: Record> = {} - for (const [k, value] of self.params) { + for (const [k, v] of self.params) { + const value = internal.unwrap(v) if (!Object.hasOwn(out, k)) { InternalRecord.assignProperty(out, k, value) } else { @@ -484,3 +508,159 @@ export const toRecord = (self: UrlParams): Record ReadonlyRecord> = toRecord as any + +/** + * Returns the parameter pairs with selected values wrapped in `Redacted`. + * + * **Details** + * + * Keeps `Redacted` values and wraps values whose names match `isRedactedName`, + * preserving order and repeated names. + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const redact: { + ( + key: string | RegExp | ReadonlyArray + ): (self: UrlParams) => Array<[string, string | Redacted.Redacted]> + ( + self: UrlParams, + key: string | RegExp | ReadonlyArray + ): Array<[string, string | Redacted.Redacted]> +} = dual( + 2, + ( + self: UrlParams, + key: string | RegExp | ReadonlyArray + ): Array<[string, string | Redacted.Redacted]> => { + const patterns: ReadonlyArray = Array.isArray(key) ? key : [key as string | RegExp] + return self.params.map(([name, value]) => [ + name, + typeof value === "string" && isRedactedName(name, patterns) ? Redacted.make(value) : value + ]) + } +) + +/** + * Checks whether a parameter name matches one of the redaction patterns. + * + * **Details** + * + * String patterns match case-insensitively; regular expressions test the name. + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const isRedactedName: (name: string, patterns: ReadonlyArray) => boolean = + Headers.isRedactedName + +/** + * Redacts URL userinfo and the values of matching query parameters. + * + * **Details** + * + * Replaces userinfo with `REDACTED:REDACTED` and query values with `REDACTED` + * when their decoded names match `isRedactedName`. Preserves all other text. + * When names are omitted, uses the current fiber's `CurrentRedactedNames`, + * falling back to the defaults outside a fiber. + * + * **Example** (Redacting a signed URL) + * + * ```ts import.meta.vitest + * import { UrlParams } from "effect/http" + * + * const url = UrlParams.redactUrl("https://bucket.test/file?Signature=abc&size=1") + * url // => "https://bucket.test/file?Signature=REDACTED&size=1" + * ``` + * + * @stability unstable + * @category combinators + * @since 4.0.0 + */ +export const redactUrl = ( + url: string, + redactedNames: ReadonlyArray = currentRedactedNames() +): string => { + if (url.includes("@")) url = redactUserinfo(url) + const queryStart = url.indexOf("?") + if (queryStart === -1 || redactedNames.length === 0) return url + const hashStart = url.indexOf("#") + if (hashStart !== -1 && hashStart < queryStart) return url + const queryEnd = hashStart === -1 ? url.length : hashStart + return url.slice(0, queryStart + 1) + redactQuery(url.slice(queryStart + 1, queryEnd), redactedNames) + + url.slice(queryEnd) +} + +const authorityStart = /^(?:(https?|ftp|wss?):[/\\]*|[a-z][a-z\d+.-]*:\/\/|[/\\]{2,})/i +const specialAuthorityEnd = /[/\\?#]/ +const authorityEnd = /[/?#]/ + +// Normalize only to locate userinfo; offsets preserve the original URL spelling. +const redactUserinfo = (url: string): string => { + let normalized = "" + const offsets: Array = [] + for (let i = 0; i < url.length; i++) { + const code = url.charCodeAt(i) + if (code === 9 || code === 10 || code === 13 || (normalized === "" && code <= 32)) continue + normalized += url[i] + offsets.push(i) + } + const authority = authorityStart.exec(normalized) + if (authority === null) return url + const start = authority[0].length + const special = authority[1] !== undefined || !authority[0].includes(":") + const rest = normalized.slice(start) + const end = rest.search(special ? specialAuthorityEnd : authorityEnd) + const userinfoEnd = (end === -1 ? rest : rest.slice(0, end)).lastIndexOf("@") + return userinfoEnd === -1 + ? url + : url.slice(0, offsets[start]) + "REDACTED:REDACTED" + url.slice(offsets[start + userinfoEnd]) +} + +const redactQuery = (query: string, redactedNames: ReadonlyArray): string => + query.split("&").map((part) => { + const index = part.indexOf("=") + return index !== -1 && isRedactedName(decodeQueryKey(part.slice(0, index)), redactedNames) + ? part.slice(0, index + 1) + "REDACTED" + : part + }).join("&") + +const decodeQueryKey = (key: string): string => { + if (!key.includes("%") && !key.includes("+")) return key + try { + return decodeURIComponent(key.replace(/\+/g, " ")) + } catch { + return key + } +} + +const currentRedactedNames = (): ReadonlyArray => + Context.get((globalThis as any)[Redactable.currentFiberTypeId]?.context ?? Context.empty(), CurrentRedactedNames) + +/** + * Query parameter names or patterns to redact in inspection, spans and errors. + * + * **Details** + * + * Defaults include `AWSAccessKeyId`, `Signature`, `sig` and `X-Goog-Signature`. + * A custom list replaces the defaults. Matching uses `isRedactedName`. + * Redaction leaves request URLs and parameter values unchanged. `Redacted` + * values are redacted regardless of this list. + * + * @stability unstable + * @category services + * @since 4.0.0 + */ +export const CurrentRedactedNames = Context.Reference< + ReadonlyArray +>("effect/http/UrlParams/CurrentRedactedNames", { + defaultValue: () => [ + "AWSAccessKeyId", + "Signature", + "sig", + "X-Goog-Signature" + ] +}) diff --git a/repos/effect/packages/effect/src/http/index.ts b/packages/effect/src/http/index.ts similarity index 100% rename from repos/effect/packages/effect/src/http/index.ts rename to packages/effect/src/http/index.ts diff --git a/repos/effect/packages/effect/src/http/internal/compression.ts b/packages/effect/src/http/internal/compression.ts similarity index 92% rename from repos/effect/packages/effect/src/http/internal/compression.ts rename to packages/effect/src/http/internal/compression.ts index d118a6faf..0b1cef8be 100644 --- a/repos/effect/packages/effect/src/http/internal/compression.ts +++ b/packages/effect/src/http/internal/compression.ts @@ -5,6 +5,9 @@ import type * as Headers from "../Headers.ts" import * as HttpBody from "../HttpBody.ts" import type { Compression, CompressionAlgorithm, CompressionOptions } from "../HttpPlatform.ts" import * as Response from "../HttpServerResponse.ts" +import { compressionTransformWeb, singleChunkStream } from "./compressionBody.ts" + +export { compressionTransformWeb } from "./compressionBody.ts" /** * @internal @@ -41,15 +44,6 @@ export const wrapCompression = (impl: Compression): Compression => ({ } }) -/** - * @internal - */ -export const compressionTransformWeb = - (format: string) => (stream: ReadableStream): ReadableStream => - stream.pipeThrough( - new CompressionStream(format as CompressionFormat) as unknown as ReadableWritablePair - ) - /** * @internal */ @@ -117,14 +111,6 @@ const streamBody = ( HttpBody.stream(Stream.fromReadableStream({ evaluate, onError: identity }), contentType) ) -const singleChunkStream = (data: Uint8Array): ReadableStream => - new ReadableStream({ - start(controller) { - controller.enqueue(data) - controller.close() - } - }) - const rawReadableStream = (raw: unknown): ReadableStream | undefined => { if (typeof ReadableStream !== "undefined" && raw instanceof ReadableStream) { return raw diff --git a/packages/effect/src/http/internal/compressionBody.ts b/packages/effect/src/http/internal/compressionBody.ts new file mode 100644 index 000000000..800475644 --- /dev/null +++ b/packages/effect/src/http/internal/compressionBody.ts @@ -0,0 +1,56 @@ +import * as Effect from "../../Effect.ts" +import { identity } from "../../Function.ts" +import * as Stream from "../../Stream.ts" +import * as HttpBody from "../HttpBody.ts" +import type { CompressionAlgorithm, CompressionOptions } from "../HttpClientRequest.ts" + +/** + * @internal + */ +export const compressionTransformWeb = + (format: string) => (stream: ReadableStream): ReadableStream => + stream.pipeThrough( + new CompressionStream(format as CompressionFormat) as unknown as ReadableWritablePair + ) + +/** + * @internal + */ +export const singleChunkStream = (data: Uint8Array): ReadableStream => + new ReadableStream({ + start(controller) { + controller.enqueue(data) + controller.close() + } + }) + +/** + * @internal + */ +export const compressBody = ( + body: HttpBody.HttpBody, + algorithm: CompressionAlgorithm, + options?: CompressionOptions | undefined +): Effect.Effect => { + if (body._tag !== "Uint8Array" && body._tag !== "Stream") { + return Effect.succeed(body) + } + if (body.contentLength !== undefined && body.contentLength < (options?.minSize ?? 1024)) { + return Effect.succeed(body) + } + if (body._tag === "Uint8Array") { + return Effect.map( + Effect.promise(() => + new globalThis.Response(compressionTransformWeb(algorithm)(singleChunkStream(body.body))).arrayBuffer() + ), + (buffer) => HttpBody.uint8Array(new Uint8Array(buffer), body.contentType) + ) + } + return Effect.succeed(HttpBody.stream( + Stream.fromReadableStream({ + evaluate: () => compressionTransformWeb(algorithm)(Stream.toReadableStream(body.stream)), + onError: identity + }), + body.contentType + )) +} diff --git a/packages/effect/src/http/internal/decompression.ts b/packages/effect/src/http/internal/decompression.ts new file mode 100644 index 000000000..4c47949f1 --- /dev/null +++ b/packages/effect/src/http/internal/decompression.ts @@ -0,0 +1,226 @@ +import * as ByteSize from "../../ByteSize.ts" +import * as Effect from "../../Effect.ts" +import * as Inspectable from "../../Inspectable.ts" +import type * as Option from "../../Option.ts" +import type { ReadonlyRecord } from "../../Record.ts" +import type * as Schema from "../../Schema.ts" +import type * as Socket from "../../socket/Socket.ts" +import * as Stream from "../../Stream.ts" +import * as Headers from "../Headers.ts" +import * as HttpIncomingMessage from "../HttpIncomingMessage.ts" +import type { HttpMethod } from "../HttpMethod.ts" +import { HttpServerError, RequestParseError } from "../HttpServerError.ts" +import * as HttpServerRequest from "../HttpServerRequest.ts" +import * as Multipart from "../Multipart.ts" +import * as UrlParams from "../UrlParams.ts" + +/** + * @internal + */ +export type Coding = "gzip" | "deflate" + +const defaultMaxDecompressedSize = ByteSize.mebibytes(10) + +/** + * Parses a `Content-Encoding` header into the codings to undo, in the order + * they were applied. `identity` members are dropped. Returns `undefined` if + * any member is unsupported. + * + * @internal + */ +export const parseContentEncoding = (header: string): ReadonlyArray | undefined => { + const codings: Array = [] + for (const member of header.split(",")) { + const coding = member.trim().toLowerCase() + if (coding === "" || coding === "identity") continue + if (coding !== "gzip" && coding !== "deflate") return undefined + codings.push(coding) + } + return codings +} + +/** + * Wraps a request so that its body accessors yield the decompressed body. + * Decompression happens lazily, when a body accessor is read. + * + * @internal + */ +export class DecompressedRequest extends Inspectable.Class implements HttpServerRequest.HttpServerRequest { + readonly [HttpServerRequest.TypeId]: typeof HttpServerRequest.TypeId + readonly [HttpIncomingMessage.TypeId]: typeof HttpIncomingMessage.TypeId + readonly inner: HttpServerRequest.HttpServerRequest + readonly codings: ReadonlyArray + readonly maxDecompressedSize: ByteSize.ByteSize | undefined + readonly headers: Headers.Headers + + constructor( + inner: HttpServerRequest.HttpServerRequest, + codings: ReadonlyArray, + maxDecompressedSize: ByteSize.ByteSize | undefined + ) { + super() + this[HttpServerRequest.TypeId] = HttpServerRequest.TypeId + this[HttpIncomingMessage.TypeId] = HttpIncomingMessage.TypeId + this.inner = inner + this.codings = codings + this.maxDecompressedSize = maxDecompressedSize + this.headers = Headers.removeMany(inner.headers, ["content-encoding", "content-length"]) + } + + toJSON(): unknown { + return HttpIncomingMessage.inspect(this, { + _id: "HttpServerRequest", + method: this.method, + url: this.originalUrl + }) + } + + modify( + options: { + readonly url?: string + readonly headers?: Headers.Headers + readonly remoteAddress?: Option.Option + } + ): HttpServerRequest.HttpServerRequest { + return new DecompressedRequest(this.inner.modify(options), this.codings, this.maxDecompressedSize) + } + + get source(): object { + return this.inner.source + } + + get url(): string { + return this.inner.url + } + + get originalUrl(): string { + return this.inner.originalUrl + } + + get method(): HttpMethod { + return this.inner.method + } + + get cookies(): ReadonlyRecord { + return this.inner.cookies + } + + get remoteAddress(): Option.Option { + return this.inner.remoteAddress + } + + get upgrade(): Effect.Effect { + return this.inner.upgrade + } + + // Platform accessors such as `NodeHttpServerRequest.toServerResponse` read + // the response handle from the request. + get response(): unknown { + return (this.inner as any).response + } + + get stream(): Stream.Stream { + return Stream.unwrap(Effect.withFiber((fiber) => { + const limit = this.maxDecompressedSize ?? fiber.getRef(HttpIncomingMessage.MaxBodySize) ?? + defaultMaxDecompressedSize + return Effect.succeed(Stream.fromReadableStream({ + evaluate: () => { + let body = Stream.toReadableStreamWith(this.inner.stream, fiber.context) + for (let i = this.codings.length - 1; i >= 0; i--) { + body = body.pipeThrough( + new DecompressionStream(this.codings[i]) as unknown as ReadableWritablePair + ) + } + let size = 0 + return body.pipeThrough( + new TransformStream({ + transform: (chunk, controller) => { + size += chunk.byteLength + if (size > limit) { + controller.error( + this.parseError(undefined, `Decompressed request body exceeds ${limit} bytes`, "BodyTooLarge") + ) + } else { + controller.enqueue(chunk) + } + } + }) + ) + }, + onError: (cause) => cause instanceof HttpServerError ? cause : this.parseError(cause) + })) + })) + } + + private bytesEffect: Effect.Effect | undefined + private get bytes(): Effect.Effect { + return this.bytesEffect ??= Effect.runSync(Effect.cached(Stream.mkUint8Array(this.stream))) + } + + get text(): Effect.Effect { + return Effect.map(this.bytes, (bytes) => textDecoder.decode(bytes)) + } + + get json(): Effect.Effect { + return Effect.flatMap(this.text, (text) => + Effect.try({ + try: () => text === "" ? null : JSON.parse(text) as Schema.Json, + catch: (cause) => this.parseError(cause) + })) + } + + get urlParamsBody(): Effect.Effect { + return Effect.flatMap(this.text, (text) => + Effect.try({ + try: () => UrlParams.fromInput(new URLSearchParams(text)), + catch: (cause) => this.parseError(cause) + })) + } + + get arrayBuffer(): Effect.Effect { + return Effect.map( + this.bytes, + (bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer + ) + } + + get multipartStream(): Stream.Stream { + return Stream.pipeThroughChannel( + Stream.mapError( + this.stream, + (cause) => Multipart.MultipartError.fromReason(multipartReasons.get(cause) ?? "InternalError", cause) + ), + Multipart.makeChannel(this.headers) + ) + } + + private multipartEffect: HttpServerRequest.HttpServerRequest["multipart"] | undefined + get multipart(): HttpServerRequest.HttpServerRequest["multipart"] { + return this.multipartEffect ??= Effect.runSync(Effect.cached(Multipart.toPersisted(this.multipartStream))) + } + + private parseError( + cause: unknown, + description?: string, + multipartReason: MultipartReason = "Parse" + ): HttpServerError { + const error = new HttpServerError({ + reason: new RequestParseError({ + request: this, + ...(description === undefined ? undefined : { description }), + ...(cause === undefined ? undefined : { cause }) + }) + }) + multipartReasons.set(error, multipartReason) + return error + } +} + +const textDecoder = new TextDecoder() + +type MultipartReason = "Parse" | "BodyTooLarge" + +// Failures caused by the client's data, keyed to the multipart reason they map +// to. Transport errors from the inner request are absent and map to +// `InternalError`. +const multipartReasons = new WeakMap() diff --git a/repos/effect/packages/effect/src/http/internal/headers.ts b/packages/effect/src/http/internal/headers.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/headers.ts rename to packages/effect/src/http/internal/headers.ts diff --git a/repos/effect/packages/effect/src/http/internal/httpBody.ts b/packages/effect/src/http/internal/httpBody.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/httpBody.ts rename to packages/effect/src/http/internal/httpBody.ts diff --git a/repos/effect/packages/effect/src/http/internal/mimeTypes.ts b/packages/effect/src/http/internal/mimeTypes.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/mimeTypes.ts rename to packages/effect/src/http/internal/mimeTypes.ts diff --git a/repos/effect/packages/effect/src/http/internal/preResponseHandler.ts b/packages/effect/src/http/internal/preResponseHandler.ts similarity index 100% rename from repos/effect/packages/effect/src/http/internal/preResponseHandler.ts rename to packages/effect/src/http/internal/preResponseHandler.ts diff --git a/repos/effect/packages/effect/src/http/internal/tracing.ts b/packages/effect/src/http/internal/tracing.ts similarity index 62% rename from repos/effect/packages/effect/src/http/internal/tracing.ts rename to packages/effect/src/http/internal/tracing.ts index 138794d87..4803a8c2c 100644 --- a/repos/effect/packages/effect/src/http/internal/tracing.ts +++ b/packages/effect/src/http/internal/tracing.ts @@ -1,5 +1,6 @@ import type * as Tracer from "../../Tracer.ts" import * as Headers from "../Headers.ts" +import * as UrlParams from "../UrlParams.ts" const defaultPorts: Record = { "http:": 80, @@ -9,16 +10,17 @@ const defaultPorts: Record = { } /** - * Records the `url.*` and `server.*` attributes for a request URL. + * Records `url.*` and `server.*` attributes with URL redaction. * * @internal */ export const addUrlAttributes = ( span: Tracer.Span, - url: URL + url: URL, + redactedNames: ReadonlyArray ): void => { - const query = url.search.slice(1) - span.attribute("url.full", url.toString()) + const query = UrlParams.redactUrl(url.search, redactedNames).slice(1) + span.attribute("url.full", UrlParams.redactUrl(url.href, redactedNames)) span.attribute("url.path", url.pathname) if (query !== "") { span.attribute("url.query", query) @@ -34,6 +36,21 @@ export const addUrlAttributes = ( } } +/** + * Adds the names of `Redacted` parameters to the redacted names. + * + * @internal + */ +export const redactedNames = ( + urlParams: UrlParams.UrlParams, + redactedNames: ReadonlyArray +): ReadonlyArray => { + const keys = urlParams.params.filter(([, value]) => typeof value !== "string").map(([key]) => key) + return keys.length === 0 + ? redactedNames + : [...redactedNames, ...new URLSearchParams(keys.map((key) => [key, ""])).keys()] +} + /** @internal */ export const addHeaderAttributes = ( span: Tracer.Span, diff --git a/packages/effect/src/http/internal/urlParams.ts b/packages/effect/src/http/internal/urlParams.ts new file mode 100644 index 000000000..9eaf19ee2 --- /dev/null +++ b/packages/effect/src/http/internal/urlParams.ts @@ -0,0 +1,5 @@ +import * as Redacted from "../../Redacted.ts" + +/** @internal */ +export const unwrap = (value: string | Redacted.Redacted): string => + typeof value === "string" ? value : Redacted.value(value) diff --git a/repos/effect/packages/effect/src/index.ts b/packages/effect/src/index.ts similarity index 100% rename from repos/effect/packages/effect/src/index.ts rename to packages/effect/src/index.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/array.ts b/packages/effect/src/internal/arbitrary/array.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/array.ts rename to packages/effect/src/internal/arbitrary/array.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/bigDecimal.ts b/packages/effect/src/internal/arbitrary/bigDecimal.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/bigDecimal.ts rename to packages/effect/src/internal/arbitrary/bigDecimal.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/bigInt.ts b/packages/effect/src/internal/arbitrary/bigInt.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/bigInt.ts rename to packages/effect/src/internal/arbitrary/bigInt.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/model.ts b/packages/effect/src/internal/arbitrary/model.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/model.ts rename to packages/effect/src/internal/arbitrary/model.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/regexp.ts b/packages/effect/src/internal/arbitrary/regexp.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/regexp.ts rename to packages/effect/src/internal/arbitrary/regexp.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/runner.ts b/packages/effect/src/internal/arbitrary/runner.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/runner.ts rename to packages/effect/src/internal/arbitrary/runner.ts diff --git a/repos/effect/packages/effect/src/internal/arbitrary/schema.ts b/packages/effect/src/internal/arbitrary/schema.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/arbitrary/schema.ts rename to packages/effect/src/internal/arbitrary/schema.ts diff --git a/repos/effect/packages/effect/src/internal/array.ts b/packages/effect/src/internal/array.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/array.ts rename to packages/effect/src/internal/array.ts diff --git a/repos/effect/packages/effect/src/internal/core.ts b/packages/effect/src/internal/core.ts similarity index 96% rename from repos/effect/packages/effect/src/internal/core.ts rename to packages/effect/src/internal/core.ts index 04d6eddc4..9fc2cbb69 100644 --- a/repos/effect/packages/effect/src/internal/core.ts +++ b/packages/effect/src/internal/core.ts @@ -321,6 +321,26 @@ export const causeCombine: { } ) +/** + * Appends the interrupts from `interrupts` that `self` does not contain yet. + * Unlike `causeCombine`, it never calls hash or equality methods, which can be + * user-defined, random, or throwing. + * + * @internal + */ +export const causeAddInterrupts = (self: Cause.Cause, interrupts: Cause.Cause): Cause.Cause => { + const reasons: Array> = self.reasons.slice() + for (const reason of interrupts.reasons) { + if ( + isInterruptReason(reason) && + !reasons.some((r) => isInterruptReason(r) && r.fiberId === reason.fiberId && r.annotations === reason.annotations) + ) { + reasons.push(reason) + } + } + return reasons.length === self.reasons.length ? self : new CauseImpl(reasons) +} + /** @internal */ export const causeEmpty: Cause.Cause = new CauseImpl([]) @@ -600,7 +620,7 @@ export const exitFailCause: (cause: Cause.Cause) => Exit.Exit = if (skippedHandler) { cause = causeFromReasons(cause.reasons.filter((reason) => reason._tag !== "Fail")) } - cause = causeCombine(cause, interruptedCause) + cause = causeAddInterrupts(cause, interruptedCause) annotated = true } return cont diff --git a/repos/effect/packages/effect/src/internal/count.ts b/packages/effect/src/internal/count.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/count.ts rename to packages/effect/src/internal/count.ts diff --git a/repos/effect/packages/effect/src/internal/dateTime.ts b/packages/effect/src/internal/dateTime.ts similarity index 98% rename from repos/effect/packages/effect/src/internal/dateTime.ts rename to packages/effect/src/internal/dateTime.ts index 7ba567f98..48d9d4086 100644 --- a/repos/effect/packages/effect/src/internal/dateTime.ts +++ b/packages/effect/src/internal/dateTime.ts @@ -934,10 +934,6 @@ export const subtractDuration: { mapEpochMillis(self, (millis) => millis - Duration.toMillis(Duration.fromInputUnsafe(duration))) ) -const addMillis = (date: Date, amount: number): void => { - date.setTime(date.getTime() + amount) -} - /** @internal */ export const add: { ( @@ -949,20 +945,26 @@ export const add: { ): A } = dual( 2, - (self: DateTime.DateTime, parts: Partial): DateTime.DateTime => - mutate(self, (date) => { + (self: DateTime.DateTime, parts: Partial): DateTime.DateTime => { + // time units move the instant, calendar units move the wall clock + const shifted = mutateUtc(self, (date) => { if (parts.milliseconds) { - addMillis(date, parts.milliseconds) + date.setTime(date.getTime() + parts.milliseconds) } if (parts.seconds) { - addMillis(date, parts.seconds * 1000) + date.setTime(date.getTime() + parts.seconds * 1000) } if (parts.minutes) { - addMillis(date, parts.minutes * 60 * 1000) + date.setTime(date.getTime() + parts.minutes * 60 * 1000) } if (parts.hours) { - addMillis(date, parts.hours * 60 * 60 * 1000) + date.setTime(date.getTime() + parts.hours * 60 * 60 * 1000) } + }) + if (!parts.days && !parts.weeks && !parts.months && !parts.years) { + return shifted + } + return mutate(shifted, (date) => { if (parts.days) { date.setUTCDate(date.getUTCDate() + parts.days) } @@ -989,6 +991,7 @@ export const add: { } } }) + } ) /** @internal */ diff --git a/repos/effect/packages/effect/src/internal/doNotation.ts b/packages/effect/src/internal/doNotation.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/doNotation.ts rename to packages/effect/src/internal/doNotation.ts diff --git a/repos/effect/packages/effect/src/internal/effect.ts b/packages/effect/src/internal/effect.ts similarity index 99% rename from repos/effect/packages/effect/src/internal/effect.ts rename to packages/effect/src/internal/effect.ts index f6343b78b..fc747b648 100644 --- a/repos/effect/packages/effect/src/internal/effect.ts +++ b/packages/effect/src/internal/effect.ts @@ -49,6 +49,7 @@ import { internalCall } from "../Utils.ts" import type { Primitive } from "./core.ts" import { args, + causeAddInterrupts, causeAnnotate, causeCombine, causeEmpty, @@ -130,9 +131,9 @@ export class Interrupt extends ReasonBase<"Interrupt"> implements Cause.Interrup ) } [Hash.symbol](): number { - return Hash.combine(Hash.string(`${this._tag}:${this.fiberId}`))( - Hash.random(this.annotations) - ) + // Equality compares annotations by reference, so leave them out instead of + // hashing them randomly. + return Hash.string(`${this._tag}:${this.fiberId}`) } } @@ -566,7 +567,7 @@ export class FiberImpl implements Fiber.Fiber { cause = causeAnnotate(cause, annotations) } this._interruptedCause = this._interruptedCause - ? causeCombine(this._interruptedCause, cause) + ? causeAddInterrupts(this._interruptedCause, cause) : cause if (this.interruptible) { if (this._running) { @@ -1507,13 +1508,19 @@ const succeedPayload = function(this: { readonly payload: any }, _value: unknown // V8 includes the property name of a stored continuation in its stack trace. // Other engines need an explicit frame for the stack cleaner to cut at. const continuationMarksStack = (() => { + const limit = getStackTraceLimit() + if (limit === 0) return false const marker = "~effect/Effect/stackProbe" const probe = { [marker]: function stackProbe() { - return new Error().stack + return new Error() } } - return probe[marker]()?.includes("[as " + marker + "]") === true + // Only the probe frame is needed to detect the continuation property name. + setStackTraceLimit(1) + const error = probe[marker]() + setStackTraceLimit(limit) + return error.stack?.includes("[as " + marker + "]") === true })() const mapCont = function(this: { readonly payload: any }, value: any, fiber: FiberImpl) { const f = this.payload @@ -4453,7 +4460,7 @@ export const cachedInvalidateWithTTL: { const infiniteTTL = constant(Infinity) interface CachedRun { - fiber: FiberImpl | undefined + readonly fiber: FiberImpl awaiters: number } @@ -4470,17 +4477,19 @@ const makeCachedUnsafe = ( onExitUnsafe(fiber, () => { // Abandon the run once every caller has left, unless it already finished. if (--run.awaiters > 0 || current !== run) return - // Detach it first so new callers start a fresh run instead of joining - // one that is being interrupted. + // Detach before interruption so new callers start a fresh run. current = undefined - return fiberInterrupt(run.fiber!) + return fiberInterrupt(run.fiber) }) - return fiberJoin(run.fiber!) + return fiberJoin(run.fiber) } return [ withFiber((fiber) => { - if (current !== undefined) return join(fiber, current) + if (current !== undefined) { + // A run joining itself is not an awaiter, so external callers can still abandon it. + return fiber === current.fiber ? fiberJoin(current.fiber) : join(fiber, current) + } if ( exit !== undefined && (expiresAt === Infinity || fiber.getRef(ClockRef).currentTimeMillisUnsafe() < expiresAt) @@ -4489,10 +4498,11 @@ const makeCachedUnsafe = ( } exit = undefined const clock = fiber.getRef(ClockRef) - const run: CachedRun = { fiber: undefined, awaiters: 0 } + const run: CachedRun = { fiber: new FiberImpl(fiber.context), awaiters: 0 } current = run - run.fiber = forkUnsafe( - fiber, + // Join before evaluating, which may synchronously re-enter and interrupt other awaiters. + const joined = join(fiber, run) + run.fiber.evaluate( onExitPrimitive(self, (exit_) => { // An abandoned run must not overwrite or clear a replacement run. if (current !== run) return @@ -4502,11 +4512,9 @@ const makeCachedUnsafe = ( const duration = ttlMillis(exit_) expiresAt = duration === Infinity ? Infinity : clock.currentTimeMillisUnsafe() + duration exit = exit_ - }), - true, - true + }) as any ) - return run.fiber._exit ?? join(fiber, run) + return run.fiber._exit ?? joined }), sync(() => { exit = undefined diff --git a/repos/effect/packages/effect/src/internal/equal.ts b/packages/effect/src/internal/equal.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/equal.ts rename to packages/effect/src/internal/equal.ts diff --git a/repos/effect/packages/effect/src/internal/errors.ts b/packages/effect/src/internal/errors.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/errors.ts rename to packages/effect/src/internal/errors.ts diff --git a/repos/effect/packages/effect/src/internal/executionPlan.ts b/packages/effect/src/internal/executionPlan.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/executionPlan.ts rename to packages/effect/src/internal/executionPlan.ts diff --git a/repos/effect/packages/effect/src/internal/graph.ts b/packages/effect/src/internal/graph.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/graph.ts rename to packages/effect/src/internal/graph.ts diff --git a/repos/effect/packages/effect/src/internal/graphCsr.ts b/packages/effect/src/internal/graphCsr.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/graphCsr.ts rename to packages/effect/src/internal/graphCsr.ts diff --git a/packages/effect/src/internal/graphql.ts b/packages/effect/src/internal/graphql.ts new file mode 100644 index 000000000..771ae1681 --- /dev/null +++ b/packages/effect/src/internal/graphql.ts @@ -0,0 +1,286 @@ +import { isReadonlyArrayNonEmpty, type NonEmptyReadonlyArray } from "../Array.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import type * as GraphQL from "../graphql/GraphQL.ts" +import type { DocumentTemplate } from "../graphql/GraphQL.ts" +import type { PartialResult } from "../graphql/GraphQLClient.ts" +import { + DecodeError, + EncodeError, + GraphQLClientError, + type GraphQLError, + type Reason, + ResponseError +} from "../graphql/GraphQLClientError.ts" +import type { ExecutionResult, GraphQLProtocol, GraphQLRequest } from "../graphql/GraphQLProtocol.ts" +import * as Schema from "../Schema.ts" + +/** + * Builds a response error with raw data and transport metadata. + * + * @internal + */ +export const responseError = ( + result: ExecutionResult | undefined, + errors: NonEmptyReadonlyArray +): ResponseError => + new ResponseError({ + errors, + ...(result?.data === undefined ? {} : { data: result.data as Schema.Json }), + ...(result?.extensions === undefined ? {} : { extensions: result.extensions }), + ...(result?.status === undefined ? {} : { status: result.status }), + ...(result?.headers === undefined ? {} : { headers: result.headers }) + }) + +/** + * Raw responses for partial results, used by resolvers to preserve error + * metadata. Mock client results have no entry. + * + * @internal + */ +export const partialResponses = new WeakMap() + +/** + * Joins two printed pieces, adding a space only where two non-punctuators + * would otherwise touch. This is the generator printer's rule, so joining a + * generated template reproduces the printed document exactly. + */ +const concat = (left: string, right: string): string => + /[\w"-]$/.test(left) && /^[\w"-]/.test(right) ? `${left} ${right}` : left + right + +const concatAll = (pieces: ReadonlyArray): string => pieces.reduce(concat, "") + +/** Renders template parts, prefixing every name (the odd entries). */ +const renderParts = (parts: DocumentTemplate.Parts, prefix: string): string => + parts.map((part, i) => i % 2 === 1 ? prefix + part : part).join("") + +const isShared = (fragment: DocumentTemplate.Fragment): boolean => fragment.body.length <= 1 + +/** + * Renders the template once per prefix. The empty prefix renders it as + * written; any other prefix renames variables and per-slice fragments and + * aliases root fields to the prefixed response key. + */ +const render = ( + kind: string, + name: string, + template: DocumentTemplate, + prefixes: ReadonlyArray +): string => + concatAll([ + kind, + name, + template.variables.length === 0 ? "" : `(${ + concatAll( + prefixes.flatMap((prefix) => + template.variables.map((variable) => `$${prefix}${variable.name}:${variable.body}`) + ) + ) + })`, + template.directives === undefined ? "" : renderParts(template.directives, ""), + `{${ + concatAll(prefixes.flatMap((prefix) => + template.selections.map((selection) => { + const key = prefix === "" ? selection.alias : `${prefix}${selection.alias ?? selection.name}` + return `${key === undefined ? "" : `${key}:`}${selection.name ?? ""}${renderParts(selection.body, prefix)}` + }) + )) + }}`, + ...template.fragments.flatMap((fragment) => + (isShared(fragment) ? [""] : prefixes).map((prefix) => + concatAll(["fragment", prefix + fragment.name, renderParts(fragment.body, prefix)]) + ) + ) + ]) + +/** + * Joins a template into the document it was split from. + * + * @internal + */ +export const joinTemplate = (kind: string, name: string, template: DocumentTemplate): string => + render(kind, name, template, [""]) + +/** + * Why the template can't be sliced for any prefixes, if it can't: an operation + * directive references a variable, or a root selection is a fragment spread or + * inline fragment. + * + * @internal + */ +export const templateUnsliceableReason = (template: DocumentTemplate): string | undefined => { + if (template.directives !== undefined && template.directives.length > 1) { + return "its operation directives reference variables" + } + if (template.selections.some((selection) => selection.name === undefined)) { + return "it has a fragment spread or inline fragment at the root" + } + return undefined +} + +/** + * Renders one operation that repeats the template once per prefix. Each + * slice's variables, root response keys and per-slice fragments are renamed + * with its prefix; operation directives and shared fragments appear once. + * Prefixes must start a GraphQL name, and none may be a prefix of another. + * + * Throws rather than producing an invalid document when the template is + * unsliceable (see {@link templateUnsliceableReason}) or a renamed fragment + * would take a shared fragment's name. + * + * @internal + */ +export const renderSlices = ( + kind: string, + name: string, + template: DocumentTemplate, + prefixes: NonEmptyReadonlyArray +): string => { + const shared = new Set(template.fragments.filter(isShared).map((fragment) => fragment.name)) + const collision = template.fragments + .flatMap((fragment) => isShared(fragment) ? [] : prefixes.map((prefix) => prefix + fragment.name)) + .find((renamed) => shared.has(renamed)) + const reason = templateUnsliceableReason(template) ?? + (collision === undefined ? undefined : `fragment ${collision} would collide with a shared fragment`) + if (reason !== undefined) throw new Error(`Cannot render slices of operation ${name}: ${reason}`) + return render(kind, name, template, prefixes) +} + +/** + * Forces POST for alias batches to avoid GET URL length limits. + * + * @internal + */ +export const ForcePost = Context.Reference("effect/graphql/ForcePost", { defaultValue: () => false }) + +/** + * The URL a resolver batch was grouped by, and the protocol `url` that + * resolved it, so that transport sends the batch there without resolving it + * again. + * + * @internal + */ +export type ResolvedUrl = { + readonly resolve: GraphQLProtocol["Service"]["url"] + readonly url: string +} | undefined + +/** + * Carries a {@link ResolvedUrl} from a resolver batch to the transport. The + * client clears it while middleware run, so calls they make resolve their + * own URL. + * + * @internal + */ +export const ResolvedUrl = Context.Reference("effect/graphql/ResolvedUrl", { + defaultValue: () => undefined +}) + +/** + * JSON with object keys sorted, so equal JSON values give equal strings. + * + * @internal + */ +export const canonicalJson = (value: unknown): string => + JSON.stringify(value, (_, value) => + value !== null && typeof value === "object" && !Array.isArray(value) + ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, value[key]])) + : value) + +/** + * Shared variable encoding, result decoding and client error mapping. + * + * @internal + */ +export const makeCodec = (operation: Op) => { + const fail = (reason: Reason) => new GraphQLClientError({ operation: operation.name, reason }) + const schemaFail = (make: (description: string) => Reason) => (error: Schema.SchemaError) => + Effect.fail(fail(make(error.message))) + const encode = Schema.encodeEffect(operation.variables) + const decode = Schema.decodeUnknownEffect(operation.result) + + const encodeVariables = ( + variables: GraphQL.Variables | undefined + ): Effect.Effect => + Effect.catch(encode(variables ?? {}), schemaFail((description) => new EncodeError({ description }))) + + const decodeData = ( + data: unknown + ): Effect.Effect, GraphQLClientError, Op["result"]["DecodingServices"]> => + Effect.catch(decode(data), schemaFail((description) => new DecodeError({ description }))) + + // GraphQL requires errors when data is null or missing. Check before decoding, + // since schemas such as Schema.Json accept null. + const failResponse = (result: ExecutionResult): Effect.Effect => { + const errors = result.errors ?? [] + return Effect.fail(fail( + isReadonlyArrayNonEmpty(errors) + ? responseError(result, errors) + : new DecodeError({ description: "data is null or missing without errors" }) + )) + } + + const decodeStrict = ( + result: ExecutionResult + ): Effect.Effect, GraphQLClientError, Op["result"]["DecodingServices"]> => + isReadonlyArrayNonEmpty(result.errors ?? []) || result.data === null || result.data === undefined + ? failResponse(result) + : decodeData(result.data) + + const decodePartial = ( + result: ExecutionResult + ): Effect.Effect>, GraphQLClientError, Op["result"]["DecodingServices"]> => { + if (result.data === null || result.data === undefined) return failResponse(result) + return Effect.map(decodeData(result.data), (data): PartialResult> => { + const partial = { data, errors: result.errors ?? [] } + partialResponses.set(partial, result) + return partial + }) + } + + return { fail, schemaFail, encodeVariables, failResponse, decodeStrict, decodePartial } as const +} + +/** + * Client batch runners (middleware and protocol, using POST) and URLs for + * resolver grouping. Mock and custom methods are not registered. + * + * @internal + */ +export const rawExecutors = new WeakMap Effect.Effect + readonly url: GraphQLProtocol["Service"]["url"] +}>() + +/** + * Splits a batched response, removing prefixes from root keys and error paths. + * Returns undefined if an error matches no slice or data is null or missing. + * + * @internal + */ +export const splitSlices = ( + result: ExecutionResult, + template: DocumentTemplate, + prefixes: ReadonlyArray +): ReadonlyArray | undefined => { + const sliceErrors = prefixes.map((): Array => []) + for (const error of result.errors ?? []) { + const key = error.path?.[0] + // No prefix is a prefix of another, so at most one matches. + const index = typeof key === "string" ? prefixes.findIndex((prefix) => key.startsWith(prefix)) : -1 + if (index === -1) return undefined + sliceErrors[index].push( + { ...error, path: [(key as string).slice(prefixes[index].length), ...error.path!.slice(1)] } as GraphQLError + ) + } + if (result.data === null || result.data === undefined) return undefined + const data = result.data as Record + return prefixes.map((prefix, i) => { + const sliceData: Record = {} + for (const selection of template.selections) { + const key = (selection.alias ?? selection.name)! + if (Object.hasOwn(data, prefix + key)) sliceData[key] = data[prefix + key] + } + return { ...result, data: sliceData, errors: sliceErrors[i] } + }) +} diff --git a/packages/effect/src/internal/graphqlHeaders.ts b/packages/effect/src/internal/graphqlHeaders.ts new file mode 100644 index 000000000..49e1741a3 --- /dev/null +++ b/packages/effect/src/internal/graphqlHeaders.ts @@ -0,0 +1,11 @@ +import * as Headers from "../http/Headers.ts" +import * as Schema from "../Schema.ts" +import * as SchemaGetter from "../SchemaGetter.ts" + +// Keep headers branded at runtime while encoding only their string entries. +export const HeadersSchema = Schema.Record(Schema.String, Schema.String).pipe( + Schema.decodeTo(Schema.declare(Headers.isHeaders), { + decode: SchemaGetter.transform(Headers.fromInput), + encode: SchemaGetter.transform((headers) => ({ ...headers })) + }) +) diff --git a/repos/effect/packages/effect/src/internal/hash.ts b/packages/effect/src/internal/hash.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/hash.ts rename to packages/effect/src/internal/hash.ts diff --git a/repos/effect/packages/effect/src/internal/hashMap.ts b/packages/effect/src/internal/hashMap.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/hashMap.ts rename to packages/effect/src/internal/hashMap.ts diff --git a/repos/effect/packages/effect/src/internal/hashSet.ts b/packages/effect/src/internal/hashSet.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/hashSet.ts rename to packages/effect/src/internal/hashSet.ts diff --git a/repos/effect/packages/effect/src/internal/layer.ts b/packages/effect/src/internal/layer.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/layer.ts rename to packages/effect/src/internal/layer.ts diff --git a/repos/effect/packages/effect/src/internal/matcher.ts b/packages/effect/src/internal/matcher.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/matcher.ts rename to packages/effect/src/internal/matcher.ts diff --git a/repos/effect/packages/effect/src/internal/metric.ts b/packages/effect/src/internal/metric.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/metric.ts rename to packages/effect/src/internal/metric.ts diff --git a/repos/effect/packages/effect/src/internal/option.ts b/packages/effect/src/internal/option.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/option.ts rename to packages/effect/src/internal/option.ts diff --git a/repos/effect/packages/effect/src/internal/persistence.ts b/packages/effect/src/internal/persistence.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/persistence.ts rename to packages/effect/src/internal/persistence.ts diff --git a/repos/effect/packages/effect/src/internal/random.ts b/packages/effect/src/internal/random.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/random.ts rename to packages/effect/src/internal/random.ts diff --git a/repos/effect/packages/effect/src/internal/rcRef.ts b/packages/effect/src/internal/rcRef.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/rcRef.ts rename to packages/effect/src/internal/rcRef.ts diff --git a/repos/effect/packages/effect/src/internal/record.ts b/packages/effect/src/internal/record.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/record.ts rename to packages/effect/src/internal/record.ts diff --git a/repos/effect/packages/effect/src/internal/redacted.ts b/packages/effect/src/internal/redacted.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/redacted.ts rename to packages/effect/src/internal/redacted.ts diff --git a/repos/effect/packages/effect/src/internal/references.ts b/packages/effect/src/internal/references.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/references.ts rename to packages/effect/src/internal/references.ts diff --git a/repos/effect/packages/effect/src/internal/request.ts b/packages/effect/src/internal/request.ts similarity index 86% rename from repos/effect/packages/effect/src/internal/request.ts rename to packages/effect/src/internal/request.ts index bd3874f22..61fe039ce 100644 --- a/repos/effect/packages/effect/src/internal/request.ts +++ b/packages/effect/src/internal/request.ts @@ -4,6 +4,8 @@ import type { Effect } from "../Effect.ts" import type { Exit } from "../Exit.ts" import type { Fiber } from "../Fiber.ts" import { dual } from "../Function.ts" +import * as MutableHashMap from "../MutableHashMap.ts" +import * as Option from "../Option.ts" import type * as Request from "../Request.ts" import { makeEntry } from "../Request.ts" import type { RequestResolver } from "../RequestResolver.ts" @@ -67,10 +69,36 @@ export const requestUnsafe = ( return () => removeEntryUnsafe(options.resolver, entry) } +/** + * Pending batches of resolvers whose `batchKey` is in this set match keys with + * `Equal` instead of by reference. + * + * @internal + */ +export const equalBatchKeys = new WeakSet() + +interface BatchMap { + get(key: unknown): Batch | undefined + has(key: unknown): boolean + set(key: unknown, batch: Batch): void + delete(key: unknown): void +} + +const makeBatchMap = (resolver: RequestResolver): BatchMap => { + if (!equalBatchKeys.has(resolver.batchKey)) return new Map() + const map = MutableHashMap.empty() + return { + get: (key) => Option.getOrUndefined(MutableHashMap.get(map, key)), + has: (key) => MutableHashMap.has(map, key), + set: (key, batch) => MutableHashMap.set(map, key, batch), + delete: (key) => MutableHashMap.remove(map, key) + } +} + interface Batch { key: unknown resolver: RequestResolver - map: Map + map: BatchMap readonly entrySet: Set> readonly entries: Set> readonly delayEffect: Effect @@ -79,7 +107,7 @@ interface Batch { } const batchPool: Array = [] -const pendingBatches = new WeakMap, Map>() +const pendingBatches = new WeakMap, BatchMap>() const addEntry = ( resolver: RequestResolver, @@ -93,7 +121,7 @@ const addEntry = ( ) => { let batchMap = pendingBatches.get(resolver) if (!batchMap) { - batchMap = new Map() + batchMap = makeBatchMap(resolver) pendingBatches.set(resolver, batchMap) } let batch: Batch | undefined diff --git a/repos/effect/packages/effect/src/internal/result.ts b/packages/effect/src/internal/result.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/result.ts rename to packages/effect/src/internal/result.ts diff --git a/repos/effect/packages/effect/src/internal/schedule.ts b/packages/effect/src/internal/schedule.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schedule.ts rename to packages/effect/src/internal/schedule.ts diff --git a/repos/effect/packages/effect/src/internal/schema/annotations.ts b/packages/effect/src/internal/schema/annotations.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/annotations.ts rename to packages/effect/src/internal/schema/annotations.ts diff --git a/repos/effect/packages/effect/src/internal/schema/cause.ts b/packages/effect/src/internal/schema/cause.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/cause.ts rename to packages/effect/src/internal/schema/cause.ts diff --git a/repos/effect/packages/effect/src/internal/schema/codegen.ts b/packages/effect/src/internal/schema/codegen.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/codegen.ts rename to packages/effect/src/internal/schema/codegen.ts diff --git a/repos/effect/packages/effect/src/internal/schema/compilerRegistry.ts b/packages/effect/src/internal/schema/compilerRegistry.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/compilerRegistry.ts rename to packages/effect/src/internal/schema/compilerRegistry.ts diff --git a/repos/effect/packages/effect/src/internal/schema/fromJsonSchemaDocument.ts b/packages/effect/src/internal/schema/fromJsonSchemaDocument.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/fromJsonSchemaDocument.ts rename to packages/effect/src/internal/schema/fromJsonSchemaDocument.ts diff --git a/repos/effect/packages/effect/src/internal/schema/fromRepresentation.ts b/packages/effect/src/internal/schema/fromRepresentation.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/fromRepresentation.ts rename to packages/effect/src/internal/schema/fromRepresentation.ts diff --git a/repos/effect/packages/effect/src/internal/schema/interpreter.ts b/packages/effect/src/internal/schema/interpreter.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/interpreter.ts rename to packages/effect/src/internal/schema/interpreter.ts diff --git a/repos/effect/packages/effect/src/internal/schema/make.ts b/packages/effect/src/internal/schema/make.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/make.ts rename to packages/effect/src/internal/schema/make.ts diff --git a/repos/effect/packages/effect/src/internal/schema/parser.ts b/packages/effect/src/internal/schema/parser.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/parser.ts rename to packages/effect/src/internal/schema/parser.ts diff --git a/repos/effect/packages/effect/src/internal/schema/standardSchema.ts b/packages/effect/src/internal/schema/standardSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/standardSchema.ts rename to packages/effect/src/internal/schema/standardSchema.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toCodeDocument.ts b/packages/effect/src/internal/schema/toCodeDocument.ts similarity index 98% rename from repos/effect/packages/effect/src/internal/schema/toCodeDocument.ts rename to packages/effect/src/internal/schema/toCodeDocument.ts index f8673241b..4546c0842 100644 --- a/repos/effect/packages/effect/src/internal/schema/toCodeDocument.ts +++ b/packages/effect/src/internal/schema/toCodeDocument.ts @@ -48,6 +48,8 @@ function renderEmittableAnnotation(input: unknown): string | undefined { return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }` } +const canonicalFiniteCheck = `Schema.isFinite().annotate({ "expected": "a finite number" })` + function renderAnnotations( annotations: Schema.Annotations.Annotations | undefined ): string | undefined { @@ -341,7 +343,11 @@ export function toCodeDocument( let runtime = base.runtime + runtimeAnnotate(representation.annotations) for (let index = 0; index < representation.checks.length; index++) { const check = representation.checks[index] - runtime += `.check(${compileCheck(check, [...path, "checks", index])})` + const compiledCheck = compileCheck(check, [...path, "checks", index]) + // Only collapse a bare number's leading canonical check. + runtime = runtime === "Schema.Number" && compiledCheck === canonicalFiniteCheck + ? "Schema.Finite" + : `${runtime}.check(${compiledCheck})` } return makeCode(runtime, base.Type) } diff --git a/repos/effect/packages/effect/src/internal/schema/toCodec.ts b/packages/effect/src/internal/schema/toCodec.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toCodec.ts rename to packages/effect/src/internal/schema/toCodec.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toDifferJsonPatch.ts b/packages/effect/src/internal/schema/toDifferJsonPatch.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toDifferJsonPatch.ts rename to packages/effect/src/internal/schema/toDifferJsonPatch.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toEncoderXml.ts b/packages/effect/src/internal/schema/toEncoderXml.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toEncoderXml.ts rename to packages/effect/src/internal/schema/toEncoderXml.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toEquivalence.ts b/packages/effect/src/internal/schema/toEquivalence.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toEquivalence.ts rename to packages/effect/src/internal/schema/toEquivalence.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toFormatter.ts b/packages/effect/src/internal/schema/toFormatter.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toFormatter.ts rename to packages/effect/src/internal/schema/toFormatter.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toIso.ts b/packages/effect/src/internal/schema/toIso.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toIso.ts rename to packages/effect/src/internal/schema/toIso.ts diff --git a/repos/effect/packages/effect/src/internal/schema/toJsonSchemaDocument.ts b/packages/effect/src/internal/schema/toJsonSchemaDocument.ts similarity index 94% rename from repos/effect/packages/effect/src/internal/schema/toJsonSchemaDocument.ts rename to packages/effect/src/internal/schema/toJsonSchemaDocument.ts index a87234b2e..0d3889afc 100644 --- a/repos/effect/packages/effect/src/internal/schema/toJsonSchemaDocument.ts +++ b/packages/effect/src/internal/schema/toJsonSchemaDocument.ts @@ -121,6 +121,37 @@ function hasNoCollisions(left: JsonSchema.JsonSchema, rightKeys: ReadonlyArray !Object.hasOwn(left, key)) } +// Every bound on one schema object applies, so a repeated bound reduces to its tighter value. +const lowerBoundKeywords = "|minimum|exclusiveMinimum|minLength|minItems|minProperties|" +const upperBoundKeywords = "|maximum|exclusiveMaximum|maxLength|maxItems|maxProperties|" + +// Tightens repeated bounds and drops an inclusive or exclusive bound implied by the other on the same side. Returns +// `undefined` for a `$ref` schema or a non-bound collision, so the caller falls back to `allOf`. +function mergeInlineCheck( + left: JsonSchema.JsonSchema, + right: JsonSchema.JsonSchema, + rightKeys: ReadonlyArray +): JsonSchema.JsonSchema | undefined { + if (typeof left.$ref === "string") return undefined + const out: JsonSchema.JsonSchema = { ...left, ...right } + for (const key of rightKeys) { + if (!Object.hasOwn(left, key)) continue + const a = left[key] + const b = right[key] + if (typeof a !== "number" || typeof b !== "number") return undefined + if (lowerBoundKeywords.includes(`|${key}|`)) out[key] = Math.max(a, b) + else if (upperBoundKeywords.includes(`|${key}|`)) out[key] = Math.min(a, b) + else return undefined + } + if (typeof out.minimum === "number" && typeof out.exclusiveMinimum === "number") { + delete out[out.exclusiveMinimum >= out.minimum ? "minimum" : "exclusiveMinimum"] + } + if (typeof out.maximum === "number" && typeof out.exclusiveMaximum === "number") { + delete out[out.exclusiveMaximum <= out.maximum ? "maximum" : "exclusiveMaximum"] + } + return out +} + // `format` and `content*` can affect validation, so they are not treated as pure annotations. const promotableAnnotationKeywords = "|title|description|default|examples|readOnly|writeOnly|" const inlineableAnnotatedCheckKeywords = inlineableCheckKeywords + promotableAnnotationKeywords @@ -149,8 +180,9 @@ function appendJsonSchema( : appendJsonSchema(base, extracted.schema, inlineCheck) } } - if (inlineCheck && hasNoCollisions(left, rightKeys)) { - return { ...left, ...right } + if (inlineCheck) { + const merged = mergeInlineCheck(left, right, rightKeys) + if (merged !== undefined) return merged } const members = Array.isArray(right.allOf) && rightKeys.length === 1 ? right.allOf : [right] if (Array.isArray(left.allOf)) { diff --git a/repos/effect/packages/effect/src/internal/schema/toRepresentation.ts b/packages/effect/src/internal/schema/toRepresentation.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schema/toRepresentation.ts rename to packages/effect/src/internal/schema/toRepresentation.ts diff --git a/repos/effect/packages/effect/src/internal/schemaError.ts b/packages/effect/src/internal/schemaError.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/schemaError.ts rename to packages/effect/src/internal/schemaError.ts diff --git a/repos/effect/packages/effect/src/internal/stackTraceLimit.ts b/packages/effect/src/internal/stackTraceLimit.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/stackTraceLimit.ts rename to packages/effect/src/internal/stackTraceLimit.ts diff --git a/repos/effect/packages/effect/src/internal/stream.ts b/packages/effect/src/internal/stream.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/stream.ts rename to packages/effect/src/internal/stream.ts diff --git a/repos/effect/packages/effect/src/internal/tracer.ts b/packages/effect/src/internal/tracer.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/tracer.ts rename to packages/effect/src/internal/tracer.ts diff --git a/repos/effect/packages/effect/src/internal/trie.ts b/packages/effect/src/internal/trie.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/trie.ts rename to packages/effect/src/internal/trie.ts diff --git a/repos/effect/packages/effect/src/internal/ulid.ts b/packages/effect/src/internal/ulid.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/ulid.ts rename to packages/effect/src/internal/ulid.ts diff --git a/repos/effect/packages/effect/src/internal/uuid.ts b/packages/effect/src/internal/uuid.ts similarity index 100% rename from repos/effect/packages/effect/src/internal/uuid.ts rename to packages/effect/src/internal/uuid.ts diff --git a/packages/effect/src/internal/version.ts b/packages/effect/src/internal/version.ts new file mode 100644 index 000000000..5ef0ed8fc --- /dev/null +++ b/packages/effect/src/internal/version.ts @@ -0,0 +1,2 @@ +/** @internal */ +export const version = "4.0.5" diff --git a/packages/effect/src/net/AddressResolver.ts b/packages/effect/src/net/AddressResolver.ts new file mode 100644 index 000000000..9faef98a2 --- /dev/null +++ b/packages/effect/src/net/AddressResolver.ts @@ -0,0 +1,222 @@ +/** + * Resolves host and port endpoints to socket addresses. + * + * The `AddressResolver` service turns a `Host.HostPort` or `host:port` string + * into socket addresses. Numeric hosts are converted without a lookup. IPv6 + * literals with a named zone such as `fe80::1%eth0` get their scope ID from the host's + * network interfaces, and domain names are looked up with the `Dns` service. + * Runtime layers look up network interfaces; this module's layer requires + * only `Dns` and supports numeric zones only. + * + * @stability experimental + * @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 Option from "../Option.ts" +import * as Dns from "./Dns.ts" +import * as Host from "./Host.ts" +import * as NetAddress from "./NetAddress.ts" + +/** + * Service that resolves endpoints to internet and socket addresses. + * + * **Details** + * + * Every operation either returns at least one address or fails. Lookups fail + * with a `Dns.DnsError`, and hosts that cannot be converted, such as IPv6 + * literals with an unknown zone or numeric addresses outside the requested + * family, fail with a `NetAddress.NetAddressError`. + * + * @stability experimental + * @category services + * @since 4.0.0 + */ +export class AddressResolver extends Context.Service( + target: NetAddress.InetAddress | Host.HostPortInput, + options: ResolveOptions & { readonly family: F } + ): Effect.Effect< + Arr.NonEmptyReadonlyArray>>, + Dns.DnsError | NetAddress.NetAddressError + > + resolve( + target: NetAddress.InetAddress | Host.HostPortInput, + options?: ResolveOptions + ): Effect.Effect, Dns.DnsError | NetAddress.NetAddressError> + resolve( + target: NetAddress.SocketAddress | Host.HostPortInput, + options?: ResolveOptions + ): Effect.Effect, Dns.DnsError | NetAddress.NetAddressError> +}>()("effect/net/AddressResolver") {} + +/** + * Options for resolving an endpoint. Without a `family`, addresses of both + * families are returned. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface ResolveOptions { + readonly family?: NetAddress.IpFamily | undefined +} + +/** + * Looks up the IPv6 scope ID of a network interface by name. + * + * **Details** + * + * Returns `None` for interfaces that do not exist or have no IPv6 scope ID, and + * fails when the interfaces cannot be listed. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type ScopeIdLookup = (name: string) => Effect.Effect, NetAddress.NetAddressError> + +/** + * Options for creating an `AddressResolver`. + * + * **Details** + * + * `scopeId` is called whenever an IPv6 literal with a named zone such as + * `fe80::1%eth0` is resolved, so interfaces added or recreated while the + * program runs are found. Without it, only numeric zones such as `fe80::1%2` + * are supported. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface MakeOptions { + readonly scopeId?: ScopeIdLookup | undefined +} + +const inFamily = ( + address: NetAddress.InetAddress, + family: NetAddress.IpFamily | undefined +): Effect.Effect, NetAddress.NetAddressError> => + family === undefined || NetAddress.isFamily(address, family) + ? Effect.succeed(Arr.of(address)) + : Effect.fail(new NetAddress.NetAddressError({ input: address, message: `expected an ${family} address` })) + +/** + * Creates an `AddressResolver` that looks up domain names with a `Dns` + * service. + * + * **Example** (Resolving an endpoint with a static resolver) + * + * ```ts import.meta.vitest + * import { Effect, Result } from "effect" + * import { AddressResolver, Dns, NetAddress } from "effect/net" + * + * const dns = Result.getOrThrow(Dns.makeStatic({ + * hosts: { "db.internal": ["10.0.0.5"] } + * })) + * const resolver = AddressResolver.make(dns) + * + * const program = resolver.resolve("db.internal:5432").pipe( + * Effect.map((addresses) => addresses.map(NetAddress.formatInet)) + * ) + * + * await Effect.runPromise(program) // => ["10.0.0.5:5432"] + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const make = (dns: Dns.Dns["Service"], options?: MakeOptions): AddressResolver["Service"] => { + const scopeId = options?.scopeId + + const fromLiteral = ( + host: NetAddress.IpAddress | NetAddress.ScopedIpv6Literal, + port: number + ): Effect.Effect => { + if (NetAddress.isIpAddress(host)) return Effect.fromResult(NetAddress.inetAddress(host, port)) + const zone = host.slice(host.indexOf("%") + 1) + if (scopeId === undefined || /^\d+$/.test(zone)) { + return Effect.fromResult(NetAddress.inetAddressFromHostString(host, port)) + } + + return Effect.flatMap(scopeId(zone), (id) => + Effect.fromResult( + NetAddress.inetAddressFromHostString(host, port, Option.isSome(id) ? new Map([[zone, id.value]]) : undefined) + )) + } + + const resolve = ( + target: NetAddress.SocketAddress | Host.HostPortInput, + options?: ResolveOptions + ): Effect.Effect, Dns.DnsError | NetAddress.NetAddressError> => { + const family = options?.family + if (NetAddress.isUnixPathAddress(target)) return Effect.succeed(Arr.of(target)) + if (NetAddress.isInetAddress(target)) return inFamily(target, family) + if (!Host.isHostPort(target)) { + return Effect.flatMap( + Effect.fromResult(Host.hostPortFromInput(target)), + (endpoint) => resolve(endpoint, options) + ) + } + + const { host, port } = target + if (NetAddress.isIpAddress(host) || NetAddress.isScopedIpv6Literal(host)) { + return Effect.flatMap(fromLiteral(host, port), (address) => inFamily(address, family)) + } + + return Effect.flatMap(dns.lookup(host, { family }), (addresses) => { + // Lookups do not report the scope that a link-local IPv6 address needs. + const usable = addresses.filter((address) => + !(NetAddress.isIpv6Address(address) && NetAddress.isLinkLocal(address)) + ) + + return Arr.isReadonlyArrayNonEmpty(usable) + ? Effect.succeed(Arr.map(usable, (address) => NetAddress.inetAddressUnsafe(address, port))) + : Effect.fail( + new NetAddress.NetAddressError({ + input: host, + message: "only link-local IPv6 addresses were found, and lookups do not report their scope" + }) + ) + }) + } + + return AddressResolver.of({ resolve: resolve as AddressResolver["Service"]["resolve"] }) +} + +/** + * Creates a layer that provides an `AddressResolver` using the `Dns` service. + * + * @stability experimental + * @category layers + * @since 4.0.0 + */ +export const layer = (options?: MakeOptions): Layer.Layer => + Layer.effect(AddressResolver, Effect.map(Effect.service(Dns.Dns), (dns) => make(dns, options))) diff --git a/packages/effect/src/net/Dns.ts b/packages/effect/src/net/Dns.ts new file mode 100644 index 000000000..ce89e323a --- /dev/null +++ b/packages/effect/src/net/Dns.ts @@ -0,0 +1,1035 @@ +/** + * Host name resolution, DNS queries, and DNS record values. + * + * `lookup` resolves a host name to the addresses used to connect to it, + * `resolve` queries DNS records of one type, and `reverse` looks up the names + * of an address. Runtime packages provide the `Dns` service from the host + * platform's resolver, whose `lookup` uses the operating system resolver + * (`getaddrinfo`) and therefore also reads the hosts file and other system + * sources, not only DNS. `AddressResolver` builds on `lookup` to resolve + * `host:port` endpoints. + * + * @stability experimental + * @since 4.0.0 + */ +import * as Arr from "../Array.ts" +import * as Context from "../Context.ts" +import * as Data from "../Data.ts" +import * as Duration from "../Duration.ts" +import * as Effect from "../Effect.ts" +import * as Hex from "../encoding/Hex.ts" +import * as Equal from "../Equal.ts" +import * as Hash from "../Hash.ts" +import * as Inspectable from "../Inspectable.ts" +import * as Layer from "../Layer.ts" +import { hasProperty } from "../Predicate.ts" +import type { ReadonlyRecord } from "../Record.ts" +import * as Result from "../Result.ts" +import * as Host from "./Host.ts" +import * as NetAddress from "./NetAddress.ts" + +const RecordTypeId = "~effect/net/Dns/DnsRecord" as const + +// ============================================================================= +// Records +// ============================================================================= + +interface RecordProto extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { + readonly _tag: Tag + readonly [RecordTypeId]: typeof RecordTypeId +} + +/** + * An IPv4 address record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface A extends RecordProto<"A"> { + readonly address: NetAddress.Ipv4Address +} + +/** + * An IPv6 address record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Aaaa extends RecordProto<"AAAA"> { + readonly address: NetAddress.Ipv6Address +} + +/** + * A certification authority authorization record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Caa extends RecordProto<"CAA"> { + readonly critical: boolean + readonly tag: string + readonly value: string +} + +/** + * A canonical name (alias) record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Cname extends RecordProto<"CNAME"> { + readonly target: Host.DomainName +} + +/** + * A mail exchange record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Mx extends RecordProto<"MX"> { + readonly exchange: Host.DomainName + readonly priority: number +} + +/** + * A naming authority pointer record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Naptr extends RecordProto<"NAPTR"> { + readonly order: number + readonly preference: number + readonly flags: string + readonly service: string + readonly regexp: string + readonly replacement: Host.DomainName +} + +/** + * A name server record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Ns extends RecordProto<"NS"> { + readonly host: Host.DomainName +} + +/** + * A pointer record, used for reverse lookups and DNS service discovery. + * + * **Details** + * + * `host` is a fully qualified name. It is a host name for reverse lookups, + * but may be a DNS-SD service instance name containing spaces or other + * characters, such as `EPSON TM-m30III._ipp._tcp.local.`. Labels are UTF-8 + * text, with dots and backslashes inside a label escaped as `\.` and `\\`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Ptr extends RecordProto<"PTR"> { + readonly host: string +} + +/** + * A start of authority record. + * + * **Details** + * + * `admin` is the administrator mailbox written as a domain name, such as + * `hostmaster.example.com`. A dot inside the mailbox's local part is escaped, + * as in `john\.doe.example.com`, so `admin` is a plain string rather than a + * `Host.DomainName`. Labels are UTF-8 text, with dots and backslashes inside + * a label escaped as `\.` and `\\`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Soa extends RecordProto<"SOA"> { + readonly primary: Host.DomainName + readonly admin: string + readonly serial: number + readonly refresh: Duration.Duration + readonly retry: Duration.Duration + readonly expire: Duration.Duration + readonly minimum: Duration.Duration +} + +/** + * A service location record. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Srv extends RecordProto<"SRV"> { + readonly target: Host.DomainName + readonly port: number + readonly priority: number + readonly weight: number +} + +/** + * A TLS authentication record (RFC 6698), which pins the certificate or public + * key that a TLS service presents, for DNS-Based Authentication of Named + * Entities (DANE). Records are published at a name made of the service's port + * and protocol, such as `_443._tcp.example.com`. + * + * **Details** + * + * `certUsage` says how the record constrains the certificate chain (0 to 3: + * PKIX-TA, PKIX-EE, DANE-TA, DANE-EE), `selector` whether `data` matches the + * full certificate (0) or its public key (1), and `matchingType` whether + * `data` holds the selected content itself (0) or its SHA-256 (1) or SHA-512 + * (2) digest. Unassigned values are kept as they are. + * + * **Gotchas** + * + * `data` is a copy of the bytes the record was made from, but typed arrays + * cannot be frozen; treat it as read-only, because changing it changes the + * record's equality. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Tlsa extends RecordProto<"TLSA"> { + readonly certUsage: number + readonly selector: number + readonly matchingType: number + readonly data: Uint8Array +} + +/** + * A text record. DNS stores text as one or more character strings, which are + * kept as separate chunks; protocols such as SPF read them joined, for example + * with `record.chunks.join("")`. + * + * **Details** + * + * Platform services decode each character string as UTF-8, replacing bytes + * that are not valid UTF-8 with U+FFFD, and `formatRecord` writes chunks back + * as UTF-8 bytes. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Txt extends RecordProto<"TXT"> { + readonly chunks: Arr.NonEmptyReadonlyArray +} + +/** + * Any supported DNS record value. + * + * **Details** + * + * Records are immutable values tagged by record type, compared and hashed by + * their fields. They hold the record data only: owner names and TTLs are not + * included because platform resolvers do not report them consistently. Names + * returned by platform services are fully qualified and end with a dot, so + * passing them back to the resolver does not apply search domains. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type DnsRecord = A | Aaaa | Caa | Cname | Mx | Naptr | Ns | Ptr | Soa | Srv | Tlsa | Txt + +/** + * The type of a DNS record, such as `"A"` or `"SRV"`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type RecordType = + | "A" + | "AAAA" + | "CAA" + | "CNAME" + | "MX" + | "NAPTR" + | "NS" + | "PTR" + | "SOA" + | "SRV" + | "TLSA" + | "TXT" + +/** + * The record value for a record type. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type RecordFor = Extract + +/** + * The fields of a record type, without its `_tag`. + * + * @see {@link makeRecord} + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type RecordFields = T extends RecordType ? Omit, keyof RecordProto> + : never + +const isUint8 = (u: unknown): boolean => Number.isInteger(u) && (u as number) >= 0 && (u as number) <= 0xff +const isUint16 = (u: unknown): boolean => Number.isInteger(u) && (u as number) >= 0 && (u as number) <= 0xffff +const isUint32 = (u: unknown): boolean => Number.isInteger(u) && (u as number) >= 0 && (u as number) <= 0xffffffff +const isString = (u: unknown): u is string => typeof u === "string" +// SOA timers are 32-bit unsigned counts of seconds on the wire. +const isTimer = (u: unknown): boolean => + Duration.isDuration(u) && Duration.isFinite(u) && isUint32(Duration.toSeconds(u)) + +const recordFields: { + readonly [K in RecordType]: { readonly [F in keyof RecordFields]-?: (u: unknown) => boolean } +} = { + A: { + address: NetAddress.isIpv4Address + }, + AAAA: { + address: NetAddress.isIpv6Address + }, + CAA: { + critical: (u) => typeof u === "boolean", + tag: (u) => isString(u) && /^[a-z0-9]+$/i.test(u), + value: isString + }, + CNAME: { + target: Host.isDomainName + }, + MX: { + exchange: Host.isDomainName, + priority: isUint16 + }, + NAPTR: { + order: isUint16, + preference: isUint16, + flags: isString, + service: isString, + regexp: isString, + replacement: Host.isDomainName + }, + NS: { + host: Host.isDomainName + }, + PTR: { + host: (u) => isString(u) && u.length > 0 + }, + SOA: { + primary: Host.isDomainName, + admin: (u) => isString(u) && u.length > 0, + serial: isUint32, + refresh: isTimer, + retry: isTimer, + expire: isTimer, + minimum: isTimer + }, + SRV: { + target: Host.isDomainName, + port: isUint16, + priority: isUint16, + weight: isUint16 + }, + TLSA: { + certUsage: isUint8, + selector: isUint8, + matchingType: isUint8, + data: (u) => u instanceof Uint8Array && u.length > 0 + }, + TXT: { + chunks: (u) => Array.isArray(u) && u.length > 0 && u.every(isString) + } +} + +/** + * Every supported record type. + * + * @stability experimental + * @category constants + * @since 4.0.0 + */ +export const recordTypes: Arr.NonEmptyReadonlyArray = Object.keys(recordFields) as Array as any + +/** + * Returns `true` when a value is a supported record type. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isRecordType = (u: unknown): u is RecordType => recordTypes.includes(u as RecordType) + +/** + * Returns `true` when a value is a DNS record. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isDnsRecord = (u: unknown): u is DnsRecord => hasProperty(u, RecordTypeId) + +const RecordPrototype = { + ...Inspectable.BaseProto, + [RecordTypeId]: RecordTypeId, + [Equal.symbol](this: DnsRecord, that: Equal.Equal): boolean { + return isDnsRecord(that) && that._tag === this._tag && + Object.keys(this).every((key) => Equal.equals((this as any)[key], (that as any)[key])) + }, + [Hash.symbol](this: DnsRecord): number { + return Hash.structureKeys(this, Object.keys(this)) + }, + toString(this: DnsRecord): string { + return formatRecord(this) + }, + toJSON(this: DnsRecord): unknown { + return { ...this } + } +} + +/** + * Creates a checked DNS record of a record type from its fields. + * + * **Details** + * + * Every field is checked at runtime: addresses must belong to the record's + * address family, names must be normalized domain names, priorities, weights, + * and ports must be 16-bit unsigned integers, the SOA serial and timers must be + * 32-bit unsigned integers (the timers in whole seconds), the TLSA fields must + * be 8-bit unsigned integers with non-empty `data`, which is copied, TXT + * records need at least one chunk, and CAA property tags must be alphanumeric. + * Fields that do not belong to the record type are ignored. + * + * **Example** (Creating a service record) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Dns, Host } from "effect/net" + * + * const record = Result.getOrThrow(Dns.makeRecord("SRV", { + * target: Host.domainNameFromStringUnsafe("db.internal"), + * port: 5432, + * priority: 10, + * weight: 5 + * })) + * Dns.formatRecord(record) // => "SRV 10 5 5432 db.internal" + * ``` + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const makeRecord = ( + type: T, + fields: RecordFields +): Result.Result, NetAddress.NetAddressError> => { + if (!isRecordType(type)) { + return Result.fail(new NetAddress.NetAddressError({ input: type, message: "unknown DNS record type" })) + } + const input: Record = typeof fields === "object" && fields !== null ? fields : {} + const self = Object.create(RecordPrototype) + self._tag = type + + for (const [key, check] of Object.entries<(u: unknown) => boolean>(recordFields[type])) { + const value = input[key] + if (!check(value)) { + return Result.fail(new NetAddress.NetAddressError({ input: fields, message: `invalid ${type} record ${key}` })) + } + self[key] = Array.isArray(value) + ? Object.freeze([...value]) + : value instanceof Uint8Array + ? new Uint8Array(value) + : value + } + + return Result.succeed(Object.freeze(self)) +} + +/** + * Creates a trusted DNS record of a record type from its fields, throwing on + * failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const makeRecordUnsafe = (type: T, fields: RecordFields): RecordFor => + Result.getOrThrow(makeRecord(type, fields)) + +const encoder = new TextEncoder() + +const decimalEscape = (byte: number): string => `\\${byte.toString().padStart(3, "0")}` + +// Quotes a character string in DNS presentation format (RFC 1035, section 5.1): +// `"` and `\` are escaped, and bytes outside printable ASCII are written as `\DDD`. +const quote = (value: string): string => { + let out = "\"" + for (const byte of encoder.encode(value)) { + out += byte === 0x22 || byte === 0x5c + ? `\\${String.fromCharCode(byte)}` + : byte < 0x20 || byte > 0x7e + ? decimalEscape(byte) + : String.fromCharCode(byte) + } + return out + "\"" +} + +const nameSpecials = [0x22, 0x24, 0x28, 0x29, 0x3b, 0x40] + +// Writes a name held as text, such as `Ptr.host`, in DNS presentation format: +// its `\.` and `\\` escapes are kept, other special characters are escaped, and +// spaces and bytes outside printable ASCII are written as `\DDD`. +const formatName = (text: string): string => { + const bytes = encoder.encode(text) + let out = "" + for (let i = 0; i < bytes.length; i++) { + const byte = bytes[i] + if (byte === 0x5c) { + const next = bytes[i + 1] + if (next === 0x2e || next === 0x5c) { + out += `\\${String.fromCharCode(next)}` + i++ + } else { + out += "\\\\" + } + } else if (byte <= 0x20 || byte > 0x7e) { + out += decimalEscape(byte) + } else { + out += nameSpecials.includes(byte) ? `\\${String.fromCharCode(byte)}` : String.fromCharCode(byte) + } + } + return out +} + +/** + * Formats a record in DNS presentation format, prefixed by its type. + * + * @stability experimental + * @category encoding + * @since 4.0.0 + */ +export const formatRecord = (self: DnsRecord): string => { + switch (self._tag) { + case "A": + case "AAAA": + return `${self._tag} ${NetAddress.formatIp(self.address)}` + case "CAA": + return `CAA ${self.critical ? 128 : 0} ${self.tag} ${quote(self.value)}` + case "CNAME": + return `CNAME ${self.target}` + case "MX": + return `MX ${self.priority} ${self.exchange}` + case "NAPTR": + return `NAPTR ${self.order} ${self.preference} ${quote(self.flags)} ${quote(self.service)} ${ + quote(self.regexp) + } ${self.replacement}` + case "NS": + return `NS ${self.host}` + case "PTR": + return `PTR ${formatName(self.host)}` + case "SOA": + return `SOA ${self.primary} ${formatName(self.admin)} ${self.serial} ${Duration.toSeconds(self.refresh)} ${ + Duration.toSeconds(self.retry) + } ${Duration.toSeconds(self.expire)} ${Duration.toSeconds(self.minimum)}` + case "SRV": + return `SRV ${self.priority} ${self.weight} ${self.port} ${self.target}` + case "TLSA": + return `TLSA ${self.certUsage} ${self.selector} ${self.matchingType} ${Hex.encode(self.data).toUpperCase()}` + case "TXT": + return `TXT ${self.chunks.map(quote).join(" ")}` + } +} + +/** + * Returns the reverse-lookup domain name for an IP address, in `in-addr.arpa` + * for IPv4 or `ip6.arpa` for IPv6. + * + * **Example** (Building reverse-lookup names) + * + * ```ts import.meta.vitest + * import { Dns, NetAddress } from "effect/net" + * + * Dns.reverseName(NetAddress.ipFromStringUnsafe("192.0.2.1")) // => "1.2.0.192.in-addr.arpa" + * ``` + * + * @stability experimental + * @category converting + * @since 4.0.0 + */ +export const reverseName = (address: NetAddress.IpAddress): Host.DomainName => { + if (NetAddress.isIpv4Address(address)) { + return `${[...NetAddress.ipv4ToOctets(address)].reverse().join(".")}.in-addr.arpa` as Host.DomainName + } + const nibbles: Array = [] + for (const octet of NetAddress.ipv6ToOctets(address)) { + nibbles.push((octet >> 4).toString(16), (octet & 0xf).toString(16)) + } + return `${nibbles.reverse().join(".")}.ip6.arpa` as Host.DomainName +} + +// ============================================================================= +// Name servers +// ============================================================================= + +/** + * Parses the address of a name server: an IP address, which uses port 53, or + * an IP address and port. + * + * **Details** + * + * IPv6 addresses with a port are written in brackets, and IPv6 addresses + * without one are not. + * + * **Example** (Parsing name server addresses) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Dns, NetAddress } from "effect/net" + * + * const format = (input: string) => Result.map(Dns.nameServerFromString(input), NetAddress.formatInet) + * + * format("192.0.2.53") // => Result.succeed("192.0.2.53:53") + * format("2001:db8::53") // => Result.succeed("[2001:db8::53]:53") + * format("[2001:db8::53]:5353") // => Result.succeed("[2001:db8::53]:5353") + * Result.isFailure(format("ns.example")) // => true + * ``` + * + * @stability experimental + * @category decoding + * @since 4.0.0 + */ +export const nameServerFromString = ( + input: string +): Result.Result => + NetAddress.inetAddressFromString( + input.startsWith("[") || /^[^:]*:\d+$/.test(input) ? input : input.includes(":") ? `[${input}]:53` : `${input}:53` + ) + +/** + * Converts a name server to the internet address to query. Strings are parsed + * like `nameServerFromString`, internet addresses and address parts with a + * port are converted like `NetAddress.inetAddressFromInput`, and other IP + * address inputs are converted like `NetAddress.ipFromInput` and use port 53. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const nameServerFromInput = ( + input: NetAddress.IpAddressInput | NetAddress.InetAddressInput +): Result.Result => + typeof input === "string" + ? nameServerFromString(input) + : "port" in input + ? NetAddress.inetAddressFromInput(input) + : Result.map(NetAddress.ipFromInput(input), (address) => NetAddress.inetAddressUnsafe(address, 53)) + +// ============================================================================= +// Errors +// ============================================================================= + +/** + * The normalized reason for a failed name resolution. + * + * **Details** + * + * - `NotFound`: no records were found, either because the name does not exist + * or because it has no records of the requested type or address family. + * Like Go's `DNSError.IsNotFound`, the two cases are not distinguished + * because not every platform resolver can tell them apart. + * - `Timeout`, `Temporary`, and `ServerFailure`: transient failures that may + * succeed when retried. + * - `Refused`: the server refused the query or could not be reached. + * - `BadName`: the name was rejected as malformed. + * - `InvalidResponse`: the platform returned data that could not be converted + * into record values. + * - `Unsupported`: the platform does not support the operation or record type. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export type DnsErrorReason = + | "NotFound" + | "Timeout" + | "Temporary" + | "Refused" + | "ServerFailure" + | "BadName" + | "InvalidResponse" + | "Unsupported" + | "Unknown" + +/** + * A failed lookup, query, or reverse lookup. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DnsError extends Data.TaggedError("DnsError")<{ + readonly reason: DnsErrorReason + readonly method: "lookup" | "resolve" | "reverse" + readonly hostname: string + readonly recordType?: RecordType | undefined + readonly cause?: unknown +}> { + /** + * Formats the reason, operation, and name of the failed resolution. + * + * @since 4.0.0 + */ + override get message(): string { + return `${this.reason}: Dns.${this.method} (${this.hostname}${ + this.recordType === undefined ? "" : ` ${this.recordType}` + })` + } + + /** + * Whether the failure is transient and the operation may succeed if retried. + * + * @since 4.0.0 + */ + get isTemporary(): boolean { + return this.reason === "Timeout" || this.reason === "Temporary" || this.reason === "ServerFailure" + } +} + +// ============================================================================= +// Service +// ============================================================================= + +/** + * Options for address lookups. Without a `family`, addresses of both families + * are returned. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface LookupOptions { + readonly family?: NetAddress.IpFamily | undefined +} + +/** + * Service that resolves names to addresses, queries DNS records, and looks up + * the names of addresses. + * + * **Details** + * + * Every operation either returns at least one result or fails with a + * {@link DnsError}. + * + * @stability experimental + * @category services + * @since 4.0.0 + */ +export class Dns extends Context.Service( + host: Host.DomainNameInput, + options: { readonly family: F } + ): Effect.Effect>, DnsError> + lookup( + host: Host.DomainNameInput, + options?: LookupOptions + ): Effect.Effect, DnsError> + + /** + * Queries the DNS records of one type for a name. + * Names are parsed and normalized; invalid names fail with `BadName`. + */ + resolve( + name: Host.DomainNameInput, + type: T + ): Effect.Effect>, DnsError> + + /** + * Looks up the host names of an address with a reverse DNS query. + * + * **Details** + * + * Addresses given as strings are parsed; invalid addresses fail with + * `BadName`. Names that are not valid host names are skipped, including + * names with non-ASCII labels, which are not converted to their ASCII + * (`xn--`) form because that form is a different DNS name. When every + * returned name is skipped, the lookup fails with `InvalidResponse`. + */ + reverse(address: NetAddress.IpAddressInput): Effect.Effect, DnsError> +}>()("effect/net/Dns") {} + +const notFound = (method: DnsError["method"], hostname: string, recordType?: RecordType) => + Effect.fail(new DnsError({ reason: "NotFound", method, hostname, recordType })) + +const badName = (method: DnsError["method"], hostname: string, cause: unknown, recordType?: RecordType) => + Effect.fail(new DnsError({ reason: "BadName", method, hostname, recordType, cause })) + +const asciiName = /^[\w.-]+$/ + +/** + * Creates a `Dns` service from platform resolver operations. + * + * **Details** + * + * Lookup and query names are parsed and normalized, and reverse lookup + * addresses are parsed, before platform callbacks receive them. Invalid names + * and addresses fail with `BadName`. + * + * The constructor filters lookups by the requested address family, keeps only + * records of the requested type, removes duplicates, and turns empty results + * into `NotFound` failures. `reverse` receives the names of an address and + * keeps those that are valid host names, failing with `InvalidResponse` when + * there are names but none is valid. Without a `reverse` operation, the names + * are those of the PTR records at the address's `reverseName`, and failures of + * that query are reported as failures of `reverse`. + * + * Only `lookup` is required. Platforms that can resolve addresses but cannot + * query records, such as browsers, omit `resolve`; record queries, and reverse + * lookups without a `reverse` operation, then fail with `Unsupported`. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const make = (impl: { + readonly lookup: ( + host: Host.DomainName, + family: NetAddress.IpFamily | undefined + ) => Effect.Effect, DnsError> + readonly resolve?: + | ((name: Host.DomainName, type: RecordType) => Effect.Effect, DnsError>) + | undefined + readonly reverse?: + | ((address: NetAddress.IpAddress) => Effect.Effect, DnsError>) + | undefined +}): Dns["Service"] => { + const inFamily = (family: NetAddress.IpFamily | undefined) => (address: NetAddress.IpAddress): boolean => + family === undefined || NetAddress.isFamily(address, family) + + const resolveRecords = impl.resolve ?? + ((name: Host.DomainName, type: RecordType) => + Effect.fail(new DnsError({ reason: "Unsupported", method: "resolve", hostname: name, recordType: type }))) + + const reverseNames = impl.reverse ?? + ((address: NetAddress.IpAddress) => + resolveRecords(reverseName(address), "PTR").pipe( + Effect.map(Arr.flatMap((record) => record._tag === "PTR" ? [record.host] : [])), + Effect.mapError((error) => + new DnsError({ + reason: error.reason, + method: "reverse", + hostname: NetAddress.formatIp(address), + cause: error.cause + }) + ) + )) + + const lookup = (input: Host.DomainNameInput, options?: LookupOptions) => + Result.match(Host.domainNameFromInput(input), { + onFailure: (cause) => badName("lookup", input, cause), + onSuccess: (host) => + impl.lookup(host, options?.family).pipe( + Effect.flatMap((addresses) => + Arr.match(Arr.dedupe(addresses.filter(inFamily(options?.family))), { + onEmpty: () => notFound("lookup", host), + onNonEmpty: Effect.succeed + }) + ) + ) + }) + + return Dns.of({ + lookup, + resolve: (input: Host.DomainNameInput, type: T) => + Result.match(Host.domainNameFromInput(input), { + onFailure: (cause) => badName("resolve", input, cause, type), + onSuccess: (name) => + resolveRecords(name, type).pipe( + Effect.flatMap((records) => + Arr.match(Arr.dedupe(records.filter((record): record is RecordFor => record._tag === type)), { + onEmpty: () => notFound("resolve", name, type), + onNonEmpty: Effect.succeed + }) + ) + ) + }), + reverse: (input) => + Result.match(NetAddress.ipFromInput(input), { + onFailure: (cause) => badName("reverse", String(input), cause), + onSuccess: (address) => + reverseNames(address).pipe( + Effect.flatMap((names) => { + const hostname = NetAddress.formatIp(address) + // Only ASCII names: `domainNameFromString` would convert UTF-8 labels + // to a different, `xn--` name. + const hosts = Arr.dedupe( + Arr.filterMap(names.filter((name) => asciiName.test(name)), Host.domainNameFromString) + ) + return Arr.isReadonlyArrayNonEmpty(hosts) + ? Effect.succeed(hosts) + : names.length > 0 + ? Effect.fail(new DnsError({ reason: "InvalidResponse", method: "reverse", hostname })) + : notFound("reverse", hostname) + }) + ) + }) + }) +} + +// ============================================================================= +// Static resolver +// ============================================================================= + +/** + * Fixed names, addresses, and records for a static resolver. + * + * **Details** + * + * `hosts` works like a hosts file: its entries are used by `lookup` and + * `reverse`, but not by `resolve`. `records` holds DNS records by owner name and + * is used by all three operations. Names are normalized like + * `Host.domainNameFromString`, and a trailing dot is ignored. Addresses are + * converted like `NetAddress.ipFromInput`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface StaticZone { + readonly hosts?: ReadonlyRecord> | undefined + readonly records?: ReadonlyRecord> | undefined +} + +const zoneKey = (name: string): string => name.length > 1 && name.endsWith(".") ? name.slice(0, -1) : name + +/** + * Creates a `Dns` service that answers from fixed data. + * + * **When to use** + * + * Use to test code that depends on `Dns`, or to provide fixed names on + * platforms without a resolver. + * + * **Details** + * + * Names without matching addresses or records fail with `NotFound`. CNAME + * records are returned by `CNAME` queries but are not followed. + * + * **Example** (Looking up a name with a static resolver) + * + * ```ts import.meta.vitest + * import { Effect, Result } from "effect" + * import { Dns, NetAddress } from "effect/net" + * + * const dns = Result.getOrThrow(Dns.makeStatic({ + * hosts: { "db.internal": ["10.0.0.5"] } + * })) + * + * const program = dns.lookup("db.internal").pipe( + * Effect.map((addresses) => addresses.map(NetAddress.formatIp)) + * ) + * + * await Effect.runPromise(program) // => ["10.0.0.5"] + * ``` + * + * @see {@link layerStatic} + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const makeStatic = (zone: StaticZone): Result.Result => { + const entries = new Map + readonly records: Array + }>() + const entry = (name: string) => + Result.map(Host.domainNameFromString(name), (domain) => { + const key = zoneKey(domain) + let current = entries.get(key) + if (current === undefined) { + current = { addresses: [], records: [] } + entries.set(key, current) + } + return current + }) + + for (const [name, addresses] of Object.entries(zone.hosts ?? {})) { + const result = entry(name) + if (Result.isFailure(result)) return Result.fail(result.failure) + for (const input of addresses) { + const address = NetAddress.ipFromInput(input) + if (Result.isFailure(address)) return Result.fail(address.failure) + result.success.addresses.push(address.success) + } + } + + for (const [name, records] of Object.entries(zone.records ?? {})) { + const result = entry(name) + if (Result.isFailure(result)) return Result.fail(result.failure) + for (const record of records) { + if (!isDnsRecord(record)) { + return Result.fail(new NetAddress.NetAddressError({ input: record, message: "expected a DNS record" })) + } + result.success.records.push(record) + } + } + + const recordsAt = (name: string): ReadonlyArray => entries.get(zoneKey(name))?.records ?? [] + + return Result.succeed(make({ + lookup: (host) => + Effect.sync(() => [ + ...(entries.get(zoneKey(host))?.addresses ?? []), + ...recordsAt(host).flatMap((record) => record._tag === "A" || record._tag === "AAAA" ? [record.address] : []) + ]), + resolve: (name) => Effect.sync(() => recordsAt(name)), + reverse: (address) => + Effect.sync(() => [ + ...[...entries].flatMap(([name, current]) => + current.addresses.some((candidate) => Equal.equals(candidate, address)) ? [name] : [] + ), + ...recordsAt(reverseName(address)).flatMap((record) => record._tag === "PTR" ? [record.host] : []) + ]) + })) +} + +/** + * Creates a layer that provides a `Dns` service answering from fixed data. + * + * @see {@link makeStatic} for the resolution rules + * @stability experimental + * @category layers + * @since 4.0.0 + */ +export const layerStatic = (zone: StaticZone): Layer.Layer => + Layer.effect(Dns, Effect.fromResult(makeStatic(zone))) diff --git a/packages/effect/src/net/Host.ts b/packages/effect/src/net/Host.ts new file mode 100644 index 000000000..e54ac1c6c --- /dev/null +++ b/packages/effect/src/net/Host.ts @@ -0,0 +1,544 @@ +/** + * Host names and unresolved host and port endpoints. + * + * A `Host` is either a numeric IP address, an IPv6 literal with a zone such as + * `fe80::1%eth0`, or a DNS domain name. Numeric addresses are parsed directly + * into `NetAddress` values. The `AddressResolver` service turns a `HostPort` + * into internet addresses, looking up domain names with the `Dns` service. + * + * @stability experimental + * @since 4.0.0 + */ +import type * as Arr from "../Array.ts" +import type * as Brand from "../Brand.ts" +import * as Equal from "../Equal.ts" +import * as Hash from "../Hash.ts" +import * as Inspectable from "../Inspectable.ts" +import { hasProperty } from "../Predicate.ts" +import * as Result from "../Result.ts" +import * as NetAddress from "./NetAddress.ts" + +const TypeId = "~effect/net/Host" as const + +/** + * A syntactically valid DNS domain name in lowercase ASCII form. + * + * **Details** + * + * Names follow the rules used by Go's resolver: at most 253 characters, labels + * of 1 to 63 letters, digits, hyphens, or underscores, and no label starting or + * ending with a hyphen. Underscores are allowed so that service names such as + * `_http._tcp.example.com` are valid. The last label must not be numeric, which + * keeps IPv4-like strings out. A trailing dot marks a fully qualified name and + * is preserved. The root name `.` is valid. + * + * Internationalized names are stored in their ASCII (`xn--`) form. + * + * @see {@link domainNameFromString} for parsing and normalizing domain names + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type DomainName = Brand.Branded + +/** + * A domain name or a string to parse and normalize. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type DomainNameInput = DomainName | string + +/** + * A host as written in configuration or a URL authority: a numeric IP address, + * a scoped IPv6 literal, or a domain name. + * + * @see {@link hostFromString} for parsing hosts + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type Host = NetAddress.IpAddress | NetAddress.ScopedIpv6Literal | DomainName + +/** + * A host, or a string or any `IpAddressInput` to convert to one. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type HostInput = Host | NetAddress.IpAddressInput + +/** + * An unresolved host and port. + * + * @see {@link hostPortFromString} for parsing `host:port` strings + * @see {@link formatHostPort} for formatting `host:port` strings + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface HostPort extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { + readonly _tag: "HostPort" + readonly host: Host + readonly port: number + readonly [TypeId]: typeof TypeId +} + +/** + * The host and port of an endpoint. + * + * @see {@link hostPort} for the checked constructor + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface HostPortParts { + readonly host: HostInput + readonly port: number +} + +/** + * An endpoint, or a `host:port` string or parts to convert to one. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type HostPortInput = HostPort | string | HostPortParts + +const hostError = (input: unknown, message: string): Result.Result => + Result.fail(new NetAddress.NetAddressError({ input, message })) + +const asciiNameCharacter = /^[a-z0-9_.-]$/i +const labelPattern = /^[a-z0-9_](?:[a-z0-9_-]{0,61}[a-z0-9_])?$/ +const numericLabel = /^(?:\d+|0x[0-9a-f]*)$/ + +const validateDomainName = (name: string): string | undefined => { + if (name === ".") return undefined + const relative = name.endsWith(".") ? name.slice(0, -1) : name + if (relative.length === 0) return "domain name must not be empty" + if (relative.length > 253) return "domain name must be at most 253 characters" + const labels = relative.split(".") + for (const label of labels) { + if (label.length === 0) return "domain name labels must not be empty" + if (label.length > 63) return "domain name labels must be at most 63 characters" + if (!labelPattern.test(label)) { + return "domain name labels must contain only letters, digits, hyphens, or underscores and must not start or end with a hyphen" + } + } + if (numericLabel.test(labels[labels.length - 1])) { + return "the last domain name label must not be numeric" + } + return undefined +} + +const toAsciiDomainName = (input: string): string | undefined => { + let ascii = true + for (const character of input) { + if (character.charCodeAt(0) < 0x80) { + if (!asciiNameCharacter.test(character)) return undefined + } else { + ascii = false + } + } + if (ascii) return input.toLowerCase() + try { + return new URL(`http://${input}`).hostname + } catch { + return undefined + } +} + +/** + * Returns `true` when a value is a valid, normalized domain name. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isDomainName = (u: unknown): u is DomainName => + typeof u === "string" && /^[a-z0-9_.-]+$/.test(u) && validateDomainName(u) === undefined + +/** + * Returns `true` when a value is a host. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isHost = (u: unknown): u is Host => + NetAddress.isIpAddress(u) || NetAddress.isScopedIpv6Literal(u) || isDomainName(u) + +/** + * Returns `true` when a value is an unresolved host and port. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isHostPort = (u: unknown): u is HostPort => hasProperty(u, TypeId) + +/** + * Parses and normalizes a domain name. + * + * **Details** + * + * ASCII names are lowercased. Names containing non-ASCII characters are + * converted to their ASCII (`xn--`) form using the WHATWG URL host parser, then + * validated with the same rules as ASCII names. A trailing dot is preserved. + * + * **Example** (Normalizing domain names) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Host } from "effect/net" + * + * Result.getOrThrow(Host.domainNameFromString("Example.COM")) // => "example.com" + * Result.getOrThrow(Host.domainNameFromString("bücher.example")) // => "xn--bcher-kva.example" + * Result.isFailure(Host.domainNameFromString("1.2.3.4")) // => true + * ``` + * + * @stability experimental + * @category decoding + * @since 4.0.0 + */ +export const domainNameFromString = (input: string): Result.Result => { + const name = toAsciiDomainName(input) + if (name === undefined) return hostError(input, "invalid domain name") + const error = validateDomainName(name) + return error === undefined ? Result.succeed(name as DomainName) : hostError(input, error) +} + +/** + * Parses a trusted domain name, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const domainNameFromStringUnsafe = (input: string): DomainName => Result.getOrThrow(domainNameFromString(input)) + +/** + * Converts a `DomainNameInput` to a domain name, parsing and normalizing it like + * `domainNameFromString`. + * + * **Details** + * + * Domain names are strings, so every input is parsed. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const domainNameFromInput = (input: DomainNameInput): Result.Result => + domainNameFromString(input) + +/** + * Converts a trusted `DomainNameInput` to a domain name, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const domainNameFromInputUnsafe = (input: DomainNameInput): DomainName => + Result.getOrThrow(domainNameFromInput(input)) + +/** + * Parses a host as a numeric IP address, a scoped IPv6 literal, or a domain + * name, in that order. + * + * **Example** (Parsing hosts) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Host, NetAddress } from "effect/net" + * + * NetAddress.isIpAddress(Result.getOrThrow(Host.hostFromString("10.0.0.5"))) // => true + * Result.getOrThrow(Host.hostFromString("fe80::1%eth0")) // => "fe80::1%eth0" + * Result.getOrThrow(Host.hostFromString("DB.internal")) // => "db.internal" + * ``` + * + * @stability experimental + * @category decoding + * @since 4.0.0 + */ +export const hostFromString = (input: string): Result.Result => { + if (input.includes("%")) return NetAddress.scopedIpv6LiteralFromString(input) + if (input.includes(":")) return NetAddress.ipFromString(input) + if (/^[\d.]+$/.test(input)) { + const address = NetAddress.ipFromString(input) + if (Result.isSuccess(address)) return address + } + return domainNameFromString(input) +} + +/** + * Parses a trusted host, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const hostFromStringUnsafe = (input: string): Host => Result.getOrThrow(hostFromString(input)) + +/** + * Converts a `HostInput` to a host, parsing strings like `hostFromString` and + * converting other inputs like `NetAddress.ipFromInput`. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const hostFromInput = (input: HostInput): Result.Result => + typeof input === "string" ? hostFromString(input) : NetAddress.ipFromInput(input) + +/** + * Converts a trusted `HostInput` to a host, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const hostFromInputUnsafe = (input: HostInput): Host => Result.getOrThrow(hostFromInput(input)) + +/** + * Formats a host without brackets. + * + * @stability experimental + * @category encoding + * @since 4.0.0 + */ +export const formatHost = (self: Host): string => typeof self === "string" ? self : NetAddress.formatIp(self) + +/** + * Returns `true` when a domain name ends with a dot, which excludes it from + * resolver search domains. + * + * @stability experimental + * @category predicates + * @since 4.0.0 + */ +export const isFullyQualified = (self: DomainName): boolean => self.endsWith(".") + +/** + * Appends the trailing dot to a domain name, unless it is already fully + * qualified. + * + * @stability experimental + * @category converting + * @since 4.0.0 + */ +export const toFullyQualified = (self: DomainName): DomainName => + isFullyQualified(self) ? self : `${self}.` as DomainName + +/** + * Removes the trailing dot of a fully qualified domain name, except from the + * root name `.`. + * + * @stability experimental + * @category converting + * @since 4.0.0 + */ +export const toRelative = (self: DomainName): DomainName => + self.length > 1 && isFullyQualified(self) ? self.slice(0, -1) as DomainName : self + +const HostPortProto = { + ...Inspectable.BaseProto, + _tag: "HostPort", + [TypeId]: TypeId, + [Equal.symbol](this: HostPort, that: Equal.Equal): boolean { + return isHostPort(that) && this.port === that.port && Equal.equals(this.host, that.host) + }, + [Hash.symbol](this: HostPort): number { + return Hash.combine(Hash.hash(this.host), Hash.number(this.port)) + }, + toString(this: HostPort): string { + return formatHostPort(this) + }, + toJSON(this: HostPort): string { + return this.toString() + } +} + +const isPort = (port: number): boolean => Number.isInteger(port) && port >= 0 && port <= 0xffff + +const makeHostPort = (host: Host, port: number): HostPort => { + const self = Object.create(HostPortProto) + self.host = host + self.port = port + return Object.freeze(self) +} + +/** + * Creates a checked host and port. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const hostPort = (host: Host, port: number): Result.Result => { + if (!isHost(host)) return hostError(host, "expected a host") + if (!isPort(port)) return hostError(port, "port must be an integer from 0 through 65535") + return Result.succeed(makeHostPort(host, port)) +} + +/** + * Creates a trusted host and port, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const hostPortUnsafe = (host: Host, port: number): HostPort => Result.getOrThrow(hostPort(host, port)) + +/** + * Parses `host:port`, `[IPv6]:port`, or `[IPv6%zone]:port`. + * + * **Details** + * + * IPv6 hosts must be bracketed and other hosts must not be. The port is + * required and must be an unpadded decimal integer from 0 through 65535. A + * zone follows the `%` directly; the percent-encoded `%25` separator of RFC + * 6874 URLs is not decoded, because `[fe80::1%253]:80` would be ambiguous. + * + * **Example** (Parsing host and port strings) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { Host } from "effect/net" + * + * const endpoint = Result.getOrThrow(Host.hostPortFromString("DB.internal:5432")) + * endpoint.host // => "db.internal" + * endpoint.port // => 5432 + * Host.formatHostPort(Result.getOrThrow(Host.hostPortFromString("[::1]:80"))) // => "[::1]:80" + * ``` + * + * @see {@link formatHostPort} for the inverse operation + * @stability experimental + * @category decoding + * @since 4.0.0 + */ +export const hostPortFromString = (input: string): Result.Result => { + let hostText: string + let portText: string + if (input.startsWith("[")) { + const end = input.indexOf("]") + if (end === -1 || input[end + 1] !== ":") return hostError(input, "expected [IPv6]:port") + hostText = input.slice(1, end) + portText = input.slice(end + 2) + if (!hostText.includes(":")) return hostError(input, "only IPv6 addresses use brackets") + } else { + const separator = input.lastIndexOf(":") + if (separator === -1) return hostError(input, "expected host:port") + if (input.indexOf(":") !== separator) return hostError(input, "IPv6 addresses must be bracketed") + hostText = input.slice(0, separator) + portText = input.slice(separator + 1) + } + if (!/^(0|[1-9]\d*)$/.test(portText)) { + return hostError(input, "port must be an unpadded decimal integer") + } + const port = Number(portText) + if (!isPort(port)) return hostError(input, "port must be an integer from 0 through 65535") + return Result.map(hostFromString(hostText), (host) => makeHostPort(host, port)) +} + +/** + * Parses a trusted `host:port` string, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const hostPortFromStringUnsafe = (input: string): HostPort => Result.getOrThrow(hostPortFromString(input)) + +/** + * Converts a `HostPortInput` to an endpoint, parsing strings like + * `hostPortFromString`, converting parts like `hostPort`, and returning + * endpoints unchanged. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const hostPortFromInput = (input: HostPortInput): Result.Result => { + if (isHostPort(input)) return Result.succeed(input) + if (typeof input === "string") return hostPortFromString(input) + if (!hasProperty(input, "host") || !hasProperty(input, "port")) { + return hostError(input, "expected a host and port") + } + const port = input.port + return Result.flatMap(hostFromInput(input.host), (host) => hostPort(host, port)) +} + +/** + * Converts a trusted `HostPortInput` to an endpoint, throwing on failure. + * + * @stability experimental + * @category unsafe + * @since 4.0.0 + */ +export const hostPortFromInputUnsafe = (input: HostPortInput): HostPort => Result.getOrThrow(hostPortFromInput(input)) + +/** + * Formats a host and port, bracketing IPv6 hosts. + * + * @see {@link hostPortFromString} for the inverse operation + * @stability experimental + * @category encoding + * @since 4.0.0 + */ +export const formatHostPort = (self: HostPort): string => { + const host = formatHost(self.host) + return host.includes(":") ? `[${host}]:${self.port}` : `${host}:${self.port}` +} + +/** + * The addresses of host names, as read from a hosts file. + * + * @see {@link parseHostsFile} for reading a hosts file + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type HostsTable = ReadonlyMap> + +/** + * Parses a hosts file into the addresses of each host name. Invalid entries + * are skipped. + * + * **Example** (Parsing a hosts file) + * + * ```ts import.meta.vitest + * import { Host, NetAddress } from "effect/net" + * + * const hosts = Host.parseHostsFile(` + * 127.0.0.1 localhost + * ::1 localhost ip6-localhost # loopback + * `) + * hosts.get(Host.domainNameFromStringUnsafe("localhost"))?.map(NetAddress.formatIp) // => ["127.0.0.1", "::1"] + * ``` + * + * @stability experimental + * @category decoding + * @since 4.0.0 + */ +export const parseHostsFile = (text: string): HostsTable => { + const hosts = new Map>() + for (const line of text.split(/\r?\n/)) { + const comment = line.indexOf("#") + const [first, ...names] = (comment === -1 ? line : line.slice(0, comment)).trim().split(/\s+/) + const address = NetAddress.ipFromString(first) + if (Result.isFailure(address)) continue + for (const name of names) { + const domain = domainNameFromString(name) + if (Result.isFailure(domain)) continue + const key = toRelative(domain.success) + const addresses = hosts.get(key) + if (addresses === undefined) hosts.set(key, [address.success]) + else if (!addresses.some((existing) => Equal.equals(existing, address.success))) addresses.push(address.success) + } + } + return hosts as unknown as HostsTable +} diff --git a/repos/effect/packages/effect/src/net/IpInterface.ts b/packages/effect/src/net/IpInterface.ts similarity index 65% rename from repos/effect/packages/effect/src/net/IpInterface.ts rename to packages/effect/src/net/IpInterface.ts index d5e55e28d..a61f5633d 100644 --- a/repos/effect/packages/effect/src/net/IpInterface.ts +++ b/packages/effect/src/net/IpInterface.ts @@ -6,7 +6,7 @@ */ import * as Equal from "../Equal.ts" import * as Hash from "../Hash.ts" -import { NodeInspectSymbol } from "../Inspectable.ts" +import * as Inspectable from "../Inspectable.ts" import { hasProperty } from "../Predicate.ts" import * as Result from "../Result.ts" import * as NetAddress from "./NetAddress.ts" @@ -23,13 +23,13 @@ const TypeId = "~effect/net/IpInterface" as const * @category models * @since 4.0.0 */ -export interface IpInterface extends Equal.Equal, Hash.Hash { +export interface IpInterface + extends Equal.Equal, Hash.Hash, Inspectable.Inspectable +{ readonly _tag: "IpInterface" readonly address: A readonly prefixLength: number readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -50,6 +50,46 @@ export type Ipv4Interface = IpInterface */ export type Ipv6Interface = IpInterface +/** + * An address and prefix length, generic over the accepted address input. + * + * @see {@link make} for the checked constructor + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface IpInterfaceParts { + readonly address: A + readonly prefixLength: number +} + +/** + * An interface address, or a string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type IpInterfaceInput = IpInterface | string | IpInterfaceParts + +/** + * An IPv4 interface address, or a string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv4InterfaceInput = Ipv4Interface | string | IpInterfaceParts + +/** + * An IPv6 interface address, or a string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv6InterfaceInput = Ipv6Interface | string | IpInterfaceParts + /** * Companion types for parsing IP interface addresses. * @@ -100,6 +140,7 @@ export const isIpv6Interface = (u: unknown): u is Ipv6Interface => export const isIpInterface = (u: unknown): u is IpInterface => hasProperty(u, TypeId) const IpInterfaceProto = { + ...Inspectable.BaseProto, _tag: "IpInterface", [TypeId]: TypeId, [Equal.symbol](this: IpInterface, that: Equal.Equal): boolean { @@ -115,9 +156,6 @@ const IpInterfaceProto = { }, toJSON(this: IpInterface): string { return this.toString() - }, - [NodeInspectSymbol](this: IpInterface): string { - return this.toJSON() } } @@ -231,6 +269,78 @@ export const fromString = ( make(address, parts.prefixLength ?? NetAddress.width(address))) ) +const fromParts = ( + input: IpInterfaceParts, + toAddress: (input: I) => Result.Result +): Result.Result, NetAddress.NetAddressError> => + hasProperty(input, "address") && hasProperty(input, "prefixLength") + ? Result.flatMap(toAddress(input.address), (address) => make(address, input.prefixLength)) + : interfaceError(input, "expected an address and prefix length") + +/** + * Converts an `Ipv4InterfaceInput` to an IPv4 interface address, parsing + * strings like `ipv4FromString` with the given options, converting parts like + * `make`, and returning interface addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipv4FromInput = ( + input: Ipv4InterfaceInput, + options?: IpInterface.ParseOptions +): Result.Result => { + if (isIpv4Interface(input)) return Result.succeed(input) + if (typeof input === "string") return ipv4FromString(input, options) + return fromParts(input, NetAddress.ipv4FromInput) +} + +/** + * Converts an `Ipv6InterfaceInput` to an IPv6 interface address, parsing + * strings like `ipv6FromString` with the given options, converting parts like + * `make`, and returning interface addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipv6FromInput = ( + input: Ipv6InterfaceInput, + options?: IpInterface.ParseOptions +): Result.Result => { + if (isIpv6Interface(input)) return Result.succeed(input) + if (typeof input === "string") return ipv6FromString(input, options) + return fromParts(input, NetAddress.ipv6FromInput) +} + +/** + * Converts an `IpInterfaceInput` to an interface address, parsing strings like + * `fromString` with the given options, converting parts like `make`, and + * returning interface addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const fromInput = ( + input: IpInterfaceInput, + options?: IpInterface.ParseOptions +): Result.Result => { + if (isIpInterface(input)) return Result.succeed(input) + if (typeof input === "string") return fromString(input, options) + return fromParts(input, NetAddress.ipFromInput) +} + +/** + * Converts a trusted `IpInterfaceInput` to an interface address, throwing on failure. + * + * @stability unstable + * @category unsafe + * @since 4.0.0 + */ +export const fromInputUnsafe = (input: IpInterfaceInput, options?: IpInterface.ParseOptions): IpInterface => + Result.getOrThrow(fromInput(input, options)) + /** * Creates a trusted interface address, throwing when its prefix length is invalid. * diff --git a/repos/effect/packages/effect/src/net/IpNetwork.ts b/packages/effect/src/net/IpNetwork.ts similarity index 71% rename from repos/effect/packages/effect/src/net/IpNetwork.ts rename to packages/effect/src/net/IpNetwork.ts index 995546b21..7aea3066f 100644 --- a/repos/effect/packages/effect/src/net/IpNetwork.ts +++ b/packages/effect/src/net/IpNetwork.ts @@ -7,7 +7,7 @@ import * as Equal from "../Equal.ts" import { dual } from "../Function.ts" import * as Hash from "../Hash.ts" -import { NodeInspectSymbol } from "../Inspectable.ts" +import * as Inspectable from "../Inspectable.ts" import { hasProperty } from "../Predicate.ts" import * as Result from "../Result.ts" import * as IpInterface from "./IpInterface.ts" @@ -25,13 +25,13 @@ const TypeId = "~effect/net/IpNetwork" as const * @category models * @since 4.0.0 */ -export interface IpNetwork extends Equal.Equal, Hash.Hash { +export interface IpNetwork + extends Equal.Equal, Hash.Hash, Inspectable.Inspectable +{ readonly _tag: "IpNetwork" readonly address: A readonly prefixLength: number readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -52,6 +52,45 @@ export type Ipv4Network = IpNetwork */ export type Ipv6Network = IpNetwork +/** + * A network address and prefix length, generic over the accepted address + * input. The address must have no host bits set. + * + * @see {@link make} for the checked constructor + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type IpNetworkParts = + IpInterface.IpInterfaceParts + +/** + * An IP network prefix, or a CIDR string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type IpNetworkInput = IpNetwork | string | IpNetworkParts + +/** + * An IPv4 network prefix, or a CIDR string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv4NetworkInput = Ipv4Network | string | IpNetworkParts + +/** + * An IPv6 network prefix, or a CIDR string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv6NetworkInput = Ipv6Network | string | IpNetworkParts + /** * Returns `true` when a value is an IPv4 network prefix. * @@ -80,6 +119,7 @@ export const isIpv6Network = (u: unknown): u is Ipv6Network => isIpNetwork(u) && export const isIpNetwork = (u: unknown): u is IpNetwork => hasProperty(u, TypeId) const IpNetworkProto = { + ...Inspectable.BaseProto, _tag: "IpNetwork", [TypeId]: TypeId, [Equal.symbol](this: IpNetwork, that: Equal.Equal): boolean { @@ -93,9 +133,6 @@ const IpNetworkProto = { }, toJSON(this: IpNetwork): string { return this.toString() - }, - [NodeInspectSymbol](this: IpNetwork): string { - return this.toJSON() } } @@ -110,18 +147,14 @@ const fromInterfaceValue = (value: IpInterface.I const toBytes = (address: NetAddress.IpAddress): ReadonlyArray => NetAddress.isIpv4Address(address) ? NetAddress.ipv4ToOctets(address) : NetAddress.ipv6ToOctets(address) -type IpAddressFamily = A extends NetAddress.Ipv4Address ? NetAddress.Ipv4Address - : A extends NetAddress.Ipv6Address ? NetAddress.Ipv6Address - : never - const fromBytes = ( address: A, bytes: ReadonlyArray -): IpAddressFamily => { +): NetAddress.Family => { const array = new Uint8Array(bytes) return (NetAddress.isIpv4Address(address) ? NetAddress.ipv4FromBytesUnsafe(array) - : NetAddress.ipv6FromBytesUnsafe(array)) as IpAddressFamily + : NetAddress.ipv6FromBytesUnsafe(array)) as NetAddress.Family } const maskBytes = (bytes: ReadonlyArray, prefixLength: number): Array => { @@ -177,7 +210,7 @@ export const make = ( export const fromAddress = ( address: A, prefixLength: number -): Result.Result>, NetAddress.NetAddressError> => { +): Result.Result>, NetAddress.NetAddressError> => { return Result.map(IpInterface.make(address, prefixLength), (value) => { const masked = fromBytes(address, maskBytes(toBytes(address), prefixLength)) return fromInterfaceValue(IpInterface.makeUnsafe(masked, value.prefixLength)) @@ -194,7 +227,7 @@ export const fromAddress = ( */ export const fromInterface = ( self: IpInterface.IpInterface -): IpNetwork> => fromAddressUnsafe(self.address, self.prefixLength) +): IpNetwork> => fromAddressUnsafe(self.address, self.prefixLength) /** * Parses a strict IPv4 network prefix in CIDR notation. @@ -238,6 +271,72 @@ export const fromString = (input: string): Result.Result( + input: IpNetworkParts, + toAddress: (input: I) => Result.Result +): Result.Result, NetAddress.NetAddressError> => + hasProperty(input, "address") && hasProperty(input, "prefixLength") + ? Result.flatMap(toAddress(input.address), (address) => make(address, input.prefixLength)) + : Result.fail(new NetAddress.NetAddressError({ input, message: "expected an address and prefix length" })) + +/** + * Converts an `Ipv4NetworkInput` to an IPv4 network prefix, parsing strings + * like `ipv4FromString`, converting parts like `make`, and returning network + * prefixes unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipv4FromInput = (input: Ipv4NetworkInput): Result.Result => { + if (isIpv4Network(input)) return Result.succeed(input) + if (typeof input === "string") return ipv4FromString(input) + return fromParts(input, NetAddress.ipv4FromInput) +} + +/** + * Converts an `Ipv6NetworkInput` to an IPv6 network prefix, parsing strings + * like `ipv6FromString`, converting parts like `make`, and returning network + * prefixes unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipv6FromInput = (input: Ipv6NetworkInput): Result.Result => { + if (isIpv6Network(input)) return Result.succeed(input) + if (typeof input === "string") return ipv6FromString(input) + return fromParts(input, NetAddress.ipv6FromInput) +} + +/** + * Converts an `IpNetworkInput` to a network prefix, parsing strings like + * `fromString`, converting parts like `make`, and returning network prefixes + * unchanged. + * + * **Gotchas** + * + * Like `make`, an address with host bits set is rejected rather than truncated. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const fromInput = (input: IpNetworkInput): Result.Result => { + if (isIpNetwork(input)) return Result.succeed(input) + if (typeof input === "string") return fromString(input) + return fromParts(input, NetAddress.ipFromInput) +} + +/** + * Converts a trusted `IpNetworkInput` to a network prefix, throwing on failure. + * + * @stability unstable + * @category unsafe + * @since 4.0.0 + */ +export const fromInputUnsafe = (input: IpNetworkInput): IpNetwork => Result.getOrThrow(fromInput(input)) + /** * Creates a trusted network prefix, throwing when its address or prefix is invalid. * @@ -260,7 +359,7 @@ export const makeUnsafe = (address: A, prefixLen export const fromAddressUnsafe = ( address: A, prefixLength: number -): IpNetwork> => Result.getOrThrow(fromAddress(address, prefixLength)) +): IpNetwork> => Result.getOrThrow(fromAddress(address, prefixLength)) /** * Parses a trusted network prefix in CIDR notation, throwing on failure. @@ -298,7 +397,7 @@ export const firstAddress = (self: IpNetwork) * @category getters * @since 4.0.0 */ -export const lastAddress = (self: IpNetwork): IpAddressFamily => { +export const lastAddress = (self: IpNetwork): NetAddress.Family => { const bytes = toBytes(self.address) const networkMask = maskBytes(bytes.map(() => 0xff), self.prefixLength) return fromBytes(self.address, bytes.map((byte, index) => byte | (networkMask[index] ^ 0xff))) diff --git a/repos/effect/packages/effect/src/net/NetAddress.ts b/packages/effect/src/net/NetAddress.ts similarity index 73% rename from repos/effect/packages/effect/src/net/NetAddress.ts rename to packages/effect/src/net/NetAddress.ts index 6867c3d5f..3e840663b 100644 --- a/repos/effect/packages/effect/src/net/NetAddress.ts +++ b/packages/effect/src/net/NetAddress.ts @@ -9,10 +9,11 @@ import * as Data from "../Data.ts" import * as Equal from "../Equal.ts" import { dual } from "../Function.ts" import * as Hash from "../Hash.ts" -import { NodeInspectSymbol } from "../Inspectable.ts" +import * as Inspectable from "../Inspectable.ts" import * as Option from "../Option.ts" -import { hasProperty } from "../Predicate.ts" +import { hasProperty, isTupleOf } from "../Predicate.ts" import * as Result from "../Result.ts" +import type { TupleOf } from "../Types.ts" const TypeId = "~effect/net/NetAddress" as const @@ -23,11 +24,9 @@ const TypeId = "~effect/net/NetAddress" as const * @category models * @since 4.0.0 */ -export interface Ipv4Address extends Equal.Equal, Hash.Hash { +export interface Ipv4Address extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { readonly _tag: "Ipv4Address" readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -37,11 +36,9 @@ export interface Ipv4Address extends Equal.Equal, Hash.Hash { * @category models * @since 4.0.0 */ -export interface Ipv6Address extends Equal.Equal, Hash.Hash { +export interface Ipv6Address extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { readonly _tag: "Ipv6Address" readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -53,6 +50,89 @@ export interface Ipv6Address extends Equal.Equal, Hash.Hash { */ export type IpAddress = Ipv4Address | Ipv6Address +/** + * The four octets of an IPv4 address in network order. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv4Octets = Readonly> + +/** + * The eight 16-bit segments of an IPv6 address in network order. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv6Segments = Readonly> + +/** + * The six octets of a MAC address in transmission order. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type MacAddressOctets = Readonly> + +/** + * An IPv4 address, or a string, four octets, or four network-order bytes to + * convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv4AddressInput = Ipv4Address | string | Ipv4Octets | Uint8Array + +/** + * An IPv6 address, or a string, eight 16-bit segments, or sixteen + * network-order bytes to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type Ipv6AddressInput = Ipv6Address | string | Ipv6Segments | Uint8Array + +/** + * An IP address, or any `Ipv4AddressInput` or `Ipv6AddressInput` to convert to + * one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type IpAddressInput = Ipv4AddressInput | Ipv6AddressInput + +/** + * An IPv6 literal with a zone, such as `fe80::1%eth0` or `fe80::1%3`. + * + * **Details** + * + * Named zones are kept as written because mapping an interface name to a + * numeric scope ID requires the operating system's interface table. Numeric + * zones must be unsigned 32-bit integers and are stored without leading zeros, + * and the address part is stored in canonical form. + * + * @see {@link scopedIpv6LiteralFromString} for parsing scoped IPv6 literals + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type ScopedIpv6Literal = Brand.Branded + +/** + * A scoped IPv6 literal or a string to parse as one. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type ScopedIpv6LiteralInput = ScopedIpv6Literal | string + /** * An immutable 48-bit IEEE 802 MAC address. * @@ -60,13 +140,20 @@ export type IpAddress = Ipv4Address | Ipv6Address * @category models * @since 4.0.0 */ -export interface MacAddress extends Equal.Equal, Hash.Hash { +export interface MacAddress extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { readonly _tag: "MacAddress" readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } +/** + * A MAC address, or a string or six octets to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type MacAddressInput = MacAddress | string | MacAddressOctets + const MulticastTypeId = "~effect/net/NetAddress/MulticastAddress" as const const UnicastTypeId = "~effect/net/NetAddress/UnicastAddress" as const const BroadcastTypeId = "~effect/net/NetAddress/BroadcastAddress" as const @@ -228,13 +315,11 @@ const getMacBytes = (self: MacAddress): Uint8Array => (self as any).bytes * @category models * @since 4.0.0 */ -export interface InetAddressV4 extends Equal.Equal, Hash.Hash { +export interface InetAddressV4 extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { readonly _tag: "InetAddressV4" readonly address: Ipv4Address readonly port: number readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -244,14 +329,12 @@ export interface InetAddressV4 extends Equal.Equal, Hash.Hash { * @category models * @since 4.0.0 */ -export interface InetAddressV6 extends Equal.Equal, Hash.Hash { +export interface InetAddressV6 extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { readonly _tag: "InetAddressV6" readonly address: Ipv6Address readonly port: number readonly scopeId: number readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -263,6 +346,32 @@ export interface InetAddressV6 extends Equal.Equal, Hash.Hash { */ export type InetAddress = InetAddressV4 | InetAddressV6 +/** + * The address, port, and optional IPv6 scope identifier of an internet address. + * + * **Details** + * + * A nonzero `scopeId` is only valid with an IPv6 address. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface InetAddressParts { + readonly address: A + readonly port: number + readonly scopeId?: number | undefined +} + +/** + * An internet address and port, or a string or parts to convert to one. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export type InetAddressInput = InetAddress | string | InetAddressParts + /** * An opaque Unix-domain filesystem socket path. * @@ -270,12 +379,10 @@ export type InetAddress = InetAddressV4 | InetAddressV6 * @category models * @since 4.0.0 */ -export interface UnixPathAddress extends Equal.Equal, Hash.Hash { +export interface UnixPathAddress extends Equal.Equal, Hash.Hash, Inspectable.Inspectable { readonly _tag: "UnixPathAddress" readonly path: string readonly [TypeId]: typeof TypeId - toString(): string - toJSON(): string } /** @@ -287,6 +394,17 @@ export interface UnixPathAddress extends Equal.Equal, Hash.Hash { */ export type SocketAddress = InetAddress | UnixPathAddress +/** + * The path of a Unix-domain socket address. + * + * @stability unstable + * @category models + * @since 4.0.0 + */ +export interface UnixPathAddressParts { + readonly path: string +} + /** * Companion types for constructing socket addresses. * @@ -300,18 +418,14 @@ export declare namespace SocketAddress { * * **Details** * - * String inputs must use `IPv4:port` or `[IPv6]:port` notation. Address - * properties must be numeric IPv4 or IPv6 literals. + * Accepts any `InetAddressInput` or a Unix path. String inputs must use + * `IPv4:port` or `[IPv6]:port` notation. * * @stability unstable * @category models * @since 4.0.0 */ - export type Input = - | SocketAddress - | string - | { readonly address: IpAddress | string; readonly port: number } - | { readonly path: string } + export type Input = SocketAddress | InetAddressInput | UnixPathAddressParts } /** @@ -338,6 +452,29 @@ export declare namespace SocketAddress { export type Family = A extends Ipv4Address | InetAddressV4 ? Ipv4Address : Ipv6Address +/** + * The name of an IP address family, spelled as in `os.networkInterfaces()` and + * socket address information on Node.js, Deno, and Bun. + * + * @see {@link familyOf} for reading the family of an address + * @see {@link FamilyAddress} for the address type of a family + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type IpFamily = "IPv4" | "IPv6" + +/** + * The IP address type for an IP address family name. `FamilyAddress` + * is `IpAddress`. + * + * @see {@link Family} for the inverse mapping from an address type + * @stability experimental + * @category models + * @since 4.0.0 + */ +export type FamilyAddress = F extends "IPv4" ? Ipv4Address : Ipv6Address + /** * The internet address type for an IP address family. * @@ -444,6 +581,49 @@ export const isIpAddress = (u: unknown): u is IpAddress => isIpv4Address(u) || i */ export const width = (address: IpAddress): 32 | 128 => isIpv4Address(address) ? 32 : 128 +/** + * Returns the family of an IP address or internet address. + * + * **Example** (Reading address families) + * + * ```ts import.meta.vitest + * import { NetAddress } from "effect/net" + * + * NetAddress.familyOf(NetAddress.ipv4Loopback) // => "IPv4" + * NetAddress.familyOf(NetAddress.inetAddressFromStringUnsafe("[::1]:80")) // => "IPv6" + * ``` + * + * @stability experimental + * @category getters + * @since 4.0.0 + */ +export const familyOf = (self: IpAddress | InetAddress): IpFamily => + isIpv4Address(self) || isInetAddressV4(self) ? "IPv4" : "IPv6" + +/** + * Returns `true` when an IP address or internet address belongs to a family, + * narrowing it to that family. + * + * @stability experimental + * @category predicates + * @since 4.0.0 + */ +export const isFamily: { + ( + family: "IPv4" + ): (self: A) => self is Extract + ( + family: "IPv6" + ): (self: A) => self is Extract + (family: IpFamily): (self: IpAddress | InetAddress) => boolean + (self: A, family: "IPv4"): self is Extract + (self: A, family: "IPv6"): self is Extract + (self: IpAddress | InetAddress, family: IpFamily): boolean +} = dual( + 2, + (self: A, family: IpFamily): self is A => familyOf(self) === family +) + /** * Returns `true` when a value is a MAC address. * @@ -499,6 +679,7 @@ export const isUnixPathAddress = (u: unknown): u is UnixPathAddress => isAddress export const isSocketAddress = (u: unknown): u is SocketAddress => isInetAddress(u) || isUnixPathAddress(u) const Ipv4Proto = { + ...Inspectable.BaseProto, _tag: "Ipv4Address", [TypeId]: TypeId, [Equal.symbol](this: Ipv4Address, that: Equal.Equal): boolean { @@ -512,13 +693,11 @@ const Ipv4Proto = { }, toJSON(this: Ipv4Address): string { return this.toString() - }, - [NodeInspectSymbol](this: Ipv4Address): string { - return this.toJSON() } } const Ipv6Proto = { + ...Inspectable.BaseProto, _tag: "Ipv6Address", [TypeId]: TypeId, [Equal.symbol](this: Ipv6Address, that: Equal.Equal): boolean { @@ -539,13 +718,11 @@ const Ipv6Proto = { }, toJSON(this: Ipv6Address): string { return this.toString() - }, - [NodeInspectSymbol](this: Ipv6Address): string { - return this.toJSON() } } const MacProto = { + ...Inspectable.BaseProto, _tag: "MacAddress", [TypeId]: TypeId, [Equal.symbol](this: MacAddress, that: Equal.Equal): boolean { @@ -559,9 +736,6 @@ const MacProto = { }, toJSON(this: MacAddress): string { return this.toString() - }, - [NodeInspectSymbol](this: MacAddress): string { - return this.toJSON() } } @@ -597,7 +771,7 @@ const packOctets = (a: number, b: number, c: number, d: number): number => ((a < const packBytes = (bytes: Uint8Array, offset: number): number => packOctets(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]) -const wordToOctets = (word: number): readonly [number, number, number, number] => [ +const wordToOctets = (word: number): Ipv4Octets => [ word >>> 24, (word >>> 16) & 0xff, (word >>> 8) & 0xff, @@ -646,6 +820,15 @@ export const ipv4Loopback: Ipv4Address = makeIpv4(0x7f000001) */ export const ipv6Loopback: Ipv6Address = makeIpv6(0, 0, 0, 1) +/** + * The loopback addresses `127.0.0.1` and `::1`. + * + * @stability unstable + * @category constants + * @since 4.0.0 + */ +export const loopbackAddresses: readonly [Ipv4Address, Ipv6Address] = [ipv4Loopback, ipv6Loopback] + /** * The unspecified IPv4 address `0.0.0.0`. * @@ -676,6 +859,15 @@ export const ipv4Broadcast: Ipv4Address = makeIpv4(0xffffffff) const addressError = (input: unknown, message: string): Result.Result => Result.fail(new NetAddressError({ input, message })) +// Checks by index because `every` skips holes in sparse arrays. +const allIntegersInRange = (values: ReadonlyArray, max: number): boolean => { + for (let i = 0; i < values.length; i++) { + const n = values[i] + if (!Number.isInteger(n) || n < 0 || n > max) return false + } + return true +} + /** * Creates an IPv4 address from four checked octets. * @@ -684,9 +876,9 @@ const addressError = (input: unknown, message: string): Result.Result => { - if (!octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255)) { + if (!allIntegersInRange(octets, 255)) { return addressError(octets, "octets must be integers from 0 through 255") } return Result.succeed(makeIpv4(packOctets(octets[0], octets[1], octets[2], octets[3]))) @@ -700,9 +892,9 @@ export const ipv4FromOctets = ( * @since 4.0.0 */ export const ipv6FromSegments = ( - segments: readonly [number, number, number, number, number, number, number, number] + segments: Ipv6Segments ): Result.Result => { - if (!segments.every((n) => Number.isInteger(n) && n >= 0 && n <= 0xffff)) { + if (!allIntegersInRange(segments, 0xffff)) { return addressError(segments, "segments must be integers from 0 through 65535") } return Result.succeed(makeIpv6( @@ -721,9 +913,9 @@ export const ipv6FromSegments = ( * @since 4.0.0 */ export const macAddressFromOctets = ( - octets: readonly [number, number, number, number, number, number] + octets: MacAddressOctets ): Result.Result => { - if (!octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255)) { + if (!allIntegersInRange(octets, 255)) { return addressError(octets, "octets must be integers from 0 through 255") } return Result.succeed(makeMac(new Uint8Array(octets))) @@ -752,6 +944,32 @@ export const macAddressFromString = (input: string): Result.Result Result.getOrThrow(macAddressFromString(input)) +/** + * Converts a `MacAddressInput` to a MAC address, parsing strings like + * `macAddressFromString`, checking octets like `macAddressFromOctets`, and + * returning MAC addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const macAddressFromInput = (input: MacAddressInput): Result.Result => { + if (isMacAddress(input)) return Result.succeed(input) + if (typeof input === "string") return macAddressFromString(input) + if (Array.isArray(input) && isTupleOf(input, 6)) return macAddressFromOctets(input) + return addressError(input, "expected a MAC address, string, or 6 octets") +} + +/** + * Converts a trusted `MacAddressInput` to a MAC address, throwing on failure. + * + * @stability unstable + * @category unsafe + * @since 4.0.0 + */ +export const macAddressFromInputUnsafe = (input: MacAddressInput): MacAddress => + Result.getOrThrow(macAddressFromInput(input)) + /** * Parses a strict dotted-decimal IPv4 address. * @@ -875,6 +1093,141 @@ export const ipFromString = (input: string): Result.Result Result.getOrThrow(ipFromString(input)) +/** + * Converts an `IpAddressInput` to an IP address, returning IP addresses + * unchanged. + * + * **Details** + * + * Strings are parsed like `ipFromString`. Four octets or bytes make an IPv4 + * address, and eight segments or sixteen bytes make an IPv6 address. + * + * **Example** (Converting IP address inputs) + * + * ```ts import.meta.vitest + * import { Result } from "effect" + * import { NetAddress } from "effect/net" + * + * const address = Result.getOrThrow(NetAddress.ipFromInput("192.0.2.1")) + * NetAddress.formatIp(address) // => "192.0.2.1" + * Result.getOrThrow(NetAddress.ipFromInput(address)) === address // => true + * NetAddress.formatIp(Result.getOrThrow(NetAddress.ipFromInput([192, 0, 2, 1]))) // => "192.0.2.1" + * Result.isFailure(NetAddress.ipFromInput("example.com")) // => true + * ``` + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipFromInput = (input: IpAddressInput): Result.Result => { + if (isIpAddress(input)) return Result.succeed(input) + if (typeof input === "string") return ipFromString(input) + if (input instanceof Uint8Array && input.length === 4) return Result.succeed(ipv4FromBytesUnsafe(input)) + if (input instanceof Uint8Array && input.length === 16) return Result.succeed(ipv6FromBytesUnsafe(input)) + if (Array.isArray(input) && isTupleOf(input, 4)) return ipv4FromOctets(input) + if (Array.isArray(input) && isTupleOf(input, 8)) return ipv6FromSegments(input) + return addressError(input, "expected an IP address, string, 4 octets, 8 segments, or 4 or 16 bytes") +} + +/** + * Converts a trusted `IpAddressInput` to an IP address, throwing on failure. + * + * @stability unstable + * @category unsafe + * @since 4.0.0 + */ +export const ipFromInputUnsafe = (input: IpAddressInput): IpAddress => Result.getOrThrow(ipFromInput(input)) + +/** + * Converts an `Ipv4AddressInput` to an IPv4 address, parsing strings like + * `ipv4FromString`, checking octets like `ipv4FromOctets`, and returning IPv4 + * addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipv4FromInput = (input: Ipv4AddressInput): Result.Result => { + if (isIpv4Address(input)) return Result.succeed(input) + if (typeof input === "string") return ipv4FromString(input) + if (input instanceof Uint8Array && input.length === 4) return Result.succeed(ipv4FromBytesUnsafe(input)) + if (Array.isArray(input) && isTupleOf(input, 4)) return ipv4FromOctets(input) + return addressError(input, "expected an IPv4 address, string, 4 octets, or 4 bytes") +} + +/** + * Converts an `Ipv6AddressInput` to an IPv6 address, parsing strings like + * `ipv6FromString`, checking segments like `ipv6FromSegments`, and returning + * IPv6 addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const ipv6FromInput = (input: Ipv6AddressInput): Result.Result => { + if (isIpv6Address(input)) return Result.succeed(input) + if (typeof input === "string") return ipv6FromString(input) + if (input instanceof Uint8Array && input.length === 16) return Result.succeed(ipv6FromBytesUnsafe(input)) + if (Array.isArray(input) && isTupleOf(input, 8)) return ipv6FromSegments(input) + return addressError(input, "expected an IPv6 address, string, 8 segments, or 16 bytes") +} + +/** + * Returns `true` when a value is a canonical scoped IPv6 literal. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isScopedIpv6Literal = (u: unknown): u is ScopedIpv6Literal => { + if (typeof u !== "string" || !u.includes("%")) return false + const result = scopedIpv6LiteralFromString(u) + return Result.isSuccess(result) && result.success === u +} + +/** + * Parses an IPv6 literal with a numeric or named zone, such as `fe80::1%eth0`. + * + * @stability experimental + * @category decoding + * @since 4.0.0 + */ +export const scopedIpv6LiteralFromString = ( + input: string +): Result.Result => { + const separator = input.indexOf("%") + if (separator === -1) return addressError(input, "expected an IPv6 zone") + let zone = input.slice(separator + 1) + if (zone.length === 0 || /[%[\]/\s\p{Cc}]/u.test(zone)) { + return addressError(input, "invalid IPv6 scope identifier") + } + if (/^\d+$/.test(zone)) { + const scopeId = Number(zone) + if (scopeId > 0xffffffff) return addressError(input, "scope identifier must be an unsigned 32-bit integer") + zone = String(scopeId) + } + return Result.map( + ipv6FromString(input.slice(0, separator)), + (address) => `${formatIp(address)}%${zone}` as ScopedIpv6Literal + ) +} + +/** + * Converts a `ScopedIpv6LiteralInput` to a scoped IPv6 literal, parsing it + * like `scopedIpv6LiteralFromString`. + * + * **Details** + * + * Scoped IPv6 literals are strings, so every input is parsed. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const scopedIpv6LiteralFromInput = ( + input: ScopedIpv6LiteralInput +): Result.Result => scopedIpv6LiteralFromString(input) + /** * Returns the four numeric octets of an IPv4 address in a fresh tuple. * @@ -882,8 +1235,7 @@ export const ipFromStringUnsafe = (input: string): IpAddress => Result.getOrThro * @category getters * @since 4.0.0 */ -export const ipv4ToOctets = (self: Ipv4Address): readonly [number, number, number, number] => - wordToOctets(ipv4Value(self)) +export const ipv4ToOctets = (self: Ipv4Address): Ipv4Octets => wordToOctets(ipv4Value(self)) /** * Returns the eight numeric segments of an IPv6 address in a fresh tuple. @@ -892,9 +1244,7 @@ export const ipv4ToOctets = (self: Ipv4Address): readonly [number, number, numbe * @category getters * @since 4.0.0 */ -export const ipv6ToSegments = ( - self: Ipv6Address -): readonly [number, number, number, number, number, number, number, number] => { +export const ipv6ToSegments = (self: Ipv6Address): Ipv6Segments => { const { w0, w1, w2, w3 } = ipv6Words(self) return [w0 >>> 16, w0 & 0xffff, w1 >>> 16, w1 & 0xffff, w2 >>> 16, w2 & 0xffff, w3 >>> 16, w3 & 0xffff] } @@ -920,9 +1270,7 @@ export const ipv6ToOctets = ( * @category getters * @since 4.0.0 */ -export const macAddressToOctets = ( - self: MacAddress -): readonly [number, number, number, number, number, number] => { +export const macAddressToOctets = (self: MacAddress): MacAddressOctets => { const bytes = getMacBytes(self) return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]] } @@ -1235,6 +1583,7 @@ export function toCanonical(self: IpAddress | InetAddress): IpAddress | InetAddr } const InetV4Proto = { + ...Inspectable.BaseProto, _tag: "InetAddressV4", [TypeId]: TypeId, [Equal.symbol](this: InetAddressV4, that: Equal.Equal): boolean { @@ -1248,13 +1597,11 @@ const InetV4Proto = { }, toJSON(this: InetAddressV4): string { return this.toString() - }, - [NodeInspectSymbol](this: InetAddressV4): string { - return this.toJSON() } } const InetV6Proto = { + ...Inspectable.BaseProto, _tag: "InetAddressV6", [TypeId]: TypeId, [Equal.symbol](this: InetAddressV6, that: Equal.Equal): boolean { @@ -1272,9 +1619,6 @@ const InetV6Proto = { }, toJSON(this: InetAddressV6): string { return this.toString() - }, - [NodeInspectSymbol](this: InetAddressV6): string { - return this.toJSON() } } @@ -1377,6 +1721,112 @@ export const inetAddressFromIpString = ( export const inetAddressFromIpStringUnsafe = (address: string, port: number): InetAddress => Result.getOrThrow(inetAddressFromIpString(address, port)) +/** + * Creates an internet address from a trusted native numeric host and port. + * Named IPv6 zones are resolved using the supplied interface-to-scope map. + * Use only for runtime-supplied socket addresses: this skips input validation, + * so malformed strings or ports may silently produce incorrect values. An + * unknown named IPv6 zone throws rather than losing its scope. + * + * @stability unstable + * @category unsafe + * @since 4.0.0 + */ +export const inetAddressFromNativeUnsafe = ( + host: string, + port: number, + scopeIds?: ReadonlyMap +): InetAddress => { + const zoneStart = host.indexOf("%") + const end = zoneStart === -1 ? host.length : zoneStart + if (host.indexOf(":") === -1) { + const self = Object.create(InetV4Proto) + self.address = makeIpv4(parseNativeIpv4(host, 0, end)) + self.port = port + return Object.freeze(self) + } + + let scopeId = 0 + if (zoneStart !== -1) { + const zone = host.slice(zoneStart + 1) + const resolved = numericZone.test(zone) ? Number(zone) : scopeIds?.get(zone) + if (resolved === undefined) throw new Error(`unknown IPv6 interface: ${zone}`) + scopeId = resolved + } + + let w0 = 0 + let w1 = 0 + let w2 = 0 + let w3 = 0 + let segment = 0 + let i = 0 + while (i < end) { + if (host.charCodeAt(i) === 58) { + // The rest of a compressed address is anchored at the last segment. + i += 2 + if (i === end) break + let remaining = 1 + for (let j = i; j < end; j++) { + if (host.charCodeAt(j) === 58) remaining++ + } + const dot = host.indexOf(".", i) + if (dot !== -1 && dot < end) remaining++ + segment = 8 - remaining + } + const start = i + let value = 0 + while (i < end) { + const ch = host.charCodeAt(i) + if (ch === 58 || ch === 46) break + value = (value << 4) | (ch <= 57 ? ch - 48 : (ch & 0x5f) - 55) + i++ + } + if (i < end && host.charCodeAt(i) === 46) { + w3 = parseNativeIpv4(host, start, end) + break + } + // even segments fill the high half of their 32-bit word + const shifted = value << ((1 - (segment & 1)) * 16) + switch (segment >>> 1) { + case 0: + w0 |= shifted + break + case 1: + w1 |= shifted + break + case 2: + w2 |= shifted + break + default: + w3 |= shifted + } + segment++ + if (i < end && host.charCodeAt(i + 1) !== 58) i++ + } + const self = Object.create(InetV6Proto) + self.address = makeIpv6(w0, w1, w2, w3) + self.port = port + self.scopeId = scopeId + return Object.freeze(self) +} + +const numericZone = /^\d+$/ + +const parseNativeIpv4 = (host: string, start: number, end: number): number => { + let value = 0 + let octet = 0 + for (let i = start; i < end; i++) { + const ch = host.charCodeAt(i) + if (ch === 46) { + value = (value << 8) | octet + octet = 0 + } else { + octet = octet * 10 + ch - 48 + } + } + return ((value << 8) | octet) >>> 0 +} + /** * Parses an unbracketed numeric host and port, resolving named IPv6 zones using * a supplied map of interface names to numeric scope IDs. @@ -1424,7 +1874,33 @@ export const inetAddressFromHostString = ( */ export interface NetworkInterfaceAddress { readonly family: string - readonly scopeid?: number | undefined + readonly scopeid?: number | null | undefined +} + +/** + * Returns the IPv6 scope ID of a network interface from its addresses. + * + * **Details** + * + * The first IPv6 address with a positive scope ID supplies the result. Accepts + * the addresses of one interface, such as `os.networkInterfaces()[name]` or the + * entries of `Deno.networkInterfaces()` with that name, without performing any + * operating-system lookup itself. + * + * @see {@link scopeIdsFromInterfaces} for mapping every interface at once + * @stability experimental + * @category converting + * @since 4.0.0 + */ +export const scopeIdFromInterface = ( + addresses: ReadonlyArray | undefined +): Option.Option => { + for (const address of addresses ?? []) { + if (address.family === "IPv6" && typeof address.scopeid === "number" && address.scopeid > 0) { + return Option.some(address.scopeid) + } + } + return Option.none() } /** @@ -1449,8 +1925,8 @@ export const scopeIdsFromInterfaces = ( ): Map => { const scopeIds = new Map() for (const [name, addresses] of interfaces) { - const address = addresses?.find((address) => address.family === "IPv6" && (address.scopeid ?? 0) > 0) - if (address?.scopeid !== undefined) scopeIds.set(name, address.scopeid) + const scopeId = scopeIdFromInterface(addresses) + if (Option.isSome(scopeId)) scopeIds.set(name, scopeId.value) } return scopeIds } @@ -1520,6 +1996,40 @@ export const inetAddressFromString = (input: string): Result.Result Result.getOrThrow(inetAddressFromString(input)) +/** + * Converts an `InetAddressInput` to an internet address, parsing strings like + * `inetAddressFromString`, converting parts like `inetAddressV4` and + * `inetAddressV6`, and returning internet addresses unchanged. + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const inetAddressFromInput = (input: InetAddressInput): Result.Result => { + if (isInetAddress(input)) return Result.succeed(input) + if (typeof input === "string") return inetAddressFromString(input) + if (!hasProperty(input, "address") || !hasProperty(input, "port")) { + return addressError(input, "expected an address and port") + } + const { port, scopeId } = input + return Result.flatMap(ipFromInput(input.address), (address): Result.Result => { + if (isIpv6Address(address)) return inetAddressV6(address, port, { scopeId }) + return (scopeId ?? 0) === 0 + ? inetAddressV4(address, port) + : addressError(input, "scopeId requires an IPv6 address") + }) +} + +/** + * Converts a trusted `InetAddressInput` to an internet address, throwing on failure. + * + * @stability unstable + * @category unsafe + * @since 4.0.0 + */ +export const inetAddressFromInputUnsafe = (input: InetAddressInput): InetAddress => + Result.getOrThrow(inetAddressFromInput(input)) + /** * Formats the numeric host of an internet address without brackets or a port, * preserving a nonzero IPv6 scope ID as a `%` suffix. @@ -1703,6 +2213,7 @@ export const formatUrlHostString = (host: string): string => host.includes(":") && !host.startsWith("[") ? `[${host}]` : host const UnixPathProto = { + ...Inspectable.BaseProto, _tag: "UnixPathAddress", [TypeId]: TypeId, [Equal.symbol](this: UnixPathAddress, that: Equal.Equal): boolean { @@ -1716,9 +2227,6 @@ const UnixPathProto = { }, toJSON(this: UnixPathAddress): string { return this.toString() - }, - [NodeInspectSymbol](this: UnixPathAddress): string { - return this.toJSON() } } @@ -1754,8 +2262,9 @@ export const formatUnixPath = (self: UnixPathAddress): string => `unix://${self. * * **Details** * - * Numeric IP strings are parsed without hostname resolution. Invalid IP - * literals, ports, and input shapes return a `NetAddressError`. + * Internet addresses are converted like `inetAddressFromInput`, without + * hostname resolution. Invalid IP literals, ports, and input shapes return a + * `NetAddressError`. * * @stability unstable * @category constructors @@ -1764,22 +2273,13 @@ export const formatUnixPath = (self: UnixPathAddress): string => `unix://${self. export const socketAddressFromInput = ( input: SocketAddress.Input ): Result.Result => { - if (isSocketAddress(input)) return Result.succeed(input) - if (typeof input === "string") return inetAddressFromString(input) + if (isUnixPathAddress(input)) return Result.succeed(input) if (hasProperty(input, "path")) { return typeof input.path === "string" ? Result.succeed(unixPathAddress(input.path)) : addressError(input, "path must be a string") } - if (!hasProperty(input, "address") || !hasProperty(input, "port")) { - return addressError(input, "expected an address and port or a Unix path") - } - const address = typeof input.address === "string" - ? ipFromString(input.address) - : isIpAddress(input.address) - ? Result.succeed(input.address) - : addressError(input, "address must be an IP address or numeric IP string") - return Result.flatMap(address, (address) => inetAddress(address, input.port as number)) + return inetAddressFromInput(input) } /** diff --git a/repos/effect/packages/effect/src/net/index.ts b/packages/effect/src/net/index.ts similarity index 58% rename from repos/effect/packages/effect/src/net/index.ts rename to packages/effect/src/net/index.ts index c409bf51e..22f56c45a 100644 --- a/repos/effect/packages/effect/src/net/index.ts +++ b/packages/effect/src/net/index.ts @@ -5,6 +5,24 @@ // @barrel: Auto-generated exports. Do not edit manually. +/** + * @stability experimental + * @since 4.0.0 + */ +export * as AddressResolver from "./AddressResolver.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as Dns from "./Dns.ts" + +/** + * @stability experimental + * @since 4.0.0 + */ +export * as Host from "./Host.ts" + /** * @stability unstable * @since 4.0.0 diff --git a/repos/effect/packages/effect/src/observability/Otlp.ts b/packages/effect/src/observability/Otlp.ts similarity index 91% rename from repos/effect/packages/effect/src/observability/Otlp.ts rename to packages/effect/src/observability/Otlp.ts index 829e67aff..6571fc877 100644 --- a/repos/effect/packages/effect/src/observability/Otlp.ts +++ b/packages/effect/src/observability/Otlp.ts @@ -42,6 +42,11 @@ export const layer = (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly maxBatchSize?: number | undefined readonly tracerContext?: ((primitive: Tracer.EffectPrimitive, span: Tracer.AnySpan) => X) | undefined readonly loggerExportInterval?: Duration.Input | undefined @@ -59,6 +64,7 @@ export const layer = (options: { url: url("/v1/logs"), resource: options.resource, headers: options.headers, + compression: options.compression, exportInterval: options.loggerExportInterval, maxBatchSize: options.maxBatchSize, shutdownTimeout: options.shutdownTimeout, @@ -69,6 +75,7 @@ export const layer = (options: { url: url("/v1/metrics"), resource: options.resource, headers: options.headers, + compression: options.compression, exportInterval: options.metricsExportInterval, shutdownTimeout: options.shutdownTimeout, temporality: options.metricsTemporality @@ -77,6 +84,7 @@ export const layer = (options: { url: url("/v1/traces"), resource: options.resource, headers: options.headers, + compression: options.compression, exportInterval: options.tracerExportInterval, maxBatchSize: options.maxBatchSize, context: options.tracerContext, @@ -138,6 +146,11 @@ export const layerJson: (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly maxBatchSize?: number | undefined readonly tracerContext?: ((primitive: Tracer.EffectPrimitive, span: Tracer.AnySpan) => X) | undefined readonly loggerExportInterval?: Duration.Input | undefined @@ -165,6 +178,11 @@ export const layerProtobuf: (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly maxBatchSize?: number | undefined readonly tracerContext?: ((primitive: Tracer.EffectPrimitive, span: Tracer.AnySpan) => X) | undefined readonly loggerExportInterval?: Duration.Input | undefined diff --git a/repos/effect/packages/effect/src/observability/OtlpExporter.ts b/packages/effect/src/observability/OtlpExporter.ts similarity index 91% rename from repos/effect/packages/effect/src/observability/OtlpExporter.ts rename to packages/effect/src/observability/OtlpExporter.ts index 5b749b870..69d9a288b 100644 --- a/repos/effect/packages/effect/src/observability/OtlpExporter.ts +++ b/packages/effect/src/observability/OtlpExporter.ts @@ -161,6 +161,12 @@ export const layerFlusher: Layer.Layer = Layer.sync(Flusher, () => { * after unhandled failures, and flushes during scope finalization up to * `shutdownTimeout`. * + * Optional gzip compression uses `HttpClientRequest.compressBody` before the + * retry loop, so retries reuse the compressed bytes. Bodies smaller than 1KB + * remain uncompressed. Compression requires runtime `CompressionStream` support; + * compression failures follow the same temporary-disable behavior as transport + * failures and do not fail flushing or shutdown. + * * @stability unstable * @category constructors * @since 4.0.0 @@ -169,6 +175,8 @@ export const make: ( options: { readonly url: string readonly headers: Headers.Input | undefined + /** Gzip uses the default 1KB minimum body size; smaller batches remain uncompressed. */ + readonly compression?: "gzip" | "none" | undefined readonly label: string readonly exportInterval: Duration.Input readonly maxBatchSize: number | "disabled" @@ -218,9 +226,12 @@ export const make: ( const items = buffer buffer = [] const [body, onSuccess] = options.body(items) - return client.execute( - HttpClientRequest.setBody(request, body) - ).pipe( + const batchRequest = HttpClientRequest.setBody(request, body) + // Compress before client.execute so transient retries reuse the encoded bytes. + const response = options.compression === "gzip" + ? HttpClientRequest.compressBody(batchRequest, "gzip").pipe(Effect.flatMap(client.execute)) + : client.execute(batchRequest) + return response.pipe( Effect.andThen(onSuccess), Effect.asVoid, Effect.withTracerEnabled(false) diff --git a/repos/effect/packages/effect/src/observability/OtlpLogger.ts b/packages/effect/src/observability/OtlpLogger.ts similarity index 90% rename from repos/effect/packages/effect/src/observability/OtlpLogger.ts rename to packages/effect/src/observability/OtlpLogger.ts index fa5e97ce5..0eff0f64e 100644 --- a/repos/effect/packages/effect/src/observability/OtlpLogger.ts +++ b/packages/effect/src/observability/OtlpLogger.ts @@ -52,6 +52,11 @@ export const make: ( readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly exportInterval?: Duration.Input | undefined readonly maxBatchSize?: number | undefined readonly shutdownTimeout?: Duration.Input | undefined @@ -73,6 +78,7 @@ export const make: ( label: "OtlpLogger", url: options.url, headers: options.headers, + compression: options.compression, maxBatchSize: options.maxBatchSize ?? 1000, exportInterval: options.exportInterval ?? Duration.seconds(1), body: (data) => [ @@ -118,6 +124,11 @@ export const layer = (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly exportInterval?: Duration.Input | undefined readonly maxBatchSize?: number | undefined readonly shutdownTimeout?: Duration.Input | undefined @@ -131,6 +142,13 @@ export const layer = (options: { /** * Creates an OTLP logs layer from OpenTelemetry configuration. * + * **Details** + * + * Compression accepts "gzip" or "none". + * `OTEL_EXPORTER_OTLP_LOGS_COMPRESSION` takes precedence over + * `OTEL_EXPORTER_OTLP_COMPRESSION`, defaulting to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + * * @stability unstable * @category layers * @since 4.0.0 @@ -156,7 +174,17 @@ export const layerFromConfig = (options?: { return Exporter.layerFlusher } - const { baseTimeout, logsTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({ + const { + baseCompression, + compression, + baseTimeout, + logsTimeout, + exportTimeout, + scheduleDelay, + maxBatchSize + } = yield* Config.all({ + baseCompression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_COMPRESSION")), + compression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_LOGS_COMPRESSION")), baseTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TIMEOUT")), logsTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT")), exportTimeout: Config.option(Config.Int("OTEL_BLRP_EXPORT_TIMEOUT")), @@ -173,6 +201,7 @@ export const layerFromConfig = (options?: { url: endpoint.toString(), resource: options?.resource, headers: options?.headers ?? (yield* OtlpEnv.headers("LOGS")), + compression: Option.getOrElse(Option.firstSomeOf([compression, baseCompression]), () => "none" as const), exportInterval: Option.getOrUndefined(exportInterval), maxBatchSize: Option.getOrUndefined(maxBatchSize), shutdownTimeout: Option.getOrUndefined(shutdownTimeout), @@ -232,7 +261,7 @@ const makeLogRecord = (options: Logger.Options, opts: { } if (options.fiber.cache.span) { - logRecord.traceId = options.fiber.cache.span.traceId + logRecord.traceId = options.fiber.cache.span.traceId.padStart(32, "0") logRecord.spanId = options.fiber.cache.span.spanId } diff --git a/repos/effect/packages/effect/src/observability/OtlpMetrics.ts b/packages/effect/src/observability/OtlpMetrics.ts similarity index 93% rename from repos/effect/packages/effect/src/observability/OtlpMetrics.ts rename to packages/effect/src/observability/OtlpMetrics.ts index a9a8296ea..9089fc9b4 100644 --- a/repos/effect/packages/effect/src/observability/OtlpMetrics.ts +++ b/packages/effect/src/observability/OtlpMetrics.ts @@ -12,7 +12,6 @@ * @stability unstable * @since 4.0.0 */ -import * as Arr from "../Array.ts" import { Clock } from "../Clock.ts" import * as Config from "../Config.ts" import * as Duration from "../Duration.ts" @@ -90,6 +89,11 @@ export const make: (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly exportInterval?: Duration.Input | undefined readonly shutdownTimeout?: Duration.Input | undefined readonly temporality?: AggregationTemporality | undefined @@ -228,27 +232,24 @@ export const make: (options: { break } case "Histogram": { - const size = state.state.buckets.length const currentBuckets = { - boundaries: Arr.allocate(size - 1) as Array, - counts: Arr.allocate(size) as Array + boundaries: [] as Array, + counts: [] as Array } - let idx = 0 let prev = 0 for (const [boundary, value] of state.state.buckets) { - if (idx < size - 1) { - currentBuckets.boundaries[idx] = boundary - } - currentBuckets.counts[idx] = value - prev + if (boundary === Number.POSITIVE_INFINITY) break + currentBuckets.boundaries.push(boundary) + currentBuckets.counts.push(value - prev) prev = value - idx++ } + // Derive overflow from the total count, whether or not boundaries include Infinity. + currentBuckets.counts.push(state.state.count - prev) 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 = previousHistogramState.get(metricKey) @@ -258,17 +259,13 @@ export const make: (options: { reportBucketCounts = currentBuckets.counts.map((c, i) => Math.max(0, c - (previousState.bucketCounts[i] ?? 0)) ) - // For delta, min/max represent the interval's min/max - // We can't compute these from cumulative state, so we use current values - // Note: This is a limitation - true delta min/max would require tracking - // observations within each interval + // Cumulative extrema cannot describe a later delta interval. + reportExtrema = false } nextHistogramState.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() }) } @@ -277,8 +274,7 @@ export const make: (options: { startTimeUnixNano: intervalStartTime, timeUnixNano: nowTime, count: reportCount, - min: reportMin, - max: reportMax, + ...(reportExtrema ? { min: state.state.min, max: state.state.max } : undefined), sum: reportSum, bucketCounts: reportBucketCounts, explicitBounds: currentBuckets.boundaries @@ -396,6 +392,7 @@ export const make: (options: { label: "OtlpMetrics", url: options.url, headers: options.headers, + compression: options.compression, maxBatchSize: "disabled", exportEmpty: true, exportInterval: options.exportInterval ?? Duration.seconds(10), @@ -419,6 +416,11 @@ export const layer = (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly exportInterval?: Duration.Input | undefined readonly shutdownTimeout?: Duration.Input | undefined readonly temporality?: AggregationTemporality | undefined @@ -428,6 +430,13 @@ export const layer = (options: { /** * Creates an OTLP metrics layer from OpenTelemetry configuration. * + * **Details** + * + * Compression accepts "gzip" or "none". + * `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` takes precedence over + * `OTEL_EXPORTER_OTLP_COMPRESSION`, defaulting to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + * * @stability unstable * @category layers * @since 4.0.0 @@ -450,7 +459,17 @@ export const layerFromConfig = (options?: { return Exporter.layerFlusher } - const { baseTimeout, metricsTimeout, exportTimeout, exportInterval, temporalityPreference } = yield* Config.all({ + const { + baseCompression, + compression, + baseTimeout, + metricsTimeout, + exportTimeout, + exportInterval, + temporalityPreference + } = yield* Config.all({ + baseCompression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_COMPRESSION")), + compression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION")), baseTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TIMEOUT")), metricsTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT")), exportTimeout: Config.option(Config.Int("OTEL_METRIC_EXPORT_TIMEOUT")), @@ -472,6 +491,7 @@ export const layerFromConfig = (options?: { url: endpoint.toString(), resource: options?.resource, headers: options?.headers ?? (yield* OtlpEnv.headers("METRICS")), + compression: Option.getOrElse(Option.firstSomeOf([compression, baseCompression]), () => "none" as const), exportInterval: Option.getOrUndefined(exportInterval), shutdownTimeout: Option.getOrUndefined(shutdownTimeout), temporality: Option.getOrUndefined(temporalityPreference) @@ -519,8 +539,6 @@ interface PreviousHistogramState { readonly count: number readonly sum: number readonly bucketCounts: Array - readonly min: number - readonly max: number } /** Properties of an InstrumentationScope. */ diff --git a/repos/effect/packages/effect/src/observability/OtlpResource.ts b/packages/effect/src/observability/OtlpResource.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/OtlpResource.ts rename to packages/effect/src/observability/OtlpResource.ts diff --git a/repos/effect/packages/effect/src/observability/OtlpSerialization.ts b/packages/effect/src/observability/OtlpSerialization.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/OtlpSerialization.ts rename to packages/effect/src/observability/OtlpSerialization.ts diff --git a/repos/effect/packages/effect/src/observability/OtlpTracer.ts b/packages/effect/src/observability/OtlpTracer.ts similarity index 91% rename from repos/effect/packages/effect/src/observability/OtlpTracer.ts rename to packages/effect/src/observability/OtlpTracer.ts index c6f6197d5..2a6892222 100644 --- a/repos/effect/packages/effect/src/observability/OtlpTracer.ts +++ b/packages/effect/src/observability/OtlpTracer.ts @@ -55,6 +55,11 @@ export const make: ( readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly exportInterval?: Duration.Input | undefined readonly maxBatchSize?: number | undefined readonly context?: ((primitive: Tracer.EffectPrimitive, span: Tracer.AnySpan) => X) | undefined @@ -76,6 +81,7 @@ export const make: ( label: "OtlpTracer", url: options.url, headers: options.headers, + compression: options.compression, exportInterval: options.exportInterval ?? Duration.seconds(5), maxBatchSize: options.maxBatchSize ?? 1000, body(spans) { @@ -128,6 +134,11 @@ export const layer: (options: { readonly attributes?: Record } | undefined readonly headers?: Headers.Input | undefined + /** + * Request compression. Defaults to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + */ + readonly compression?: "gzip" | "none" | undefined readonly exportInterval?: Duration.Input | undefined readonly maxBatchSize?: number | undefined readonly context?: ((primitive: Tracer.EffectPrimitive, span: Tracer.AnySpan) => X) | undefined @@ -141,6 +152,13 @@ export const layer: (options: { /** * Creates an OTLP traces layer from OpenTelemetry configuration. * + * **Details** + * + * Compression accepts "gzip" or "none". + * `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` takes precedence over + * `OTEL_EXPORTER_OTLP_COMPRESSION`, defaulting to "none". Gzip uses + * HttpClientRequest's default minimum body size of 1KB. + * * @stability unstable * @category layers * @since 4.0.0 @@ -165,7 +183,17 @@ export const layerFromConfig = (options?: { return Exporter.layerFlusher } - const { baseTimeout, tracesTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({ + const { + baseCompression, + compression, + baseTimeout, + tracesTimeout, + exportTimeout, + scheduleDelay, + maxBatchSize + } = yield* Config.all({ + baseCompression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_COMPRESSION")), + compression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION")), baseTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TIMEOUT")), tracesTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT")), exportTimeout: Config.option(Config.Int("OTEL_BSP_EXPORT_TIMEOUT")), @@ -185,6 +213,7 @@ export const layerFromConfig = (options?: { url: endpoint.toString(), resource: options?.resource, headers: options?.headers ?? (yield* OtlpEnv.headers("TRACES")), + compression: Option.getOrElse(Option.firstSomeOf([compression, baseCompression]), () => "none" as const), exportInterval: Option.getOrUndefined(scheduleDelay), maxBatchSize: Option.getOrUndefined(maxBatchSize), context: options?.context, @@ -342,7 +371,7 @@ const makeOtlpSpan = (self: SpanImpl): OtlpSpan => { for (let i = 0; i < self.links.length; i++) { const link = self.links[i] links.push({ - traceId: link.span.traceId, + traceId: link.span.traceId.padStart(32, "0"), spanId: link.span.spanId, attributes: entriesToAttributes(Object.entries(link.attributes)), droppedAttributesCount: 0 @@ -350,7 +379,7 @@ const makeOtlpSpan = (self: SpanImpl): OtlpSpan => { } return { - traceId: self.traceId, + traceId: self.traceId.padStart(32, "0"), spanId: self.spanId, parentSpanId: Option.isSome(self.parent) ? self.parent.value.spanId : undefined, name: self.name, diff --git a/repos/effect/packages/effect/src/observability/PrometheusMetrics.ts b/packages/effect/src/observability/PrometheusMetrics.ts similarity index 99% rename from repos/effect/packages/effect/src/observability/PrometheusMetrics.ts rename to packages/effect/src/observability/PrometheusMetrics.ts index 2b0872ca2..5b1c8383b 100644 --- a/repos/effect/packages/effect/src/observability/PrometheusMetrics.ts +++ b/packages/effect/src/observability/PrometheusMetrics.ts @@ -425,6 +425,7 @@ const formatHistogram = ( // Effect buckets are [boundary, cumulativeCount] pairs for (let i = 0; i < state.buckets.length; i++) { const [boundary, cumulativeCount] = state.buckets[i] + if (boundary === Number.POSITIVE_INFINITY) continue const bucketLabels = formatLabels(metric.attributes, [["le", boundary.toString()]]) lines.push(`${name}_bucket${bucketLabels} ${cumulativeCount}`) } diff --git a/repos/effect/packages/effect/src/observability/index.ts b/packages/effect/src/observability/index.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/index.ts rename to packages/effect/src/observability/index.ts diff --git a/repos/effect/packages/effect/src/observability/internal/otlpEnv.ts b/packages/effect/src/observability/internal/otlpEnv.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/internal/otlpEnv.ts rename to packages/effect/src/observability/internal/otlpEnv.ts diff --git a/repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts b/packages/effect/src/observability/internal/otlpProtobuf.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/internal/otlpProtobuf.ts rename to packages/effect/src/observability/internal/otlpProtobuf.ts diff --git a/repos/effect/packages/effect/src/observability/internal/protobuf.ts b/packages/effect/src/observability/internal/protobuf.ts similarity index 100% rename from repos/effect/packages/effect/src/observability/internal/protobuf.ts rename to packages/effect/src/observability/internal/protobuf.ts diff --git a/repos/effect/packages/effect/src/persistence/KeyValueStore.ts b/packages/effect/src/persistence/KeyValueStore.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/KeyValueStore.ts rename to packages/effect/src/persistence/KeyValueStore.ts diff --git a/repos/effect/packages/effect/src/persistence/Persistable.ts b/packages/effect/src/persistence/Persistable.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/Persistable.ts rename to packages/effect/src/persistence/Persistable.ts diff --git a/repos/effect/packages/effect/src/persistence/PersistedCache.ts b/packages/effect/src/persistence/PersistedCache.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/PersistedCache.ts rename to packages/effect/src/persistence/PersistedCache.ts diff --git a/repos/effect/packages/effect/src/persistence/PersistedQueue.ts b/packages/effect/src/persistence/PersistedQueue.ts similarity index 92% rename from repos/effect/packages/effect/src/persistence/PersistedQueue.ts rename to packages/effect/src/persistence/PersistedQueue.ts index 6439ca53b..55227eaad 100644 --- a/repos/effect/packages/effect/src/persistence/PersistedQueue.ts +++ b/packages/effect/src/persistence/PersistedQueue.ts @@ -34,6 +34,7 @@ import * as Scope from "../Scope.ts" import * as Migrator from "../sql/Migrator.ts" import * as SqlClient from "../sql/SqlClient.ts" import type { SqlError } from "../sql/SqlError.ts" +import type { Fragment } from "../sql/Statement.ts" import * as Redis from "./Redis.ts" /** @@ -80,11 +81,27 @@ export interface PersistedQueue { * **Details** * * If an element with the same id already exists in the queue, it will not be - * added again. De-duplication survives completion until the id is removed by + * added again, and the `delay` of the duplicate offer is ignored. + * De-duplication survives completion until the id is removed by * `layerCleanup`. + * + * `delay` postpones the first delivery. Measured by the store's clock, the + * element is never delivered before the delay elapses, and is typically + * delivered within the store's `pollInterval` after. The SQL store uses the + * database clock and rounds delays up to whole seconds. The Redis store + * compares the offering process's clock with the polling worker's, so it + * relies on those clocks agreeing. + * Retries still use the queue's `retrySchedule`, and the delay does not + * count an attempt. A zero, negative or omitted delay enqueues the element + * immediately. + * + * An infinite delay does nothing: the id is returned, but nothing is stored + * and no de-duplication record is written, so the same id can be offered + * again later. */ readonly offer: (value: A, options?: { - readonly id: string | undefined + readonly id?: string | undefined + readonly delay?: Duration.Input | undefined }) => Effect.Effect /** @@ -232,21 +249,27 @@ export const makeFactory = Effect.gen(function*() { return Effect.succeed>({ [TypeId]: TypeId, offer: (value, opts) => - Effect.flatMap( - encodeUnknown(value), - (element) => { - const id = opts?.id ?? crypto.randomUUID() - return Effect.as( - store.offer({ - name: options.name, - id, - element, - isCustomId: opts?.id !== undefined - }), - id - ) + Effect.suspend(() => { + const id = opts?.id ?? crypto.randomUUID() + const delay = positiveDelay(opts?.delay) + if (delay !== undefined && !Duration.isFinite(delay)) { + return Effect.succeed(id) } - ), + return Effect.flatMap( + encodeUnknown(value), + (element) => + Effect.as( + store.offer({ + name: options.name, + id, + element, + isCustomId: opts?.id !== undefined, + delay + }), + id + ) + ) + }), take: ( f: (value: S["Type"], metadata: { readonly id: string @@ -430,6 +453,13 @@ class DeadLetter { const isDeadLetter = (u: unknown): u is DeadLetter => Predicate.isTagged(u, "~effect/persistence/PersistedQueue/DeadLetter") +// stores only see a positive delay, so a zero or negative one is dropped here +const positiveDelay = (input: Duration.Input | undefined): Duration.Duration | undefined => { + if (input === undefined) return undefined + const delay = Duration.fromInputUnsafe(input) + return Duration.isPositive(delay) ? delay : undefined +} + const deadLetterFromCause = (cause: Cause.Cause): DeadLetter | undefined => { for (const reason of cause.reasons) { if (Cause.isFailReason(reason) && isDeadLetter(reason.error)) { @@ -452,6 +482,9 @@ const deadLetterFromCause = (cause: Cause.Cause): DeadLetter | undefine * The store persists offered elements and returns taken elements in a scope so * the finalizer can complete or retry them based on the processing exit. * + * An offered `delay` is always finite and positive, and the element must not + * be taken before it elapses. It is `undefined` for immediate elements. + * * Claiming an element counts an attempt, so the `attempts` returned by `take` * is 1-based. When the take scope closes with a success the element is marked * completed; a failure retries it according to `retryDelay` or marks it failed @@ -471,6 +504,7 @@ export class PersistedQueueStore extends Context.Service< readonly id: string readonly element: unknown readonly isCustomId: boolean + readonly delay?: Duration.Duration | undefined } ) => Effect.Effect @@ -509,6 +543,9 @@ export class PersistedQueueStore extends Context.Service< * queue's retry schedule until the configured maximum attempts is reached, * after which the element is marked as failed. * + * Delayed elements are scanned on every take loop, so the store is meant for + * development and tests rather than large delayed backlogs. + * * @stability unstable * @category layers * @since 4.0.0 @@ -558,7 +595,7 @@ export const layerStoreMemory: Layer.Layer< element: options.element, attempts: 0, state: "pending", - visibleAt: now, + visibleAt: options.delay === undefined ? now : now + Duration.toMillis(options.delay), stateChangedAt: now } queue.entries.set(options.id, entry) @@ -655,7 +692,9 @@ export const layerStoreMemory: Layer.Layer< * The store uses Redis lists, hashes, and sorted sets with worker locks, * periodically refreshes locks while items are being processed, delays retried * items with the queue's retry schedule, and moves exhausted items to a failed - * queue. + * queue. Delayed offers share the retry sorted set: they are scored with the + * offering process's clock and released by the polling worker's clock, so + * clock skew between hosts shifts their delivery. * * @stability unstable * @category constructors @@ -716,6 +755,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*( const retry = redis.eval(retryRedis) const resetQueue = redis.eval(resetQueueRedis) const offer = redis.eval(offerRedis) + const offerDelayed = redis.eval(offerDelayedRedis) const take = redis.eval(takeRedis) const expireAll = redis.eval(expireAllRedis) const trimFailed = redis.eval(trimFailedRedis) @@ -856,24 +896,33 @@ export const makeStoreRedis = Effect.fnUntraced(function*( }) return PersistedQueueStore.of({ - offer: ({ element, id, isCustomId, name }) => { + offer: ({ delay, element, id, isCustomId, name }) => { const keys = keysFor(name) const payload = JSON.stringify({ id, element }) - return (isCustomId - ? offer(keys.queue, keys.ids, id, payload) - : redis.send("RPUSH", keys.queue, payload)).pipe( - Effect.mapError(({ cause }) => - new PersistedQueueError({ - message: "Failed to offer element to persisted queue", - cause - }) - ), - Effect.tap(() => - Effect.sync(() => { - queueStates.peek(name)?.nudge.openUnsafe() - }) - ) + const send = Effect.suspend(() => { + if (delay === undefined) { + return isCustomId + ? offer(keys.queue, keys.ids, id, payload) + : redis.send("RPUSH", keys.queue, payload) + } + const visibleAt = clock.currentTimeMillisUnsafe() + Duration.toMillis(delay) + return isCustomId + ? offerDelayed(keys.delayed, keys.ids, id, payload, visibleAt) + : redis.send("ZADD", keys.delayed, visibleAt.toString(), payload) + }) + return send.pipe( + Effect.mapError(({ cause }) => + new PersistedQueueError({ + message: "Failed to offer element to persisted queue", + cause + }) + ), + Effect.tap(() => + Effect.sync(() => { + queueStates.peek(name)?.nudge.openUnsafe() + }) ) + ) }, take: (options) => Effect.uninterruptibleMask((restore) => { @@ -1009,6 +1058,25 @@ end } ) +const offerDelayedRedis = Redis.script( + (...args: [keyDelayed: string, keyIds: string, id: string, payload: string, visibleAt: number]) => args, + { + lua: ` +local key_delayed = KEYS[1] +local key_ids = KEYS[2] +local id = ARGV[1] +local payload = ARGV[2] +local visible_at = ARGV[3] + +local result = redis.call("ZADD", key_ids, "NX", "+inf", id) +if result == 1 then + redis.call("ZADD", key_delayed, visible_at, payload) +end +`, + numberOfKeys: 2 + } +) + const resetQueueRedis = Redis.script( ( ...args: [ @@ -1296,6 +1364,9 @@ export const layerStoreRedis: ( * per-worker locks, refreshes active locks while scoped takes are running, and * retries or completes rows according to the processing exit. * + * Offer and retry delays are measured with the database clock and rounded up + * to whole seconds. + * * @stability unstable * @category constructors * @since 4.0.0 @@ -1360,34 +1431,46 @@ export const makeStoreSql: ( } const secondsAgo = (seconds: number) => secondsOffset(-Math.max(Math.ceil(seconds), 0)) const secondsFromNow = (seconds: number) => secondsOffset(Math.max(Math.ceil(seconds), 0)) + // Deadline for a delayed offer, which must never land before the delay + // elapses. pg reads clock_timestamp() since NOW() is fixed at transaction + // start. mysql and sqlite clocks drop the fraction of the current second, so + // they get a whole extra second to cover it. + const delayedVisibleAt = (delay: Duration.Duration) => { + const seconds = Math.ceil(Duration.toSeconds(delay)) + return sql.onDialectOrElse({ + pg: () => sql`clock_timestamp() + INTERVAL '${sql.literal(seconds.toString())} seconds'`, + mssql: () => secondsOffset(seconds), + orElse: () => secondsOffset(seconds + 1) + }) + } const expiresAt = secondsAgo(Duration.toSeconds(lockExpiration)) const offer = sql.onDialectOrElse({ - pg: () => (id: string, name: string, element: string) => + pg: () => (id: string, name: string, element: string, visibleAt: Fragment) => sql` INSERT INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at) - VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow}) + VALUES (${id}, ${name}, ${element}, 'pending', 0, ${visibleAt}, ${sqlNow}, ${sqlNow}) ON CONFLICT (id, queue_name) DO NOTHING `, - mysql: () => (id: string, name: string, element: string) => + mysql: () => (id: string, name: string, element: string, visibleAt: Fragment) => sql` INSERT IGNORE INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at) - VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow}) + VALUES (${id}, ${name}, ${element}, 'pending', 0, ${visibleAt}, ${sqlNow}, ${sqlNow}) `, - mssql: () => (id: string, name: string, element: string) => + mssql: () => (id: string, name: string, element: string, visibleAt: Fragment) => sql` MERGE ${tableNameSql} WITH (HOLDLOCK) AS target USING (SELECT ${id} AS id, ${name} AS queue_name) AS source ON target.id = source.id AND target.queue_name = source.queue_name WHEN NOT MATCHED THEN INSERT (id, queue_name, element, state, attempts, visible_at, created_at, updated_at) - VALUES (source.id, source.queue_name, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow}); + VALUES (source.id, source.queue_name, ${element}, 'pending', 0, ${visibleAt}, ${sqlNow}, ${sqlNow}); `, // sqlite - orElse: () => (id: string, name: string, element: string) => + orElse: () => (id: string, name: string, element: string, visibleAt: Fragment) => sql` INSERT OR IGNORE INTO ${tableNameSql} (id, queue_name, element, state, attempts, visible_at, created_at, updated_at) - VALUES (${id}, ${name}, ${element}, 'pending', 0, ${sqlNow}, ${sqlNow}, ${sqlNow}) + VALUES (${id}, ${name}, ${element}, 'pending', 0, ${visibleAt}, ${sqlNow}, ${sqlNow}) ` }) @@ -1696,20 +1779,30 @@ export const makeStoreSql: ( ) return PersistedQueueStore.of({ - offer: ({ element, id, name }) => - Effect.catchCause(Effect.suspend(() => offer(id, name, JSON.stringify(element))), (cause) => - Effect.fail( - new PersistedQueueError({ - message: "Failed to offer element to persisted queue", - cause - }) - )).pipe( - Effect.tap(() => - Effect.sync(() => { - queueStates.peek(name)?.nudge.openUnsafe() - }) + offer: ({ delay, element, id, name }) => + Effect.catchCause( + Effect.suspend(() => + offer( + id, + name, + JSON.stringify(element), + delay === undefined ? sqlNow : delayedVisibleAt(delay) ) ), + (cause) => + Effect.fail( + new PersistedQueueError({ + message: "Failed to offer element to persisted queue", + cause + }) + ) + ).pipe( + Effect.tap(() => + Effect.sync(() => { + queueStates.peek(name)?.nudge.openUnsafe() + }) + ) + ), take: (options) => { queueStates.get(options.name).maxAttempts = options.maxAttempts const loop: Effect.Effect< diff --git a/repos/effect/packages/effect/src/persistence/Persistence.ts b/packages/effect/src/persistence/Persistence.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/Persistence.ts rename to packages/effect/src/persistence/Persistence.ts diff --git a/repos/effect/packages/effect/src/persistence/RateLimiter.ts b/packages/effect/src/persistence/RateLimiter.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/RateLimiter.ts rename to packages/effect/src/persistence/RateLimiter.ts diff --git a/repos/effect/packages/effect/src/persistence/Redis.ts b/packages/effect/src/persistence/Redis.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/Redis.ts rename to packages/effect/src/persistence/Redis.ts diff --git a/repos/effect/packages/effect/src/persistence/index.ts b/packages/effect/src/persistence/index.ts similarity index 100% rename from repos/effect/packages/effect/src/persistence/index.ts rename to packages/effect/src/persistence/index.ts diff --git a/repos/effect/packages/effect/src/process/ChildProcess.ts b/packages/effect/src/process/ChildProcess.ts similarity index 100% rename from repos/effect/packages/effect/src/process/ChildProcess.ts rename to packages/effect/src/process/ChildProcess.ts diff --git a/repos/effect/packages/effect/src/process/ChildProcessSpawner.ts b/packages/effect/src/process/ChildProcessSpawner.ts similarity index 100% rename from repos/effect/packages/effect/src/process/ChildProcessSpawner.ts rename to packages/effect/src/process/ChildProcessSpawner.ts diff --git a/repos/effect/packages/effect/src/process/index.ts b/packages/effect/src/process/index.ts similarity index 100% rename from repos/effect/packages/effect/src/process/index.ts rename to packages/effect/src/process/index.ts diff --git a/repos/effect/packages/effect/src/reactivity/AsyncResult.ts b/packages/effect/src/reactivity/AsyncResult.ts similarity index 97% rename from repos/effect/packages/effect/src/reactivity/AsyncResult.ts rename to packages/effect/src/reactivity/AsyncResult.ts index 3869e5876..5dbf1873a 100644 --- a/repos/effect/packages/effect/src/reactivity/AsyncResult.ts +++ b/packages/effect/src/reactivity/AsyncResult.ts @@ -720,6 +720,12 @@ export const matchWithWaiting: { /** * Combines an iterable or record of `AsyncResult` and plain values into one `AsyncResult`, returning the first non-success result or a success of the collected values marked waiting when any input success is waiting. * + * **Details** + * + * If the first non-success result is a failure, its previous success is + * rebuilt in the combined shape from each input's current or previous value. + * It is omitted if any input has no value. + * * @stability unstable * @category combinators * @since 4.0.0 @@ -751,6 +757,11 @@ export const all = | Record>( if (!isAsyncResult(result)) { InternalRecord.assignProperty(successes, key, result) continue + } else if (isFailure(result)) { + return failure(result.cause, { + previousSuccess: combinedPreviousSuccess(entries, isIter), + waiting: result.waiting + }) as any } else if (!isSuccess(result)) { return result as any } @@ -762,6 +773,26 @@ export const all = | Record>( return success(successes, { waiting }) as any } +const combinedPreviousSuccess = ( + entries: ReadonlyArray, + isIter: boolean +): Option.Option> => { + const values: any = isIter ? [] : {} + for (let i = 0; i < entries.length; i++) { + const [key, entry] = entries[i] + if (!isAsyncResult(entry)) { + InternalRecord.assignProperty(values, key, entry) + continue + } + const entryValue = value(entry) + if (Option.isNone(entryValue)) { + return Option.none() + } + InternalRecord.assignProperty(values, key, entryValue.value) + } + return Option.some(success(values)) +} + /** * Creates a typed builder for rendering an `AsyncResult` by handling waiting, initial, success, error, defect, interrupt, and failure cases. * diff --git a/repos/effect/packages/effect/src/reactivity/Atom.ts b/packages/effect/src/reactivity/Atom.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/Atom.ts rename to packages/effect/src/reactivity/Atom.ts diff --git a/repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts b/packages/effect/src/reactivity/AtomHttpApi.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/AtomHttpApi.ts rename to packages/effect/src/reactivity/AtomHttpApi.ts diff --git a/repos/effect/packages/effect/src/reactivity/AtomRef.ts b/packages/effect/src/reactivity/AtomRef.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/AtomRef.ts rename to packages/effect/src/reactivity/AtomRef.ts diff --git a/repos/effect/packages/effect/src/reactivity/AtomRegistry.ts b/packages/effect/src/reactivity/AtomRegistry.ts similarity index 99% rename from repos/effect/packages/effect/src/reactivity/AtomRegistry.ts rename to packages/effect/src/reactivity/AtomRegistry.ts index ef4af667c..a55ce8c4b 100644 --- a/repos/effect/packages/effect/src/reactivity/AtomRegistry.ts +++ b/packages/effect/src/reactivity/AtomRegistry.ts @@ -838,7 +838,9 @@ class NodeImpl { this._value = value let failure: unknown = noFailure try { - this.invalidateChildren() + // A first value cannot replace one held by a dependent. Recovery still + // retries dependents whose reads failed before this node had a value. + if (initialized || recovered) this.invalidateChildren() } catch (error) { failure = error } diff --git a/repos/effect/packages/effect/src/reactivity/AtomRpc.ts b/packages/effect/src/reactivity/AtomRpc.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/AtomRpc.ts rename to packages/effect/src/reactivity/AtomRpc.ts diff --git a/repos/effect/packages/effect/src/reactivity/Hydration.ts b/packages/effect/src/reactivity/Hydration.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/Hydration.ts rename to packages/effect/src/reactivity/Hydration.ts diff --git a/repos/effect/packages/effect/src/reactivity/Reactivity.ts b/packages/effect/src/reactivity/Reactivity.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/Reactivity.ts rename to packages/effect/src/reactivity/Reactivity.ts diff --git a/repos/effect/packages/effect/src/reactivity/index.ts b/packages/effect/src/reactivity/index.ts similarity index 100% rename from repos/effect/packages/effect/src/reactivity/index.ts rename to packages/effect/src/reactivity/index.ts diff --git a/repos/effect/packages/effect/src/rpc/Rpc.ts b/packages/effect/src/rpc/Rpc.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/Rpc.ts rename to packages/effect/src/rpc/Rpc.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcClient.ts b/packages/effect/src/rpc/RpcClient.ts similarity index 95% rename from repos/effect/packages/effect/src/rpc/RpcClient.ts rename to packages/effect/src/rpc/RpcClient.ts index 615d168ad..3528fe62f 100644 --- a/repos/effect/packages/effect/src/rpc/RpcClient.ts +++ b/packages/effect/src/rpc/RpcClient.ts @@ -1050,11 +1050,19 @@ export const makeProtocolSocket = (options?: { readonly retryPolicy?: Schedule.Schedule | undefined /** * Runs for each retried `SocketOpenError` when `retryTransientErrors` is enabled. - * 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. + * A ping timeout fails in-flight calls and is reported through `onPingTimeout` + * instead. The returned `Effect` cannot fail with a typed error or + * require services; defects are logged and ignored so retries can continue. */ readonly onTransientError?: ((error: RpcClientError) => Effect.Effect) | undefined + /** + * Runs when an open connection is dropped because no server frame arrived + * within `pingTimeout`, before `ConnectionHooks.onDisconnect` and before + * in-flight calls fail with a `SocketReadError`. Defects are logged and ignored. + * + * @since 4.0.3 + */ + readonly onPingTimeout?: Effect.Effect | undefined }): Effect.Effect< Protocol["Service"], never, @@ -1070,10 +1078,12 @@ export const makeProtocolSocket = (options?: { let parser = serialization.makeUnsafe() - // `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. + // Encode each ping with the current connection's parser. const pinger = yield* makePinger(Effect.suspend(() => writer.write(parser.encode(constPing)!)), options) + const onPingTimeout = Effect.ignoreCause(options?.onPingTimeout ?? Effect.void, { + log: true, + message: "RpcClient onPingTimeout hook failed" + }) let currentError: RpcClientError | undefined const broadcast = (response: FromServerEncoded) => @@ -1127,9 +1137,11 @@ export const makeProtocolSocket = (options?: { yield* Effect.suspend(() => { parser = serialization.makeUnsafe() pinger.reset() + let connected = false return Effect.gen(function*() { const { pull } = yield* socket.reader currentError = undefined + connected = true if (Option.isSome(hooks)) { yield* hooks.value.onConnect } @@ -1141,17 +1153,19 @@ export const makeProtocolSocket = (options?: { } }).pipe( Effect.scoped, - Effect.raceFirst(Effect.flatMap( - pinger.timeout, - () => - Effect.fail( - new Socket.SocketError({ - reason: new Socket.SocketReadError({ - cause: new Error("ping timeout") - }) + // The read loop never succeeds, so the race only succeeds on a ping + // timeout, after the socket has been cleaned up. + Effect.raceFirst(pinger.timeout), + Effect.andThen(() => connected ? onPingTimeout : Effect.void), + Effect.andThen(() => + Effect.fail( + new Socket.SocketError({ + reason: new Socket.SocketReadError({ + cause: new Error("ping timeout") }) - ) - )) + }) + ) + ) ) }).pipe( Option.isSome(hooks) ? Effect.ensuring(hooks.value.onDisconnect) : identity, @@ -1219,16 +1233,21 @@ const makePinger = Effect.fnUntraced(function*(writePing: Effect.Effect const interval = options?.pingInterval ?? "5 seconds" const timeoutMillis = Duration.toMillis(options?.pingTimeout ?? interval) let lastSeen = clock.currentTimeMillisUnsafe() + let awaitingResponse = false const latch = Latch.makeUnsafe() const onFrame = () => { lastSeen = clock.currentTimeMillisUnsafe() + awaitingResponse = false } const reset = () => { onFrame() latch.closeUnsafe() } yield* Effect.suspend((): Effect.Effect => { - if (clock.currentTimeMillisUnsafe() - lastSeen > timeoutMillis) return latch.open + // A late tick must send a ping before it can declare the connection silent. + // Keep the timeout measured from the last frame, including for custom timeouts. + if (awaitingResponse && clock.currentTimeMillisUnsafe() - lastSeen > timeoutMillis) return latch.open + awaitingResponse = true return writePing }).pipe( Effect.delay(interval), @@ -1262,11 +1281,19 @@ export const layerProtocolSocket = (options?: { readonly retryPolicy?: Schedule.Schedule | undefined /** * Runs for each retried `SocketOpenError` when `retryTransientErrors` is enabled. - * 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. + * A ping timeout fails in-flight calls and is reported through `onPingTimeout` + * instead. The returned `Effect` cannot fail with a typed error or + * require services; defects are logged and ignored so retries can continue. */ readonly onTransientError?: ((error: RpcClientError) => Effect.Effect) | undefined + /** + * Runs when an open connection is dropped because no server frame arrived + * within `pingTimeout`, before `ConnectionHooks.onDisconnect` and before + * in-flight calls fail with a `SocketReadError`. Defects are logged and ignored. + * + * @since 4.0.3 + */ + readonly onPingTimeout?: Effect.Effect | undefined }): Layer.Layer< Protocol, never, diff --git a/repos/effect/packages/effect/src/rpc/RpcClientError.ts b/packages/effect/src/rpc/RpcClientError.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcClientError.ts rename to packages/effect/src/rpc/RpcClientError.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcGroup.ts b/packages/effect/src/rpc/RpcGroup.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcGroup.ts rename to packages/effect/src/rpc/RpcGroup.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcMessage.ts b/packages/effect/src/rpc/RpcMessage.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcMessage.ts rename to packages/effect/src/rpc/RpcMessage.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcMiddleware.ts b/packages/effect/src/rpc/RpcMiddleware.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcMiddleware.ts rename to packages/effect/src/rpc/RpcMiddleware.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcSchema.ts b/packages/effect/src/rpc/RpcSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcSchema.ts rename to packages/effect/src/rpc/RpcSchema.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcSerialization.ts b/packages/effect/src/rpc/RpcSerialization.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcSerialization.ts rename to packages/effect/src/rpc/RpcSerialization.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcServer.ts b/packages/effect/src/rpc/RpcServer.ts similarity index 91% rename from repos/effect/packages/effect/src/rpc/RpcServer.ts rename to packages/effect/src/rpc/RpcServer.ts index bda39256d..7f38c61ab 100644 --- a/repos/effect/packages/effect/src/rpc/RpcServer.ts +++ b/packages/effect/src/rpc/RpcServer.ts @@ -82,6 +82,12 @@ export interface RpcServer { * handlers for a group and sending decoded server responses through * `onFromServer`. * + * **Details** + * + * If writing a stream `Chunk` fails with anything other than an interruption, + * the request fails with that cause. `onFromServer` is assumed to have already + * answered the client and reported it, so nothing else is sent or reported. + * * @stability unstable * @category constructors * @since 4.0.0 @@ -188,7 +194,7 @@ export const makeNoSerialization: ( clients.set(clientId, client) } else if ( client.ended && - (message._tag !== "Interrupt" || !client.fibers.has(message.requestId)) + ((message._tag !== "Interrupt" && message._tag !== "Ack") || !client.fibers.has(message.requestId)) ) { return Effect.interrupt } @@ -199,7 +205,7 @@ export const makeNoSerialization: ( } case "Ack": { const latch = client.latches?.get(message.requestId) - return latch ? latch.open : Effect.void + return latch ? Effect.asVoid(latch.open) : Effect.void } case "Interrupt": { const fiber = client.fibers.get(message.requestId) @@ -279,13 +285,23 @@ export const makeNoSerialization: ( const isUninterruptible = isWrapper && result.uninterruptible // unwrap the fork data type const streamOrEffect = isWrapper ? result.value : result + // set when a chunk could not be written; the writer has already responded + let writeFailure: Cause.Cause | undefined const handler = isStream - ? (streamEffect(client, request, streamOrEffect) as Effect.Effect<{} | Deferred.Deferred>) + ? (streamEffect(client, request, streamOrEffect, (cause) => { + writeFailure = cause + }) as Effect.Effect<{} | Deferred.Deferred>) : (streamOrEffect as Effect.Effect<{} | Deferred.Deferred>) - const withMiddleware = rpc.middlewares.size > 0 - ? applyMiddleware(services, handler, metadata) - : handler + const withMiddleware = rpc.middlewares.size === 0 + ? handler + : isStream + // middleware may rewrite or swallow a chunk write failure, so restore it + ? Effect.flatMap( + Effect.exit(applyMiddleware(services, handler, metadata)), + (exit) => writeFailure ? Effect.failCause(writeFailure) : exit + ) + : applyMiddleware(services, handler, metadata) let responded = false const scope = Scope.makeUnsafe() let deferred: Deferred.Deferred | undefined = undefined @@ -304,6 +320,9 @@ export const makeNoSerialization: ( exit: exit as any }) } + } else if (writeFailure) { + // onFromServer has already answered the client and reported the failure + return Scope.closeUnsafe(scope, exit) ?? Effect.void } else if ( !disableFatalDefects && Cause.hasDies(exit.cause) && @@ -409,8 +428,17 @@ export const makeNoSerialization: ( request: Request, stream: | Stream.Stream - | Effect.Effect, any, Scope.Scope> + | Effect.Effect, any, Scope.Scope>, + onWriteFailure: (cause: Cause.Cause) => void ) => { + const writeChunk = (values: NonEmptyReadonlyArray) => + Effect.onError( + options.onFromServer({ _tag: "Chunk", clientId: client.id, requestId: request.id, values }), + (cause) => { + if (!Cause.hasInterruptsOnly(cause)) onWriteFailure(cause) + return Effect.void + } + ) let latch: Latch.Latch | undefined if (supportsAck) { client.latches ??= new Map() @@ -427,12 +455,7 @@ export const makeNoSerialization: ( while: constTrue, body: constant( Effect.flatMap(Queue.takeAll(queue), (values) => { - const write = options.onFromServer({ - _tag: "Chunk", - clientId: client.id, - requestId: request.id, - values - }) + const write = writeChunk(values) if (!latch) return write latch.closeUnsafe() return Effect.flatMap(write, () => latch.await) @@ -446,12 +469,7 @@ export const makeNoSerialization: ( ) } return Stream.runForEachArray(stream, (values) => { - const write = options.onFromServer({ - _tag: "Chunk", - clientId: client.id, - requestId: request.id, - values - }) + const write = writeChunk(values) if (!latch) return write latch.closeUnsafe() return Effect.andThen(write, latch.await) @@ -476,6 +494,57 @@ export const makeNoSerialization: ( }) }) +// Built-in JSON codecs depend only on the RPC schemas and can be shared. +// Other factories may capture state at construction, so keep them per server. +const isSharedCodecFor = (codecFor: RpcSerialization.CodecFor) => codecFor === RpcSerialization.json.codecFor + +interface RpcCodecs { + readonly decode: (u: unknown) => Effect.Effect + readonly encodeChunk: ( + u: NonEmptyReadonlyArray + ) => Effect.Effect, Schema.SchemaError> + readonly encodeExit: (u: unknown) => Effect.Effect +} + +const sharedRpcCodecs = new WeakMap() + +const rpcCodecs = (codecFor: RpcSerialization.CodecFor, rpc: Rpc.AnyWithProps): RpcCodecs => { + const shared = isSharedCodecFor(codecFor) + let codecs = shared ? sharedRpcCodecs.get(rpc) : undefined + if (codecs === undefined) { + const streamSchemas = RpcSchema.getStreamSchemas(rpc.successSchema) + codecs = { + decode: Schema.decodeUnknownEffect(codecFor(rpc.payloadSchema)) as any, + encodeChunk: Schema.encodeUnknownEffect( + codecFor( + Schema.NonEmptyArray(Option.isSome(streamSchemas) ? streamSchemas.value.success : Schema.Any) + ) + ) as any, + encodeExit: Schema.encodeUnknownEffect(codecFor(Rpc.exitSchema(rpc as any))) as any + } + if (shared) sharedRpcCodecs.set(rpc, codecs) + } + return codecs +} + +interface DefectCodecs { + readonly encodeDefectUnsafe: (defect: unknown) => unknown + readonly encodeUnknownRequestExit: (u: unknown) => Effect.Effect +} + +let sharedDefectCodecs: DefectCodecs | undefined + +const makeDefectCodecs = (codecFor: RpcSerialization.CodecFor): DefectCodecs => ({ + encodeDefectUnsafe: Schema.encodeSync(codecFor(Schema.Defect())), + // Unknown tags have no RPC schema, so use a defect-only exit schema. + encodeUnknownRequestExit: Schema.encodeUnknownEffect( + codecFor(Schema.Exit(Schema.Never, Schema.Never, Schema.Defect())) + ) as any +}) + +const defectCodecs = (codecFor: RpcSerialization.CodecFor): DefectCodecs => + isSharedCodecFor(codecFor) ? (sharedDefectCodecs ??= makeDefectCodecs(codecFor)) : makeDefectCodecs(codecFor) + // Merging the handler entry services with the request fiber services costs // O(services), so the merged context is cached per (entry, fiber context) // pair. Request fibers on persistent connections keep one context across @@ -568,11 +637,7 @@ export const make: ( supportsSpanPropagation, 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 { encodeDefectUnsafe, encodeUnknownRequestExit } = defectCodecs(codecFor) const services = yield* Effect.context | Rpc.Middleware>() const scope = yield* Scope.make() @@ -633,6 +698,7 @@ export const make: ( ) type Schemas = { + readonly tag: string readonly decode: (u: unknown) => Effect.Effect, Schema.SchemaError> readonly encodeChunk: ( u: NonEmptyReadonlyArray @@ -647,15 +713,9 @@ export const make: ( let schemas = schemasCache.get(rpc) if (!schemas) { const entry = Context.getOrUndefinedUnsafe(services, rpc.key) as Rpc.Handler - const streamSchemas = RpcSchema.getStreamSchemas(rpc.successSchema) schemas = { - decode: Schema.decodeUnknownEffect(codecFor(rpc.payloadSchema)) as any, - encodeChunk: Schema.encodeUnknownEffect( - codecFor( - Schema.NonEmptyArray(Option.isSome(streamSchemas) ? streamSchemas.value.success : Schema.Any) - ) - ) as any, - encodeExit: Schema.encodeUnknownEffect(codecFor(Rpc.exitSchema(rpc as any))) as any, + tag: rpc._tag, + ...rpcCodecs(codecFor, rpc), context: entry.context } schemasCache.set(rpc, schemas) @@ -686,23 +746,38 @@ export const make: ( tag: "Chunk" | "Exit" ) => { const collector = schemas.collector + let encodeFailed = false // The schema encoders evaluate eagerly, so an encode that needs no // services is already a resolved success and can skip the effect wrappers. const write = Exit.isExit(effect) && Exit.isSuccess(effect) ? send(client.id, responseEnvelope(requestId, tag, effect.value), collector && collector.clearUnsafe()) : Effect.flatMap( - Effect.provideContext( - collector ? Effect.provideService(effect, Transferable.Collector, collector) : effect, - schemas.context + Effect.catch( + Effect.provideContext( + collector ? Effect.provideService(effect, Transferable.Collector, collector) : effect, + schemas.context + ), + (error) => + Effect.withFiber((fiber) => { + const defect = Cause.die( + `Failed to encode response for RPC "${schemas.tag}": ${SchemaIssue.defaultFormatter(error.issue)}` + ) + reportCauseUnsafe(fiber, defect) + encodeFailed = true + return Effect.failCause(defect) + }) ), (value) => send(client.id, responseEnvelope(requestId, tag, value), collector && collector.clearUnsafe()) ) return Effect.catchCause(write, (cause) => { client.schemas.delete(requestId) - const defect = Cause.squash(Cause.map(cause, (e) => SchemaIssue.defaultFormatter(e.issue))) + // An encode failure ends the request with the defect, so the request + // span records it. Other failures stop the request as a cancellation. return Effect.andThen( - sendRequestDefect(client, requestId, schemas.encodeExit, defect), - server.write(client.id, { _tag: "Interrupt", requestId, interruptors: [] }) + sendRequestDefect(client, requestId, schemas.encodeExit, Cause.squash(cause)), + encodeFailed + ? Effect.failCause(cause) + : server.write(client.id, { _tag: "Interrupt", requestId, interruptors: [] }) ) }) } @@ -1074,7 +1149,7 @@ export const makeProtocolWithHttpEffect: ( RpcSerialization.RpcSerialization > = Effect.fnUntraced(function*(options = {}) { const serialization = yield* RpcSerialization.RpcSerialization - const encodeDefectUnsafe = Schema.encodeSync(serialization.codecFor(Schema.Defect())) + const { encodeDefectUnsafe } = defectCodecs(serialization.codecFor) const includesFraming = serialization.includesFraming const isBinary = !serialization.contentType.includes("json") @@ -1534,7 +1609,7 @@ const makeSocketProtocol: Effect.Effect< RpcSerialization.RpcSerialization > = Effect.gen(function*() { const serialization = yield* RpcSerialization.RpcSerialization - const encodeDefectUnsafe = Schema.encodeSync(serialization.codecFor(Schema.Defect())) + const { encodeDefectUnsafe } = defectCodecs(serialization.codecFor) const disconnects = yield* Queue.make() let clientId = 0 diff --git a/repos/effect/packages/effect/src/rpc/RpcTest.ts b/packages/effect/src/rpc/RpcTest.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcTest.ts rename to packages/effect/src/rpc/RpcTest.ts diff --git a/repos/effect/packages/effect/src/rpc/RpcWorker.ts b/packages/effect/src/rpc/RpcWorker.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/RpcWorker.ts rename to packages/effect/src/rpc/RpcWorker.ts diff --git a/repos/effect/packages/effect/src/rpc/Utils.ts b/packages/effect/src/rpc/Utils.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/Utils.ts rename to packages/effect/src/rpc/Utils.ts diff --git a/repos/effect/packages/effect/src/rpc/index.ts b/packages/effect/src/rpc/index.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/index.ts rename to packages/effect/src/rpc/index.ts diff --git a/repos/effect/packages/effect/src/rpc/internal/tracing.ts b/packages/effect/src/rpc/internal/tracing.ts similarity index 100% rename from repos/effect/packages/effect/src/rpc/internal/tracing.ts rename to packages/effect/src/rpc/internal/tracing.ts diff --git a/repos/effect/packages/effect/src/schema/Model.ts b/packages/effect/src/schema/Model.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/Model.ts rename to packages/effect/src/schema/Model.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaAOTCompiler.ts b/packages/effect/src/schema/SchemaAOTCompiler.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaAOTCompiler.ts rename to packages/effect/src/schema/SchemaAOTCompiler.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaAOTCompiler/Build.ts b/packages/effect/src/schema/SchemaAOTCompiler/Build.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaAOTCompiler/Build.ts rename to packages/effect/src/schema/SchemaAOTCompiler/Build.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaCompiler.ts b/packages/effect/src/schema/SchemaCompiler.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaCompiler.ts rename to packages/effect/src/schema/SchemaCompiler.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts b/packages/effect/src/schema/SchemaCompiler/runtime.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaCompiler/runtime.ts rename to packages/effect/src/schema/SchemaCompiler/runtime.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaJITCompiler.ts b/packages/effect/src/schema/SchemaJITCompiler.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaJITCompiler.ts rename to packages/effect/src/schema/SchemaJITCompiler.ts diff --git a/repos/effect/packages/effect/src/schema/SchemaJITCompiler/enable.ts b/packages/effect/src/schema/SchemaJITCompiler/enable.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/SchemaJITCompiler/enable.ts rename to packages/effect/src/schema/SchemaJITCompiler/enable.ts diff --git a/repos/effect/packages/effect/src/schema/VariantSchema.ts b/packages/effect/src/schema/VariantSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/VariantSchema.ts rename to packages/effect/src/schema/VariantSchema.ts diff --git a/repos/effect/packages/effect/src/schema/index.ts b/packages/effect/src/schema/index.ts similarity index 100% rename from repos/effect/packages/effect/src/schema/index.ts rename to packages/effect/src/schema/index.ts diff --git a/packages/effect/src/socket/DatagramSocket.ts b/packages/effect/src/socket/DatagramSocket.ts new file mode 100644 index 000000000..9a6a52ed2 --- /dev/null +++ b/packages/effect/src/socket/DatagramSocket.ts @@ -0,0 +1,919 @@ +/** + * Pull-based UDP sockets with scoped native handles. + * + * Acquiring a reader opens and binds a socket owned by its scope. Only one + * reader is open at a time; another acquisition waits interruptibly for it to + * close. Use `Effect.retry` around a scoped consume loop to open a new socket + * on each retry. + * + * Writes wait while no reader is open, so a send-only client must still + * acquire a reader. + * + * **Example** (Portable request/reply client) + * + * ```ts import.meta.vitest + * import { Effect } from "effect" + * import { NetAddress } from "effect/net" + * import { DatagramSocket } from "effect/socket" + * + * const server = NetAddress.inetAddressFromIpStringUnsafe("127.0.0.1", 5353) + * + * export const request = (payload: string) => + * Effect.gen(function*() { + * const socket = yield* DatagramSocket.DatagramSocket + * const reader = yield* socket.reader + * const writer = yield* socket.writer + * yield* writer.write({ payload, address: server }) + * const [response] = yield* reader.pull + * return response.payload + * }).pipe(Effect.scoped) + * ``` + * + * @stability experimental + * @since 4.0.0 + */ +import type { NonEmptyReadonlyArray } from "../Array.ts" +import * as Context from "../Context.ts" +import * as Effect from "../Effect.ts" +import * as Fiber from "../Fiber.ts" +import { constVoid } from "../Function.ts" +import { args, contA, contAll, exitSucceed, makePrimitive, type Primitive, withFiber } from "../internal/core.ts" +import type { FiberImpl } from "../internal/effect.ts" +import * as Latch from "../Latch.ts" +import * as NetAddress from "../net/NetAddress.ts" +import * as Predicate from "../Predicate.ts" +import * as Schema from "../Schema.ts" +import * as Scope from "../Scope.ts" + +/** + * Runtime type identifier attached to `DatagramSocket` services. + * + * @stability experimental + * @category type IDs + * @since 4.0.0 + */ +export const TypeId = "~effect/socket/DatagramSocket" + +/** + * Service tag for UDP socket transports. + * + * @stability experimental + * @category services + * @since 4.0.0 + */ +export const DatagramSocket: Context.Service = Context.Service( + "effect/socket/DatagramSocket" +) + +/** + * A UDP socket with a scoped, exclusive reader. Acquiring `reader` opens and + * binds a native socket; subsequent acquisitions wait for its scope to close. + * Acquiring `writer` cannot fail, but writes wait for an open reader. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface DatagramSocket { + readonly [TypeId]: typeof TypeId + readonly reader: Effect.Effect + readonly writer: Effect.Effect +} + +/** + * A received packet. + * + * **Details** + * + * The payload is safe to retain. The sender's `address` is parsed on first + * read and cached. + * + * Reply with `writer.write({ payload, address: received })` to reuse the raw + * sender address without parsing. This requires a datagram from a platform + * adapter or `makeFromBackingSocket`. For hand-built datagrams or custom + * readers, use `{ payload, address: received.address }`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Datagram { + readonly payload: Uint8Array + readonly address: NetAddress.InetAddress +} + +/** + * A packet to send. + * + * **Details** + * + * String payloads are encoded as UTF-8. `address` can be omitted when the + * socket has a `peer` or is connected. On a connected socket an explicit + * `InetAddress` fails the write, while a received `Datagram` is allowed and + * goes out on the connected path. + * + * Pass a received `Datagram` as the destination to reply, or as the whole + * argument to echo it. Both reuse the raw sender address and require a + * datagram from a platform adapter or `makeFromBackingSocket`. Hand-built + * datagrams and custom readers must pass an explicit `InetAddress` instead. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface OutgoingDatagram { + readonly payload: Uint8Array | string + readonly address?: NetAddress.InetAddress | Datagram | undefined +} + +/** + * The receive side of an open native socket. + * + * **Details** + * + * `pull` returns everything queued since the last pull, or waits for the next + * packet. After a terminal error, `pull` first returns the queued packets and + * then fails with the same error until the reader's scope closes. Closing the + * scope discards the queue and fails every waiting `pull` with + * `DatagramSocketClosedError`. + * + * Concurrent pulls wait in FIFO order. Each incoming packet goes to the + * oldest waiter; a terminal error fails all waiters. + * + * `address` is the bound local address, parsed on first read and cached. + * + * `dropped` counts receive-buffer overflow since this reader opened, not + * kernel or network loss or packets discarded on scope close. + * + * `joinMulticast` joins a group until its scope closes; leave failures are + * ignored. Its `interface` selects where packets arrive (ingress), not the + * platform's `multicast.interface` for sending (egress). + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Reader { + readonly pull: Effect.Effect, DatagramSocketError> + readonly address: NetAddress.InetAddress + readonly dropped: () => number + readonly joinMulticast: (options: { + readonly group: NetAddress.MulticastAddress + readonly interface?: NetAddress.MulticastInterface | undefined + readonly source?: A | undefined + }) => Effect.Effect +} + +/** + * The send side of a `DatagramSocket`. Writes wait until a reader is open, so + * a send-only client must still acquire a reader. + * + * **Details** + * + * A write completes when the runtime reports the send's result, and send + * errors fail it with a `DatagramSocketWriteError` carrying the destination + * `address`. After a terminal reader error, writes fail with that error until + * the reader's scope closes, and then wait for the next reader. + * + * `writeAll` fails with the first error, without resending. Partial delivery is + * unspecified. The error has no packet index, and its `address` is absent on Bun. + * + * There is no built-in write timeout; use `Effect.timeout`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface Writer { + readonly write: (datagram: OutgoingDatagram) => Effect.Effect + readonly writeAll: (datagrams: NonEmptyReadonlyArray) => Effect.Effect +} + +/** + * Receive queue options. + * + * **Details** + * + * The queue is bounded in packets, with no byte cap. `capacity` defaults to + * 1024 and must be a positive safe integer. The worst-case + * memory is capacity × 64 KiB, about 64 MiB at the default. + * + * When the queue is full, `"dropping"` (the default) discards the incoming + * packet and `"sliding"` discards the oldest queued one. Both count the + * packet in `Reader.dropped`. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface ReceiveBufferOptions { + readonly capacity?: number | undefined + readonly strategy?: "dropping" | "sliding" | undefined +} + +/** + * Creates a `DatagramSocket` from its `reader` and `writer` effects. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const make = (options: { + readonly reader: DatagramSocket["reader"] + readonly writer: DatagramSocket["writer"] +}): DatagramSocket => + DatagramSocket.of({ + [TypeId]: TypeId, + reader: options.reader, + writer: options.writer + }) + +/** + * A native host and port, parsed on demand. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface BackingAddress { + readonly host: string + readonly port: number +} + +/** + * Native transport contract used by platform adapters. + * + * **Details** + * + * `address` is the raw bound address, read once at open. `scopeIds` maps IPv6 + * zone names to scope IDs, for parsing named zones and formatting + * destinations. `peer` is the default destination, and `connected` marks a + * natively connected socket, whose sends receive no destination. + * + * `send` and `sendMany` hand datagrams to the runtime and call `done` exactly + * once, synchronously or later, when the runtime reports the result. They must + * not throw. `sendMany` passes the index of the failing datagram to `done` + * when the runtime knows it, so core can attach its address. + * + * Adapters normalize native errors before passing them to core. + * Read destination `host` and `port` during the call; do not retain the record. + * + * `close` must not throw. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface BackingSocket { + readonly address: BackingAddress + readonly scopeIds?: ReadonlyMap | undefined + readonly peer?: BackingAddress | undefined + readonly connected?: boolean | undefined + readonly send: ( + payload: Uint8Array, + destination: BackingAddress | undefined, + done: (error?: DatagramSocketError) => void + ) => void + readonly sendMany: ( + payloads: ReadonlyArray, + destinations: ReadonlyArray, + done: (error?: DatagramSocketError, index?: number) => void + ) => void + readonly joinMulticast: (options: { + readonly group: NetAddress.MulticastAddress + readonly interface?: NetAddress.MulticastInterface | undefined + readonly source?: A | undefined + }) => Effect.Effect<() => Effect.Effect, DatagramSocketError> + readonly close: () => void +} + +/** + * Callbacks installed before opening the backing socket; packets may arrive + * synchronously. + * + * **Details** + * + * `onPacket` queues a received packet. `onReadError` is a terminal receive + * error and `onClose` reports the native socket closing underneath the + * reader; both are sticky. `onError` forwards errors with no write left to + * fail, such as ICMP reports, to the user's `onError` option. + * + * Each callback can be passed on its own, for example as a runtime's event + * listener. + * + * @stability experimental + * @category models + * @since 4.0.0 + */ +export interface BackingEvents { + readonly onPacket: (payload: Uint8Array, host: string, port: number) => void + readonly onReadError: (error: DatagramSocketError) => void + readonly onError: (error: DatagramSocketError) => void + readonly onClose: () => void +} + +/** + * The normalized kind of a send or receive failure. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export type IoErrorKind = "MessageTooLarge" | "Unreachable" | "ConnectionRefused" | "PermissionDenied" | "Unknown" + +/** + * Opening or binding the native socket, applying its options, or resolving + * a name failed. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DatagramSocketOpenError + extends Schema.Error("effect/socket/DatagramSocket/OpenError")({ + _tag: Schema.tag("DatagramSocketOpenError"), + kind: Schema.Literals(["AddressInUse", "AddressNotAvailable", "PermissionDenied", "Unknown"]), + cause: Schema.Defect() + }) +{} + +/** + * A receive failed, or an ICMP report arrived with no write left to fail. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DatagramSocketReadError + extends Schema.Error("effect/socket/DatagramSocket/ReadError")({ + _tag: Schema.tag("DatagramSocketReadError"), + kind: Schema.Literals(["MessageTooLarge", "Unreachable", "ConnectionRefused", "PermissionDenied", "Unknown"]), + cause: Schema.Defect() + }) +{} + +/** + * A send failed. `address` is the destination when it is known. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DatagramSocketWriteError + extends Schema.Error("effect/socket/DatagramSocket/WriteError")({ + _tag: Schema.tag("DatagramSocketWriteError"), + kind: Schema.Literals(["MessageTooLarge", "Unreachable", "ConnectionRefused", "PermissionDenied", "Unknown"]), + address: Schema.optional(Schema.InetAddress), + cause: Schema.Defect() + }) +{} + +/** + * The native socket closed underneath the reader, or the reader's scope + * closed. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DatagramSocketClosedError + extends Schema.Error("effect/socket/DatagramSocket/ClosedError")({ + _tag: Schema.tag("DatagramSocketClosedError") + }) +{} + +/** + * The runtime lacks a capability, such as source-specific multicast on Deno. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DatagramSocketUnsupportedError + extends Schema.Error("effect/socket/DatagramSocket/UnsupportedError")({ + _tag: Schema.tag("DatagramSocketUnsupportedError"), + capability: Schema.String, + runtime: Schema.String + }) +{ + /** + * Names the runtime and the capability it lacks. + * + * @stability experimental + * @since 4.0.0 + */ + override get message() { + return `${this.runtime} does not support ${this.capability}` + } +} + +/** + * Schema for the union of `DatagramSocketError` reasons. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export const DatagramSocketErrorReason = Schema.Union([ + DatagramSocketOpenError, + DatagramSocketReadError, + DatagramSocketWriteError, + DatagramSocketClosedError, + DatagramSocketUnsupportedError +]) +/** + * The union of `DatagramSocketError` reasons. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export type DatagramSocketErrorReason = typeof DatagramSocketErrorReason.Type + +/** + * Runtime type identifier attached to `DatagramSocketError` values. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export const DatagramSocketErrorTypeId = "~effect/socket/DatagramSocket/DatagramSocketError" + +/** + * Returns `true` when a value is a `DatagramSocketError`. + * + * @stability experimental + * @category guards + * @since 4.0.0 + */ +export const isDatagramSocketError = (u: unknown): u is DatagramSocketError => + Predicate.hasProperty(u, DatagramSocketErrorTypeId) + +/** + * The error raised by `DatagramSocket` operations, wrapping a specific + * reason. Its `cause` and `message` come from the reason. + * + * @stability experimental + * @category errors + * @since 4.0.0 + */ +export class DatagramSocketError + extends Schema.TaggedError(DatagramSocketErrorTypeId)("DatagramSocketError", { + _tag: Schema.tag("DatagramSocketError"), + reason: DatagramSocketErrorReason + }) +{ + // @effect-diagnostics-next-line overriddenSchemaConstructor:off + constructor(props: { readonly reason: DatagramSocketErrorReason }) { + if ("cause" in props.reason) { + super({ ...props, cause: props.reason.cause } as any) + } else { + super(props) + } + } + readonly [DatagramSocketErrorTypeId] = DatagramSocketErrorTypeId + override get message() { + return this.reason.message + } +} + +/** + * Builds a `DatagramSocket` over a backing socket, opening a new socket for + * each reader acquisition. + * + * **Details** + * + * `open` receives the event callbacks before the handle exists, so packets can + * be queued while it opens. Core owns the receive queue and its overflow + * strategy, reader exclusivity, sticky errors, lazy address parsing, the + * writer latch and destination formatting. The handle is closed when the + * reader's scope closes. If acquisition is interrupted while `open` is still + * running, the interruption returns at once, `open` is left to finish, and + * the handle it produces is closed. The next reader waits until that open has + * settled, so two native sockets never exist at once. An adopted `acquire` + * must therefore finish in bounded time. + * + * `open` runs in the reader's scope, with the services of the fiber that + * built the socket, so an adopted `acquire` can use them and register + * finalizers that run when the reader closes. Where the fiber acquiring the + * reader has a service with the same tag, its own takes precedence. + * + * Concurrent pulls are served in the order they started waiting. + * + * `onError` receives errors with no write left to fail. It runs + * synchronously, and anything it throws is ignored. + * + * Invalid `receiveBuffer.capacity` values cause a defect; use a positive + * safe integer. + * + * @stability experimental + * @category constructors + * @since 4.0.0 + */ +export const makeFromBackingSocket = ( + open: (events: BackingEvents) => Effect.Effect, + options?: { + readonly receiveBuffer?: ReceiveBufferOptions | undefined + readonly onError?: ((error: DatagramSocketError) => void) | undefined + } | undefined +): Effect.Effect> => + withFiber((fiber) => { + const capacity = options?.receiveBuffer?.capacity ?? 1024 + if (!Number.isSafeInteger(capacity) || capacity < 1) { + throw new RangeError(`DatagramSocket receive buffer capacity must be a positive integer, received ${capacity}`) + } + return Effect.succeed( + makeFromBackingSocketWithContext( + open, + fiber.context as Context.Context, + capacity, + options?.receiveBuffer?.strategy === "sliding", + options?.onError + ) + ) + }) + +const makeFromBackingSocketWithContext = ( + open: (events: BackingEvents) => Effect.Effect, + services: Context.Context, + capacity: number, + sliding: boolean, + onError: ((error: DatagramSocketError) => void) | undefined +): DatagramSocket => { + const free = Latch.makeUnsafe(true) + const latch = Latch.makeUnsafe(false) + let current: ReaderState | undefined + + const abandon = (state: ReaderState, opening: Fiber.Fiber) => { + state.close() + // Release reader ownership only after the interrupted open settles. + opening.addObserver((exit) => { + if (exit._tag === "Success") exit.value.close() + free.openUnsafe() + }) + } + + const release = (state: ReaderState) => { + state.close() + current = undefined + latch.closeUnsafe() + free.openUnsafe() + } + + const reader: DatagramSocket["reader"] = Effect.uninterruptibleMask( + Effect.fnUntraced(function*(restore) { + while (!free.closeUnsafe()) yield* restore(free.await) + const scope = yield* Effect.scope + const state = new ReaderState(capacity, sliding, onError) + // Native opens may be uncancellable. Interrupt only the wait; `abandon` + // closes any socket returned later. + const opened = open(state.events).pipe( + Effect.updateContext((input: Context.Context) => + Context.add(Context.merge(services, input), Scope.Scope, scope) + ) + ) as Effect.Effect + const opening = yield* Effect.forkDetach(opened, { startImmediately: true }) + state.handle = yield* restore(Fiber.join(opening)).pipe( + Effect.onError(() => Effect.sync(() => abandon(state, opening))) + ) + current = state + latch.openUnsafe() + yield* Effect.addFinalizer(() => Effect.sync(() => release(state))) + return makeReader(state) + }) + ) + + const write = (datagram: OutgoingDatagram): Effect.Effect => + withFiber((fiber) => { + const state = current + return state === undefined ? latch.whenOpen(write(datagram)) : state.write(datagram, fiber) + }) + const writeAll = (datagrams: NonEmptyReadonlyArray): Effect.Effect => + withFiber((fiber) => { + const state = current + return state === undefined ? latch.whenOpen(writeAll(datagrams)) : state.writeAll(datagrams, fiber) + }) + const writer: DatagramSocket["writer"] = Effect.succeed({ write, writeAll }) + + return make({ reader, writer }) +} + +const encoder = new TextEncoder() +const encode = (payload: Uint8Array | string) => typeof payload === "string" ? encoder.encode(payload) : payload +const emptyScopeIds: ReadonlyMap = new Map() + +const closedError = () => new DatagramSocketError({ reason: new DatagramSocketClosedError() }) + +// Rejected writes return this sentinel, avoiding per-packet error type checks. +const rejected: BackingAddress = { host: "", port: 0 } + +class DatagramImpl implements Datagram, BackingAddress { + payload: Uint8Array + host: string + port: number + readonly owner: ReaderState + #address: NetAddress.InetAddress | undefined + constructor(payload: Uint8Array, host: string, port: number, owner: ReaderState) { + this.payload = payload + this.host = host + this.port = port + this.owner = owner + } + get address(): NetAddress.InetAddress { + return this.#address ??= NetAddress.inetAddressFromNativeUnsafe(this.host, this.port, this.owner.scopeIds) + } + // only for a queued record, which no pull has returned yet + reuse(payload: Uint8Array, host: string, port: number) { + this.payload = payload + this.host = host + this.port = port + this.#address = undefined + } +} + +const targetOf = (datagram: OutgoingDatagram): NetAddress.InetAddress | DatagramImpl | undefined => + "host" in datagram ? datagram as DatagramImpl : datagram.address as NetAddress.InetAddress | DatagramImpl | undefined + +class ReaderState { + readonly capacity: number + readonly sliding: boolean + readonly listener: ((error: DatagramSocketError) => void) | undefined + readonly events: BackingEvents = { + onPacket: (payload, host, port) => this.push(payload, host, port), + onReadError: (error) => this.fail(error), + onError: (error) => this.report(error), + onClose: () => this.fail(closedError()) + } + + // set when `open` completes; packets can arrive before that + handle: BackingSocket | undefined = undefined + failure: Effect.Effect | undefined = undefined + // separate from `failure`: after a sticky read error the socket is still + // open, so ICMP reports still reach `onError` + closed = false + + // Sliding overflow turns the full buffer into a ring, oldest at `head`. + buffer: Array = [] + head = 0 + dropped = 0 + + // A single consumer uses the first waiter slot without touching the array. + waiter: FiberImpl | undefined = undefined + waiters: Array = [] + readonly unpark: Primitive = unpark(this) + + bound: NetAddress.InetAddress | undefined = undefined + // one-entry cache for `write` to the same explicit address + lastTarget: NetAddress.InetAddress | undefined = undefined + lastDestination: BackingAddress | undefined = undefined + rejection: DatagramSocketError | undefined = undefined + + constructor(capacity: number, sliding: boolean, listener: ((error: DatagramSocketError) => void) | undefined) { + this.capacity = capacity + this.sliding = sliding + this.listener = listener + } + + push(payload: Uint8Array, host: string, port: number) { + if (this.failure !== undefined) return + if (this.buffer.length >= this.capacity) return this.overflow(payload, host, port) + const datagram = new DatagramImpl(payload, host, port, this) + if (this.waiter !== undefined) return this.wake(datagram) + const buffer = this.buffer + buffer[buffer.length] = datagram + } + + wake(datagram: DatagramImpl) { + const fiber = this.waiter! + this.promoteWaiter() + fiber.evaluate(exitSucceed([datagram]) as any) + } + + overflow(payload: Uint8Array, host: string, port: number) { + this.dropped++ + if (!this.sliding) return + const buffer = this.buffer + const head = this.head + buffer[head].reuse(payload, host, port) + this.head = head + 1 === buffer.length ? 0 : head + 1 + } + + take(): NonEmptyReadonlyArray { + const buffer = this.buffer + const head = this.head + this.buffer = [] + if (head === 0) return buffer as unknown as NonEmptyReadonlyArray + this.head = 0 + const length = buffer.length + const batch = new Array(length) + let j = 0 + for (let i = head; i < length; i++) batch[j++] = buffer[i] + for (let i = 0; i < head; i++) batch[j++] = buffer[i] + return batch as unknown as NonEmptyReadonlyArray + } + + park(fiber: FiberImpl) { + if (this.waiter === undefined) this.waiter = fiber + else this.waiters.push(fiber) + } + + promoteWaiter() { + this.waiter = this.waiters.length === 0 ? undefined : this.waiters.shift() + } + + removeWaiter(fiber: FiberImpl) { + if (this.waiter === fiber) return this.promoteWaiter() + if (this.waiters.length === 0) return + const index = this.waiters.indexOf(fiber) + if (index !== -1) this.waiters.splice(index, 1) + } + + failWaiters() { + const waiter = this.waiter + if (waiter === undefined) return + const failure = this.failure! + const waiters = this.waiters + this.waiter = undefined + this.waiters = [] + waiter.evaluate(failure as any) + for (let i = 0; i < waiters.length; i++) waiters[i].evaluate(failure as any) + } + + fail(error: DatagramSocketError) { + if (this.failure !== undefined) return + this.failure = Effect.fail(error) + this.failWaiters() + } + + close() { + this.closed = true + this.buffer = [] + this.head = 0 + this.failure = Effect.fail(closedError()) + this.failWaiters() + this.handle?.close() + } + + report(error: DatagramSocketError) { + const listener = this.listener + if (listener === undefined || this.closed) return + try { + listener(error) + } catch { + // Listener errors cannot fail native callbacks. + } + } + + // packets can arrive before the handle does, so they read this lazily + get scopeIds(): ReadonlyMap { + return this.handle?.scopeIds ?? emptyScopeIds + } + + get address(): NetAddress.InetAddress { + return this.bound ??= NetAddress.inetAddressFromNativeUnsafe( + this.handle!.address.host, + this.handle!.address.port, + this.scopeIds + ) + } + + // Synchronous sends avoid parking; async completions resume inline. + // Ignore completions after interruption. + write(datagram: OutgoingDatagram, fiber: FiberImpl): Effect.Effect { + if (this.failure !== undefined) return this.failure + const target = targetOf(datagram) + const destination = this.destination(target) + if (destination === rejected) return Effect.fail(this.rejection!) + const handle = this.handle! + const payload = encode(datagram.payload) + let result: Effect.Effect | undefined + let parked = false + handle.send(payload, destination, (error) => { + if (result !== undefined) return + result = error === undefined ? Effect.void : Effect.fail(this.withAddress(error, target)) + if (parked) fiber.evaluate(result as any) + }) + if (result !== undefined) return result + parked = true + return fiber.yieldWith(() => { + parked = false + }) as any + } + + writeAll( + datagrams: NonEmptyReadonlyArray, + fiber: FiberImpl + ): Effect.Effect { + if (this.failure !== undefined) return this.failure + const payloads = new Array(datagrams.length) + const destinations = new Array(datagrams.length) + for (let i = 0; i < datagrams.length; i++) { + const datagram = datagrams[i] + const destination = this.destination(targetOf(datagram)) + if (destination === rejected) return Effect.fail(this.rejection!) + payloads[i] = encode(datagram.payload) + destinations[i] = destination + } + let result: Effect.Effect | undefined + let parked = false + this.handle!.sendMany(payloads, destinations, (error, index) => { + if (result !== undefined) return + result = error === undefined + ? Effect.void + : Effect.fail(index === undefined ? error : this.withAddress(error, targetOf(datagrams[index]!))) + if (parked) fiber.evaluate(result as any) + }) + if (result !== undefined) return result + parked = true + return fiber.yieldWith(() => { + parked = false + }) as any + } + + // Returns `rejected` on failure, with the error stored in `rejection`. + destination(target: NetAddress.InetAddress | DatagramImpl | undefined): BackingAddress | undefined { + const handle = this.handle! + if (target === undefined) { + if (handle.connected) return undefined + return handle.peer ?? this.reject("DatagramSocket write has no destination and the socket has no peer") + } + if (!("_tag" in target)) return handle.connected ? undefined : target as DatagramImpl + if (handle.connected) return this.reject("an explicit address cannot be used on a connected DatagramSocket", target) + if (target === this.lastTarget) return this.lastDestination + const destination = { host: NetAddress.formatNativeHost(target, this.scopeIds), port: target.port } + this.lastTarget = target + this.lastDestination = destination + return destination + } + + reject(message: string, address?: NetAddress.InetAddress): BackingAddress { + this.rejection = new DatagramSocketError({ + reason: new DatagramSocketWriteError({ kind: "Unknown", address, cause: new Error(message) }) + }) + return rejected + } + + withAddress( + error: DatagramSocketError, + target: NetAddress.InetAddress | DatagramImpl | undefined + ): DatagramSocketError { + const reason = error.reason + if (reason._tag !== "DatagramSocketWriteError" || reason.address !== undefined) return error + const address = this.addressOf(target) + if (address === undefined) return error + return new DatagramSocketError({ + reason: new DatagramSocketWriteError({ kind: reason.kind, address, cause: reason.cause }) + }) + } + + addressOf(target: NetAddress.InetAddress | DatagramImpl | undefined): NetAddress.InetAddress | undefined { + try { + if (target !== undefined) return "_tag" in target ? target : (target as DatagramImpl).address + const peer = this.handle!.peer + return peer === undefined + ? undefined + : NetAddress.inetAddressFromNativeUnsafe(peer.host, peer.port, this.scopeIds) + } catch { + // an unknown IPv6 zone leaves the address out + return undefined + } + } +} + +// Park the fiber itself and resume it inline, avoiding `Effect.callback` allocations. +const makePull = (state: ReaderState): Reader["pull"] => + withFiber((fiber): any => { + if (state.buffer.length !== 0) { + const batch = state.take() + const cont = fiber.getCont(contA) + return cont ? cont[contA](batch, fiber) : fiber.yieldWith(exitSucceed(batch)) + } + if (state.failure !== undefined) return state.failure + state.park(fiber) + fiber._stack.push(state.unpark) + return fiber.yieldWith(constVoid) + }) + +// `contAll` removes interrupted waiters too; after a wake, removal is a no-op. +const unpark: (state: ReaderState) => Primitive = makePrimitive({ + op: "DatagramSocketUnpark", + [contAll](fiber) { + this[args].removeWaiter(fiber) + } +}) + +const makeReader = (state: ReaderState): Reader => ({ + pull: makePull(state), + get address() { + return state.address + }, + dropped: () => state.dropped, + joinMulticast: (options) => + Effect.suspend(() => { + if (state.failure !== undefined) return state.failure + return Effect.acquireRelease( + state.handle!.joinMulticast(options), + (leave) => state.closed ? Effect.void : Effect.ignoreCause(leave()) + ) + }) +}) diff --git a/repos/effect/packages/effect/src/socket/Socket.ts b/packages/effect/src/socket/Socket.ts similarity index 96% rename from repos/effect/packages/effect/src/socket/Socket.ts rename to packages/effect/src/socket/Socket.ts index 43d8b094f..8258a2575 100644 --- a/repos/effect/packages/effect/src/socket/Socket.ts +++ b/packages/effect/src/socket/Socket.ts @@ -161,6 +161,12 @@ export interface Writer { * @since 4.0.0 */ export interface TlsUpgradeOptions { + /** + * DNS hostname used for Server Name Indication (SNI) and certificate identity + * verification during client upgrades. Server upgrades ignore this option. + * When omitted, the adapter retains its existing hostname behavior. + */ + readonly servername?: string | undefined readonly key?: | Redacted.Redacted | ReadonlyArray> @@ -770,13 +776,18 @@ export interface WebSocketLike { } /** - * Common options understood by a WebSocket client implementation. + * Client options for Node and Bun WebSocket implementations, allowing + * handshake headers and subprotocols to be supplied together. * * @stability unstable * @category models * @since 4.0.0 */ export interface WebSocketClientOptions { + /** + * Subprotocols to offer in the opening handshake. + */ + readonly protocols?: string | Array | undefined /** * Headers to include in the opening handshake. * @@ -828,6 +839,7 @@ export class WebSocketConstructor extends Context.Service< /** * Layer that provides `WebSocketConstructor` using `globalThis.WebSocket`. + * Rejects client options objects, including those with handshake headers. * * @stability unstable * @category layers @@ -845,6 +857,8 @@ export const layerWebSocketConstructorGlobal: Layer.Layer /** * Creates a `Socket` backed by a `WebSocketConstructor`, dialing the * WebSocket for each reader acquisition. + * Optional handshake headers require a Node or Bun constructor; without + * headers, protocols are passed directly for browser compatibility. * * @stability unstable * @category constructors @@ -853,13 +867,21 @@ export const layerWebSocketConstructorGlobal: Layer.Layer export const makeWebSocket = (url: string | Effect.Effect, options?: { readonly openTimeout?: Duration.Input | undefined readonly protocols?: string | Array | undefined + readonly headers?: Readonly> | undefined readonly highWaterMark?: number | undefined }): Effect.Effect => WebSocketConstructor.use((makeWs) => fromWebSocket( Effect.acquireRelease( (typeof url === "string" ? Effect.succeed(url) : url).pipe( - Effect.map((url) => makeWs(url, options?.protocols)) + Effect.map((url) => + makeWs( + url, + options?.headers === undefined + ? options?.protocols + : { protocols: options.protocols, headers: options.headers } + ) + ) ), (ws) => Effect.sync(() => ws.close(1000)) ), @@ -1136,6 +1158,7 @@ const defaultHighWaterMark = 64 * 1024 /** * Creates a binary `Channel` backed by a WebSocket URL, requiring a * `WebSocketConstructor` service. + * Supports subprotocols and, with a Node or Bun constructor, handshake headers. * * @stability unstable * @category constructors @@ -1146,6 +1169,7 @@ export const makeWebSocketChannel = ( options?: { readonly openTimeout?: Duration.Input | undefined readonly protocols?: string | Array | undefined + readonly headers?: Readonly> | undefined readonly highWaterMark?: number | undefined } ): Channel.Channel< @@ -1164,6 +1188,7 @@ export const makeWebSocketChannel = ( /** * Layer that provides a `Socket` service backed by a WebSocket URL or URL * effect. + * Supports subprotocols and, with a Node or Bun constructor, handshake headers. * * @stability unstable * @category layers @@ -1174,6 +1199,7 @@ export const layerWebSocket: ( options?: { readonly openTimeout?: Duration.Input | undefined readonly protocols?: string | Array | undefined + readonly headers?: Readonly> | undefined readonly highWaterMark?: number | undefined } | undefined ) => Layer.Layer = flow(makeWebSocket, Layer.effect(Socket)) diff --git a/repos/effect/packages/effect/src/socket/SocketServer.ts b/packages/effect/src/socket/SocketServer.ts similarity index 100% rename from repos/effect/packages/effect/src/socket/SocketServer.ts rename to packages/effect/src/socket/SocketServer.ts diff --git a/repos/effect/packages/effect/src/socket/index.ts b/packages/effect/src/socket/index.ts similarity index 73% rename from repos/effect/packages/effect/src/socket/index.ts rename to packages/effect/src/socket/index.ts index 29a5c45ce..9028de0ce 100644 --- a/repos/effect/packages/effect/src/socket/index.ts +++ b/packages/effect/src/socket/index.ts @@ -5,6 +5,12 @@ // @barrel: Auto-generated exports. Do not edit manually. +/** + * @stability experimental + * @since 4.0.0 + */ +export * as DatagramSocket from "./DatagramSocket.ts" + /** * @stability unstable * @since 4.0.0 diff --git a/repos/effect/packages/effect/src/sql/Migrator.ts b/packages/effect/src/sql/Migrator.ts similarity index 94% rename from repos/effect/packages/effect/src/sql/Migrator.ts rename to packages/effect/src/sql/Migrator.ts index 8d09037dd..408b72470 100644 --- a/repos/effect/packages/effect/src/sql/Migrator.ts +++ b/packages/effect/src/sql/Migrator.ts @@ -140,14 +140,21 @@ export const make = ({ PRIMARY KEY (migration_id) )`, pg: () => - Effect.catch( - sql`select ${table}::regclass`, - () => - sql`CREATE TABLE ${sql(table)} ( + Effect.flatMap( + migrationsTableExists(sql, table), + (exists) => + exists ? Effect.void : Effect.asVoid(sql`CREATE TABLE ${sql(table)} ( migration_id integer primary key, created_at timestamp with time zone not null default now(), name text not null -)` +)`).pipe( + Effect.catch((error) => + Effect.flatMap( + migrationsTableExists(sql, table), + (exists) => exists ? Effect.void : Effect.fail(error) + ) + ) + ) ), orElse: () => sql`CREATE TABLE IF NOT EXISTS ${sql(table)} ( @@ -328,6 +335,15 @@ export const make = ({ return completed }) +const migrationsTableExists = (sql: Client.SqlClient, table: string): Effect.Effect => { + // Match query quoting: to_regclass folds unquoted names to lowercase. + const quotedTable = sql`${sql(table)}`.compile(true)[0] + return Effect.map( + sql`SELECT 1 AS found WHERE to_regclass(${quotedTable}) IS NOT NULL`.withoutTransform, + (rows) => rows.length > 0 + ) +} + const migrationOrder = Order.make(([a], [b]) => Order.Number(a, b)) const isConstraintConflict = (error: SqlError): boolean => diff --git a/repos/effect/packages/effect/src/sql/SqlClient.ts b/packages/effect/src/sql/SqlClient.ts similarity index 89% rename from repos/effect/packages/effect/src/sql/SqlClient.ts rename to packages/effect/src/sql/SqlClient.ts index 61d39e97b..bf24fec61 100644 --- a/repos/effect/packages/effect/src/sql/SqlClient.ts +++ b/packages/effect/src/sql/SqlClient.ts @@ -9,7 +9,7 @@ * @stability unstable * @since 4.0.0 */ -import type * as Cause from "../Cause.ts" +import * as Cause from "../Cause.ts" import { Clock } from "../Clock.ts" import * as Context from "../Context.ts" import * as Effect from "../Effect.ts" @@ -275,16 +275,22 @@ export const make = Effect.fnUntraced(function*(options: SqlClient.MakeOptions) }) /** - * Builds the acquirers and failed-COMMIT cleanup for a SQLite client that + * Builds the acquirers, borrower and failed-COMMIT cleanup for a SQLite client that * shares one connection through a semaphore. * * **Details** * + * Both acquirers lease the connection into the current scope and hold the + * semaphore until that scope closes. The borrower holds the same semaphore + * until the statement finishes without creating a scope. Another fiber cannot + * begin a transaction underneath either. Effects that use the connection directly, + * such as an export, have to run inside `Effect.scoped`. + * * SQLite can keep a transaction open after COMMIT fails, for example on a * deferred foreign key violation. `onCommitFailure` rolls it back before the * connection is reused. If that ROLLBACK fails, the connection is rejected, and - * each acquisition retries the ROLLBACK once while holding the semaphore until - * it succeeds. The database is never reopened, and transactions started with a + * each acquisition or borrow retries the ROLLBACK once while holding the semaphore + * until it succeeds. The database is never reopened, and transactions started with a * plain `BEGIN` are only touched after a failed cleanup. * * Pass `isTransaction` when the driver can report whether a transaction is @@ -300,7 +306,8 @@ export const makeSqliteAcquirers = (options: { readonly semaphore: Semaphore.Semaphore readonly isTransaction?: ((conn: C) => boolean) | undefined }): { - readonly acquirer: Effect.Effect + readonly acquirer: Effect.Effect + readonly borrower: Connection.Borrower readonly transactionAcquirer: Effect.Effect readonly onCommitFailure: (conn: Connection.Connection) => Effect.Effect } => { @@ -336,15 +343,18 @@ export const makeSqliteAcquirers = (options: { }) }) + const lease = Effect.uninterruptibleMask((restore) => + Effect.flatMap(Effect.scope, (scope) => + restore(semaphore.take(1)).pipe( + Effect.andThen(Scope.addFinalizer(scope, semaphore.release(1))), + Effect.andThen(available) + )) + ) + return { - acquirer: semaphore.withPermits(1)(available), - transactionAcquirer: Effect.uninterruptibleMask((restore) => - Effect.flatMap(Effect.scope, (scope) => - restore(semaphore.take(1)).pipe( - Effect.andThen(Scope.addFinalizer(scope, semaphore.release(1))), - Effect.andThen(available) - )) - ), + acquirer: lease, + borrower: (f) => semaphore.withPermit(Effect.flatMap(available, f)), + transactionAcquirer: lease, onCommitFailure: (conn) => Effect.tapCause(rollback(conn as C), (cause) => Effect.sync(() => poison(cause))) } } @@ -359,6 +369,18 @@ const isNoTransactionError = (error: SqlError): boolean => { return /no transaction is active/i.test(message) } +/** + * A failed COMMIT cleanup leaves the connection in an unsafe state, so both the + * COMMIT error and the cleanup error are promoted to defects: typed recovery + * must not be able to hide them. + */ +const failuresToDefects = (cause: Cause.Cause): Cause.Cause => + Cause.fromReasons(cause.reasons.map((reason) => + Cause.isFailReason(reason) + ? Cause.makeDieReason(reason.error).annotate(Cause.reasonAnnotations(reason)) + : reason + )) + /** * Builds a transaction wrapper that begins top-level transactions, uses * savepoints for nested transactions, commits on success, and rolls back on @@ -424,15 +446,19 @@ export const makeWithTransaction = (options: { ) ), (exit) => { - let effect: Effect.Effect + let effect: Effect.Effect if (Exit.isSuccess(exit)) { if (id === 0) { 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))) - } + effect = onCommitFailure + ? Effect.catchCause(options.commit(conn), (commitCause) => + Effect.matchCauseEffect(onCommitFailure(conn), { + onFailure: (cleanupCause) => + Effect.failCause(failuresToDefects(Cause.combine(commitCause, cleanupCause))), + onSuccess: () => Effect.failCause(commitCause) + })) + : options.commit(conn) } else { span.event("effect.sql.transaction.savepoint", clock.currentTimeNanosUnsafe()) effect = Effect.void diff --git a/repos/effect/packages/effect/src/sql/SqlConnection.ts b/packages/effect/src/sql/SqlConnection.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlConnection.ts rename to packages/effect/src/sql/SqlConnection.ts diff --git a/repos/effect/packages/effect/src/sql/SqlError.ts b/packages/effect/src/sql/SqlError.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlError.ts rename to packages/effect/src/sql/SqlError.ts diff --git a/repos/effect/packages/effect/src/sql/SqlModel.ts b/packages/effect/src/sql/SqlModel.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlModel.ts rename to packages/effect/src/sql/SqlModel.ts diff --git a/repos/effect/packages/effect/src/sql/SqlResolver.ts b/packages/effect/src/sql/SqlResolver.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlResolver.ts rename to packages/effect/src/sql/SqlResolver.ts diff --git a/repos/effect/packages/effect/src/sql/SqlSchema.ts b/packages/effect/src/sql/SqlSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlSchema.ts rename to packages/effect/src/sql/SqlSchema.ts diff --git a/repos/effect/packages/effect/src/sql/SqlStream.ts b/packages/effect/src/sql/SqlStream.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/SqlStream.ts rename to packages/effect/src/sql/SqlStream.ts diff --git a/repos/effect/packages/effect/src/sql/Statement.ts b/packages/effect/src/sql/Statement.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/Statement.ts rename to packages/effect/src/sql/Statement.ts diff --git a/repos/effect/packages/effect/src/sql/index.ts b/packages/effect/src/sql/index.ts similarity index 100% rename from repos/effect/packages/effect/src/sql/index.ts rename to packages/effect/src/sql/index.ts diff --git a/repos/effect/packages/effect/src/testing/TestClock.ts b/packages/effect/src/testing/TestClock.ts similarity index 77% rename from repos/effect/packages/effect/src/testing/TestClock.ts rename to packages/effect/src/testing/TestClock.ts index d4a4951c8..95934b9dc 100644 --- a/repos/effect/packages/effect/src/testing/TestClock.ts +++ b/packages/effect/src/testing/TestClock.ts @@ -13,14 +13,14 @@ */ import * as Arr from "../Array.ts" import * as Clock from "../Clock.ts" +import * as Context from "../Context.ts" import * as Data from "../Data.ts" import * as Duration from "../Duration.ts" import * as Effect from "../Effect.ts" import * as Fiber from "../Fiber.ts" -import { flow } from "../Function.ts" import * as Latch from "../Latch.ts" import * as Layer from "../Layer.ts" -import * as Order from "../Order.ts" +import * as Scheduler from "../Scheduler.ts" import * as Semaphore from "../Semaphore.ts" /** @@ -40,6 +40,9 @@ import * as Semaphore from "../Semaphore.ts" * the time is set to on or after the time they are scheduled to run. Fork the * effect being tested, then adjust the clock time, and finally verify that the * expected effects have been performed. + * The ordered execution of sleeps registered after multiple scheduler hops + * requires fibers running under {@link layer}; providing a clock from + * {@link make} by hand does not install the tracking scheduler. * * **Example** (Testing timeouts deterministically) * @@ -142,7 +145,8 @@ export declare namespace TestClock { /** * Options used when constructing a `TestClock`. `warningDelay` controls how * long the live clock waits before logging a warning when a test uses time - * without advancing the test clock. + * without advancing the test clock, or when a fiber woken by the test clock + * keeps running without suspending. * * **Example** (Configuring the warning delay) * @@ -170,7 +174,8 @@ export declare namespace TestClock { export interface Options { /** * The amount of time to wait before displaying a warning message when a - * test is using time but is not advancing the `TestClock`. + * test is using time but is not advancing the `TestClock`, or when a fiber + * woken by `adjust` or `setTime` keeps running without suspending. */ readonly warningDelay?: Duration.Input } @@ -197,15 +202,30 @@ const warningMessage = "A test is using time, but is not advancing the test " + "clock, which may result in the test hanging. Use TestClock.adjust to " + "manually advance the time." +/** + * The warning message that will be displayed if a fiber woken by the + * `TestClock` does not suspend within the warning delay. + */ +const busyWarningMessage = "A fiber woken by the test clock is still running " + + "without suspending, so the test clock stopped waiting for it. Sleeps it " + + "starts later may be scheduled relative to a later time." + +/** + * The live clock of each `TestClock`, so a `TestClock` constructed while + * another one is the current clock uses the outer clock's live clock instead + * of the outer `TestClock`, which only advances when adjusted. + */ +const liveClocks = new WeakMap() + +interface WakeTracker { + pending: number + onIdle: (() => void) | undefined +} + const defaultOptions: Required = { warningDelay: "1 second" } -const SleepOrder = Order.flip(Order.Struct({ - timestamp: Order.Number, - sequence: Order.Number -})) - const nanosPerMilli = BigInt(1_000_000) const millisToNanos = (millis: number): bigint => { @@ -217,6 +237,14 @@ const millisToNanos = (millis: number): bigint => { /** * Creates a `TestClock` with optional configuration. * + * **Gotchas** + * + * The guarantee that sleeps registered after multiple scheduler hops run in + * order applies only to fibers running under {@link layer}, which + * `@effect/vitest`'s `it.effect` uses. Providing a clock from `make` by hand + * waits only one scheduler turn after each woken sleep. Prefer `layer` when + * forked fibers sleep. + * * **Example** (Creating a test clock) * * ```ts import.meta.vitest @@ -245,18 +273,21 @@ const millisToNanos = (millis: number): bigint => { * @category constructors * @since 4.0.0 */ -export const make = Effect.fnUntraced(function*( +export const make = (options?: TestClock.Options) => Effect.map(makeWithScheduler(options), ([testClock]) => testClock) + +const makeWithScheduler = Effect.fnUntraced(function*( options?: TestClock.Options ) { const config = { ...defaultOptions, ...options } - let sequence = 0 + // Sorted by descending timestamp, so `pop` wakes the earliest sleep first const sleeps: Array<{ - readonly sequence: number readonly timestamp: number readonly latch: Latch.Latch }> = [] - const liveClock = yield* Clock.clockWith(Effect.succeed) + const currentClock = yield* Clock.clockWith(Effect.succeed) + const liveClock = liveClocks.get(currentClock) ?? currentClock const warningSemaphore = yield* Semaphore.make(1) + const parentScheduler = yield* Scheduler.Scheduler let currentTimestamp: number = new Date(0).getTime() let currentWallNanos = BigInt(0) @@ -332,17 +363,97 @@ export const make = Effect.fnUntraced(function*( const millis = Duration.toMillis(duration) const end = currentTimestamp + millis if (end <= currentTimestamp) return - const latch = Latch.makeUnsafe() - sleeps.push({ - sequence: sequence++, - timestamp: end, - latch - }) - sleeps.sort(SleepOrder) - yield* warningStart - yield* latch.await + yield* Effect.acquireUseRelease( + Effect.sync(() => { + const entry = { timestamp: end, latch: Latch.makeUnsafe() } + // Insert before entries with an equal timestamp, so they wake in + // registration order + let low = 0 + let high = sleeps.length + while (low < high) { + const mid = (low + high) >>> 1 + if (sleeps[mid].timestamp > end) low = mid + 1 + else high = mid + } + sleeps.splice(low, 0, entry) + return entry + }), + (entry) => Effect.andThen(warningStart, entry.latch.await), + (entry) => + Effect.sync(() => { + // The entry is already gone if `run` popped it + const index = sleeps.indexOf(entry) + if (index !== -1) sleeps.splice(index, 1) + }) + ) }) + /** + * Tasks scheduled while waking a sleeper, and tasks scheduled by those tasks, + * are counted by that wake's tracker so `run` can wait for the woken fiber, + * and any fibers it resumes or forks, to suspend before checking the next + * sleep. + */ + let currentTracker: WakeTracker | undefined = undefined + + function runTracked(tracker: WakeTracker, f: () => void): void { + const previous = currentTracker + currentTracker = tracker + try { + f() + } finally { + currentTracker = previous + } + } + + const scheduler: Scheduler.Scheduler = { + executionMode: parentScheduler.executionMode, + shouldYield: (fiber) => parentScheduler.shouldYield(fiber), + makeDispatcher() { + const dispatcher = parentScheduler.makeDispatcher() + return { + scheduleTask(task, priority) { + const tracker = currentTracker + if (tracker === undefined) { + return dispatcher.scheduleTask(task, priority) + } + tracker.pending++ + dispatcher.scheduleTask(() => { + try { + runTracked(tracker, task) + } finally { + if (--tracker.pending === 0) tracker.onIdle?.() + } + }, priority) + }, + flush() { + dispatcher.flush() + } + } + } + } + + const wake = (latch: Latch.Latch) => + Effect.suspend(() => { + const tracker: WakeTracker = { pending: 0, onIdle: undefined } + runTracked(tracker, () => latch.openUnsafe()) + if (tracker.pending === 0) return Effect.yieldNow + return Effect.callback((resume) => { + if (tracker.pending === 0) return resume(Effect.void) + tracker.onIdle = () => resume(Effect.void) + return Effect.sync(() => { + tracker.onIdle = undefined + }) + }).pipe( + Effect.timeoutOrElse({ + duration: config.warningDelay, + orElse: () => Effect.logWarning(busyWarningMessage) + }), + withLive, + Effect.andThen(Effect.yieldNow) + ) + }) + const runSemaphore = yield* Semaphore.make(1) const run = Effect.fnUntraced(function*( step: (currentTimestamp: number) => number, @@ -366,8 +477,7 @@ export const make = Effect.fnUntraced(function*( if (Arr.lastNonEmpty(sleeps).timestamp > endTimestamp) break const entry = sleeps.pop()! advanceTo(entry.timestamp) - entry.latch.openUnsafe() - yield* Effect.yieldNow + yield* wake(entry.latch) } advanceTo(endTimestamp) if (adjustmentNanos !== undefined && Number.isFinite(endTimestamp)) { @@ -391,7 +501,7 @@ export const make = Effect.fnUntraced(function*( yield* Effect.addFinalizer(() => warningDone) - return { + const testClock = { currentTimeMillisUnsafe, currentTimeNanosUnsafe, monotonicTimeNanosUnsafe, @@ -403,6 +513,8 @@ export const make = Effect.fnUntraced(function*( sleep, withLive } + liveClocks.set(testClock, liveClock) + return [testClock, scheduler] as const }) /** @@ -438,10 +550,13 @@ export const make = Effect.fnUntraced(function*( * @category layers * @since 4.0.0 */ -export const layer: (options?: TestClock.Options) => Layer.Layer = flow( - make, - Layer.effect(Clock.Clock) -) as any +export const layer = (options?: TestClock.Options): Layer.Layer => + Layer.effectContext( + Effect.map(makeWithScheduler(options), ([testClock, scheduler]) => + Context.make(Clock.Clock, testClock).pipe( + Context.add(Scheduler.Scheduler, scheduler) + )) + ) as any /** * Retrieves the `TestClock` service for this test and uses it to run the diff --git a/repos/effect/packages/effect/src/testing/TestConsole.ts b/packages/effect/src/testing/TestConsole.ts similarity index 100% rename from repos/effect/packages/effect/src/testing/TestConsole.ts rename to packages/effect/src/testing/TestConsole.ts diff --git a/packages/effect/src/testing/TestCrypto.ts b/packages/effect/src/testing/TestCrypto.ts new file mode 100644 index 000000000..99fe9ca62 --- /dev/null +++ b/packages/effect/src/testing/TestCrypto.ts @@ -0,0 +1,94 @@ +/** + * Provides a deterministic implementation of the Effect `Crypto` service for + * tests. + * + * Random bytes, numbers, shuffles, UUIDs, and ULIDs are derived from a seeded + * pseudo-random generator. Digest operations are delegated to an underlying + * platform `Crypto` service. + * + * @stability unstable + * @since 4.0.0 + */ +import * as Crypto from "../Crypto.ts" +import * as Effect from "../Effect.ts" +import * as Layer from "../Layer.ts" +import * as Random from "../Random.ts" + +/** + * Creates a deterministic `Crypto` service using a seeded pseudo-random + * generator. + * + * **When to use** + * + * Use to construct a `Crypto` service whose random operations are reproducible + * in tests while preserving the digest implementation of a platform `Crypto` + * service. + * + * **Details** + * + * The effect requires an existing `Crypto` service and delegates digest + * operations to it. Each evaluation starts a new random sequence from `seed`; + * every other random operation is derived from that sequence. + * + * **Gotchas** + * + * The generated values are deterministic and must not be used for + * security-sensitive purposes. The sequence depends on the order in which + * random operations are evaluated. UUIDv7 and ULID values also include the + * current `Clock` time, which must be controlled separately when their complete + * output needs to be reproducible. + * + * @see {@link layer} for providing the service as a layer + * + * @stability unstable + * @category constructors + * @since 4.0.0 + */ +export const make: ( + seed: string | number +) => Effect.Effect = Effect.fnUntraced( + function*(_seed: string | number) { + const crypto = yield* Crypto.Crypto + const seededRandom = yield* Random.Random + return Crypto.make({ + randomBytes: (size) => { + const bytes = new Uint8Array(size) + for (let i = 0; i < size; i++) { + bytes[i] = Math.floor(seededRandom.nextDoubleUnsafe() * 256) + } + return bytes + }, + digest: crypto.digest + }) + }, + (effect, seed) => Random.withSeed(effect, seed) +) + +/** + * Creates a layer that provides deterministic `Crypto` random operations from + * a seed. + * + * **When to use** + * + * Use to provide reproducible random bytes, numbers, shuffles, UUIDs, and ULIDs + * to an Effect program under test. + * + * **Details** + * + * The layer requires an existing platform `Crypto` service whose digest + * implementation is retained. Each layer build starts the random sequence from + * `seed`. + * + * **Gotchas** + * + * UUIDv7 and ULID values also depend on the current `Clock`. Provide a test + * clock when their complete output needs to be reproducible. + * + * @see {@link make} for constructing the service directly + * + * @stability unstable + * @category layers + * @since 4.0.0 + */ +export const layer = (seed: string | number): Layer.Layer => + Layer.effect(Crypto.Crypto, make(seed)) diff --git a/repos/effect/packages/effect/src/testing/TestSchema.ts b/packages/effect/src/testing/TestSchema.ts similarity index 100% rename from repos/effect/packages/effect/src/testing/TestSchema.ts rename to packages/effect/src/testing/TestSchema.ts diff --git a/repos/effect/packages/effect/src/testing/index.ts b/packages/effect/src/testing/index.ts similarity index 80% rename from repos/effect/packages/effect/src/testing/index.ts rename to packages/effect/src/testing/index.ts index 12aca49b5..8f1663406 100644 --- a/repos/effect/packages/effect/src/testing/index.ts +++ b/packages/effect/src/testing/index.ts @@ -17,6 +17,12 @@ export * as TestClock from "./TestClock.ts" */ export * as TestConsole from "./TestConsole.ts" +/** + * @stability unstable + * @since 4.0.0 + */ +export * as TestCrypto from "./TestCrypto.ts" + /** * @stability unstable * @since 4.0.0 diff --git a/repos/effect/packages/effect/src/workers/Transferable.ts b/packages/effect/src/workers/Transferable.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/Transferable.ts rename to packages/effect/src/workers/Transferable.ts diff --git a/repos/effect/packages/effect/src/workers/Worker.ts b/packages/effect/src/workers/Worker.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/Worker.ts rename to packages/effect/src/workers/Worker.ts diff --git a/repos/effect/packages/effect/src/workers/WorkerError.ts b/packages/effect/src/workers/WorkerError.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/WorkerError.ts rename to packages/effect/src/workers/WorkerError.ts diff --git a/repos/effect/packages/effect/src/workers/WorkerRunner.ts b/packages/effect/src/workers/WorkerRunner.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/WorkerRunner.ts rename to packages/effect/src/workers/WorkerRunner.ts diff --git a/repos/effect/packages/effect/src/workers/index.ts b/packages/effect/src/workers/index.ts similarity index 100% rename from repos/effect/packages/effect/src/workers/index.ts rename to packages/effect/src/workers/index.ts diff --git a/repos/effect/packages/effect/src/workflow/Activity.ts b/packages/effect/src/workflow/Activity.ts similarity index 99% rename from repos/effect/packages/effect/src/workflow/Activity.ts rename to packages/effect/src/workflow/Activity.ts index fb883b8ce..fbd023788 100644 --- a/repos/effect/packages/effect/src/workflow/Activity.ts +++ b/packages/effect/src/workflow/Activity.ts @@ -336,6 +336,7 @@ const makeExecute = Effect.fnUntraced(function*< (_) => _._tag === "Suspended" ) if (result._tag === "Suspended") { + if (result.failed) instance.failed = true return yield* Workflow.suspend(instance) } return yield* result.exit diff --git a/repos/effect/packages/effect/src/workflow/DurableClock.ts b/packages/effect/src/workflow/DurableClock.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/DurableClock.ts rename to packages/effect/src/workflow/DurableClock.ts diff --git a/repos/effect/packages/effect/src/workflow/DurableDeferred.ts b/packages/effect/src/workflow/DurableDeferred.ts similarity index 77% rename from repos/effect/packages/effect/src/workflow/DurableDeferred.ts rename to packages/effect/src/workflow/DurableDeferred.ts index d3c48698c..61742708a 100644 --- a/repos/effect/packages/effect/src/workflow/DurableDeferred.ts +++ b/packages/effect/src/workflow/DurableDeferred.ts @@ -10,7 +10,6 @@ * @stability unstable * @since 4.0.0 */ -import * as Arr from "../Array.ts" import type { NonEmptyReadonlyArray } from "../Array.ts" import type * as Brand from "../Brand.ts" import * as Cause from "../Cause.ts" @@ -18,8 +17,9 @@ import * as Context from "../Context.ts" import * as Effect from "../Effect.ts" import * as Base64Url from "../encoding/Base64Url.ts" import * as Exit from "../Exit.ts" -import * as Filter from "../Filter.ts" +import * as Fiber from "../Fiber.ts" import { dual } from "../Function.ts" +import * as Latch from "../Latch.ts" import * as Option from "../Option.ts" import * as Schema from "../Schema.ts" import * as SchemaGetter from "../SchemaGetter.ts" @@ -134,6 +134,25 @@ const CurrentAttempt = Context.Reference( { defaultValue: () => 1 } ) +/** Awaits registered under the instance copy of a `recordHeld` body. */ +const awaitOwners = new WeakMap>() + +const registerAwait = (instance: WorkflowInstance["Service"], name: string) => { + instance.awaitedDeferreds.set(name, (instance.awaitedDeferreds.get(name) ?? 0) + 1) + awaitOwners.get(instance)?.push(name) +} + +const releaseAwaits = (instance: WorkflowInstance["Service"], names: Array) => { + for (const name of names) { + const count = instance.awaitedDeferreds.get(name)! - 1 + if (count === 0) { + instance.awaitedDeferreds.delete(name) + } else { + instance.awaitedDeferreds.set(name, count) + } + } +} + const await_: ( self: DurableDeferred ) => Effect.Effect< @@ -150,7 +169,7 @@ const await_: => - Effect.contextWith( - (context: Context.Context) => { - const engine = Context.get(context, EngineTag) - const parentInstance = Context.get(context, InstanceTag) - const instance = { ...parentInstance } - return Effect.onExit( - Effect.provideService(effect, InstanceTag, instance), - Effect.fnUntraced(function*(exit) { - if (Exit.isFailure(exit)) { - const [interrupts, reasons] = Arr.partition( - exit.cause.reasons, - Filter.fromPredicate(Cause.isInterruptReason) - ) - if (interrupts.length === exit.cause.reasons.length) { - // An interrupt-only exit is never a result: the effect was - // suspended, preempted or interrupted, so record nothing. - if (instance.suspended) { - parentInstance.suspended = true - } - return - } else if (interrupts.length > 0) { - exit = Exit.failCause(Cause.fromReasons(reasons)) - } + > => recordHeld(effect, self, false) +) + +/** + * Records `exit` into `self`. An interrupt-only exit is never a result: the + * effect was suspended, preempted or interrupted, so nothing is recorded. + * Interrupts mixed into a failure are dropped from the recorded cause. + */ +const recordExit = ( + engine: WorkflowEngine["Service"], + instance: WorkflowInstance["Service"], + self: DurableDeferred, + exit: Exit.Exit +) => { + if (Exit.isFailure(exit)) { + if (Cause.hasInterruptsOnly(exit.cause)) return Effect.void + if (Cause.hasInterrupts(exit.cause)) { + exit = Exit.failCause(Cause.fromReasons( + exit.cause.reasons.filter((reason) => !Cause.isInterruptReason(reason)) + )) + } + } + return engine.deferredDone(self, { + workflowName: instance.workflow._tag, + executionId: instance.executionId, + deferredName: self.name, + exit + }) +} + +/** + * Runs `effect` as one activity of the enclosing workflow and records its exit + * into `self`, returning a previously recorded exit instead when `checkCache` + * is set. The hold spans the lookup, `effect` and the write, so a parked + * sibling cannot suspend the run before the continuation starts. + */ +const recordHeld = Effect.fnUntraced(function*( + effect: Effect.Effect, + self: DurableDeferred, + checkCache: boolean +): Effect.fn.Return { + const engine = yield* EngineTag + const instance = yield* InstanceTag + const exit = yield* Workflow.wrapActivityResult( + Effect.gen(function*() { + if (checkCache) { + const cached = yield* engine.deferredResult(self) + if (Option.isSome(cached)) return cached + } + // `effect` counts on its own activity state, so a durable await inside + // it waits for its own siblings and not for the hold above. The copy + // starts unsuspended: a parked arm of an enclosing race leaves that + // copy marked suspended while only interrupting its own fiber. + const local: WorkflowInstance["Service"] = { + ...instance, + suspended: false, + activityState: { count: 0, latch: Latch.makeUnsafe() } + } + // A losing branch parked on a durable wait has already exited when the + // race settles, so this body owns the waits registered beneath it. + const owned: Array = [] + awaitOwners.set(local, owned) + // A parked `effect` interrupts the fiber it runs on. The child fiber + // keeps that from skipping the suspension handling below. + const fiber = yield* effect.pipe( + Effect.provideService(InstanceTag, local), + Effect.onExit((exit) => recordExit(engine, instance, self, exit)), + Effect.forkChild({ startImmediately: true }) + ) + return yield* Effect.onInterrupt(Fiber.await(fiber), () => Fiber.interrupt(fiber)).pipe( + Effect.map((exit) => + local.suspended && Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause) + ? Option.none() + : Option.some(exit) + ), + // The waits are over once the body has a result. Otherwise every branch + // parked, or an enclosing race interrupted the body, and the waits move + // to the enclosing owner, if any, to be released when that race settles. + Effect.onExit((result) => + Effect.sync(() => { + if (Exit.isSuccess(result) && Option.isSome(result.value)) { + releaseAwaits(instance, owned) + } else { + awaitOwners.get(instance)?.push(...owned) } - yield* engine.deferredDone(self, { - workflowName: instance.workflow._tag, - executionId: instance.executionId, - deferredName: self.name, - exit - }) }) ) - } - ) -) + ) + }), + // Every branch of `effect` parked: release the hold and let the enclosing + // activities finish before suspending. External preemption does not wait. + Option.isNone + ) + if (Option.isNone(exit)) { + return yield* Workflow.suspend(instance) + } + return yield* exit.value +}) /** * Runs effects as a durable race, returning a previously persisted result when @@ -298,17 +393,7 @@ export const raceAll = < success: options.success, error: options.error }) - return Effect.gen(function*() { - const engine = yield* EngineTag - const exit = yield* engine.deferredResult(deferred) - if (Option.isSome(exit)) { - return yield* exit.value - } - return yield* into( - Effect.raceAll(options.effects), - deferred - ) - }) + return recordHeld(Effect.raceAll(options.effects), deferred, true) } /** diff --git a/repos/effect/packages/effect/src/workflow/DurableQueue.ts b/packages/effect/src/workflow/DurableQueue.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/DurableQueue.ts rename to packages/effect/src/workflow/DurableQueue.ts diff --git a/repos/effect/packages/effect/src/workflow/Workflow.ts b/packages/effect/src/workflow/Workflow.ts similarity index 95% rename from repos/effect/packages/effect/src/workflow/Workflow.ts rename to packages/effect/src/workflow/Workflow.ts index 9cc8f8ffc..43dec1749 100644 --- a/repos/effect/packages/effect/src/workflow/Workflow.ts +++ b/packages/effect/src/workflow/Workflow.ts @@ -78,11 +78,26 @@ export interface Workflow< /** * Execute the workflow with the given payload. + * + * **Details** + * + * `discard: true` returns the execution ID without waiting for the result. + * The child remains linked to its parent for completion wakes and interruption. + * + * With `discard: true, detached: true`, the child neither wakes its parent + * nor gets interrupted with it. The cluster engine retains the parent for + * observability. + * + * **Gotchas** + * + * Do not await a detached execution later. Executing the same payload joins + * it, but its completion will not wake the awaiting workflow. */ readonly execute: ( payload: Payload["~type.make.in"], options?: { readonly discard?: Discard + readonly detached?: [Discard] extends [true] ? boolean | undefined : undefined } ) => Effect.Effect< Discard extends true ? string : Success["Type"], @@ -248,7 +263,7 @@ export interface AnyWithProps extends Any { readonly errorSchema: Schema.Top readonly execute: ( payload: any, - options?: { readonly discard?: boolean } + options?: { readonly discard?: boolean; readonly detached?: boolean | undefined } ) => Effect.Effect readonly resume: ( executionId: string @@ -352,7 +367,7 @@ const Proto = { execute( this: AnyWithProps, fields: any, - opts?: { readonly discard?: Discard } | undefined + opts?: { readonly discard?: Discard; readonly detached?: boolean | undefined } | undefined ) { return Effect.suspend(() => { const payload = this.payloadSchema.make(fields) @@ -366,6 +381,7 @@ const Proto = { executionId, payload, discard: opts?.discard, + detached: opts?.detached, suspendedRetrySchedule: this.suspendedRetrySchedule }) )) @@ -617,6 +633,11 @@ export class Complete extends Data.TaggedClass("Complete")<{ * Represents a suspended workflow execution, optionally carrying the cause that * triggered suspension. * + * **Details** + * + * `failed` marks this execution's own `SuspendOnFailure` and requires explicit + * `resume` or `interrupt`. Inherited child failures do not set it. + * * @stability unstable * @category schemas * @since 4.0.0 @@ -625,7 +646,8 @@ export class Suspended extends Schema.Class( "effect/workflow/Workflow/Suspended" )({ _tag: Schema.tag("Suspended"), - cause: Schema.optional(Schema.Cause(Schema.Never, Schema.Defect())) + cause: Schema.optional(Schema.Cause(Schema.Never, Schema.Defect())), + failed: Schema.optional(Schema.Boolean) }) { /** * Marks this value as a workflow result for runtime guards. @@ -697,11 +719,13 @@ export const intoResult = ( Effect.interruptible, suspendOnFailure ? Effect.catchCause((cause) => { - if (instance.abandoned) { + if (instance.abandoned || instance.interrupted) { return Effect.failCause(cause) } instance.suspended = true + // Ordinary suspension is a self-interrupt and also reaches here. if (!Cause.hasInterruptsOnly(cause)) { + instance.failed = true instance.cause = Cause.die(Cause.squash(cause)) } return Effect.interrupt @@ -720,7 +744,9 @@ export const intoResult = ( const abandoned = instance.abandoned && !instance.interrupted && hasInterruptsOnly instance.abandoned = abandoned if (instance.suspended && hasInterruptsOnly && !abandoned) { - return Effect.succeed(new Suspended({ cause: instance.cause })) + return Effect.succeed( + new Suspended(instance.failed ? { cause: instance.cause, failed: true } : { cause: instance.cause }) + ) } if (!instance.interrupted && hasInterruptsOnly) { // External interrupts exit without a result. When the cluster has diff --git a/repos/effect/packages/effect/src/workflow/WorkflowEngine.ts b/packages/effect/src/workflow/WorkflowEngine.ts similarity index 84% rename from repos/effect/packages/effect/src/workflow/WorkflowEngine.ts rename to packages/effect/src/workflow/WorkflowEngine.ts index 288499f3c..625436275 100644 --- a/repos/effect/packages/effect/src/workflow/WorkflowEngine.ts +++ b/packages/effect/src/workflow/WorkflowEngine.ts @@ -12,6 +12,7 @@ */ import * as Cause from "../Cause.ts" import * as Context from "../Context.ts" +import * as Deferred from "../Deferred.ts" import * as Effect from "../Effect.ts" import * as Exit from "../Exit.ts" import * as Fiber from "../Fiber.ts" @@ -75,6 +76,17 @@ export class WorkflowEngine extends Context.Service< /** * Execute a registered workflow. + * + * **Details** + * + * Unless `discard` is set, a caller outside any workflow waits for the + * result. While the execution is suspended, the caller retries on + * `suspendedRetrySchedule`; engines that observe the final result wake it + * sooner. `ClusterWorkflowEngine` does so only for callers on the runner + * that owns the execution. + * + * `detached: true` requires `discard: true` and skips parent completion + * wakes and interruption linkage. Otherwise, it is a defect. */ readonly execute: < Name extends string, @@ -88,6 +100,7 @@ export class WorkflowEngine extends Context.Service< readonly executionId: string readonly payload: Payload["Type"] readonly discard?: Discard | undefined + readonly detached?: boolean | undefined readonly suspendedRetrySchedule?: | Schedule.Schedule | undefined @@ -271,8 +284,17 @@ export class WorkflowInstance extends Context.Service< */ cause: Cause.Cause | undefined - /** Deferred names this run parked on; their completions preempt the run. */ - readonly awaitedDeferreds: Set + /** + * Whether this workflow or an activity hit `SuspendOnFailure`, excluding + * inherited child failures. + */ + failed: boolean + + /** + * Deferred names this run waits on, counted per live wait; their + * completions preempt the run. + */ + readonly awaitedDeferreds: Map /** Deferred names this run completed itself; their completions need not wake it. */ readonly completedDeferreds: Set @@ -286,17 +308,26 @@ export class WorkflowInstance extends Context.Service< static initial( workflow: Workflow.Any, executionId: string, - scope = Scope.makeUnsafe() + options?: { + readonly scope?: Scope.Closeable | undefined + /** + * Awaited deferred names shared with the run this instance belongs to. + * An activity instance registers its durable waits there so the run + * can be woken once it suspends, without those waits preempting it. + */ + readonly awaitedDeferreds?: Map | undefined + } ): WorkflowInstance["Service"] { return WorkflowInstance.of({ executionId, workflow, - scope, + scope: options?.scope ?? Scope.makeUnsafe(), suspended: false, interrupted: false, abandoned: false, cause: undefined, - awaitedDeferreds: new Set(), + failed: false, + awaitedDeferreds: options?.awaitedDeferreds ?? new Map(), completedDeferreds: new Set(), activityState: { count: 0, @@ -394,6 +425,8 @@ export interface Encoded { readonly payload: object readonly discard: Discard readonly parent?: WorkflowInstance["Service"] | undefined + /** Skip parent completion wakes and interruption linkage. */ + readonly detached?: boolean | undefined } ) => Effect.Effect< Discard extends true ? void : Workflow.Result @@ -414,6 +447,22 @@ export interface Encoded { workflow: Workflow.Any, executionId: string ) => Effect.Effect + + /** + * Waits until a suspended execution has a result other than `Suspended`. + * + * **Gotchas** + * + * External callers race this with their `suspendedRetrySchedule`, so it may + * never complete when the engine cannot observe the result. Completing while + * the execution is still suspended skips the sleep and spends a step of that + * schedule. A hook that keeps completing early makes the caller spin. + */ + readonly awaitComplete?: ( + workflow: Workflow.Any, + executionId: string + ) => Effect.Effect + readonly activityExecute: ( activity: Activity.Any, attempt: number @@ -486,11 +535,15 @@ export const makeUnsafe = (options: Encoded): WorkflowEngine["Service"] => readonly executionId: string readonly payload: Payload["Type"] readonly discard?: Discard | undefined + readonly detached?: boolean | undefined readonly suspendedRetrySchedule?: | Schedule.Schedule | undefined } ) { + if (opts.detached && !opts.discard) { + return yield* Effect.die(`${self._tag}.execute: detached requires discard`) + } const payload = opts.payload const executionId = opts.executionId const suspendedRetrySchedule = opts.suspendedRetrySchedule ?? defaultRetrySchedule @@ -499,7 +552,7 @@ export const makeUnsafe = (options: Encoded): WorkflowEngine["Service"] => // link interruption with parent workflow const parentInstance = yield* Effect.serviceOption(WorkflowInstance) - if (Option.isSome(parentInstance)) { + if (Option.isSome(parentInstance) && !opts.detached) { const instance = parentInstance.value yield* Effect.addFinalizer(() => { if (!instance.interrupted || (Option.isSome(result) && result.value._tag === "Complete")) { @@ -512,7 +565,8 @@ export const makeUnsafe = (options: Encoded): WorkflowEngine["Service"] => executionId, payload: payload as object, discard: opts.discard ?? false, - parent: Option.getOrUndefined(parentInstance) + parent: Option.getOrUndefined(parentInstance), + detached: opts.detached }) as Effect.Effect> if (opts.discard) { @@ -548,7 +602,10 @@ export const makeUnsafe = (options: Encoded): WorkflowEngine["Service"] => ) ) ) - yield* sleep + // Completion wakes the caller early; the schedule is the fallback. + yield* options.awaitComplete + ? Effect.raceFirst(sleep, options.awaitComplete(self, executionId)) + : sleep } }), poll: options.poll, @@ -644,6 +701,11 @@ const defaultRetrySchedule = Schedule.min([ Schedule.spaced(30000) ]) +type WakeMode = "explicit" | "automatic" + +const isUnwakeable = (result: Workflow.Result, mode: WakeMode) => + result._tag === "Complete" || (mode === "automatic" && result.failed === true) + /** * Layer that provides an in-memory `WorkflowEngine`. * @@ -683,8 +745,11 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng readonly parent: string | undefined instance: WorkflowInstance["Service"] interrupted: boolean - resumeRequested: boolean + // The strongest wake requested while a run was still finishing. + resumeRequested: WakeMode | undefined fiber: Fiber.Fiber> | undefined + // Completed once a run succeeds with `Complete`, after which it cannot resume. + readonly done: Deferred.Deferred } const executions = new Map() @@ -695,21 +760,22 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng const deferredState = makeDeferredState() - const resume = Effect.fnUntraced(function*(executionId: string): Effect.fn.Return { + const resume = Effect.fnUntraced(function*(executionId: string, mode: WakeMode): Effect.fn.Return { const state = executions.get(executionId) if (!state) return const exit = state.fiber?.pollUnsafe() - if (exit && exit._tag === "Success" && exit.value._tag === "Complete") { + if (exit && exit._tag === "Success" && isUnwakeable(exit.value, mode)) { return } else if (state.fiber && !exit) { - // A child can finish while the parent is still releasing its activity. - // Preserve the wake until that run has published its suspended result. - if (!state.resumeRequested) { - state.resumeRequested = true + // Queue wakes until the run finishes cleanup; explicit wakes take priority. + const requested = state.resumeRequested + state.resumeRequested = mode === "explicit" ? mode : requested ?? mode + if (requested === undefined) { yield* Fiber.await(state.fiber).pipe( Effect.flatMap(() => { - state.resumeRequested = false - return resume(executionId) + const mode = state.resumeRequested! + state.resumeRequested = undefined + return resume(executionId, mode) }), Effect.forkIn(scope) ) @@ -721,7 +787,7 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng const instance = WorkflowInstance.initial( state.instance.workflow, state.instance.executionId, - state.instance.scope + { scope: state.instance.scope } ) state.instance = instance state.fiber = yield* state.execute(state.payload, state.instance.executionId).pipe( @@ -740,7 +806,12 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng if (!state.parent || result._tag !== "Complete") { return Effect.void } - return Effect.forkIn(resume(state.parent), scope) + return Effect.forkIn(resume(state.parent, "automatic"), scope) + }), + Effect.onExit((exit) => { + return Exit.isSuccess(exit) && exit.value._tag === "Complete" + ? Deferred.succeed(state.done, void 0) + : Effect.void }), Effect.forkIn(entry.scope) ) @@ -771,12 +842,13 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng execute: entry.execute, instance: WorkflowInstance.initial(workflow, options.executionId), interrupted: false, - resumeRequested: false, + resumeRequested: undefined, fiber: undefined, - parent: options.parent?.executionId + done: Deferred.makeUnsafe(), + parent: options.detached ? undefined : options.parent?.executionId } executions.set(options.executionId, state) - yield* resume(options.executionId) + yield* resume(options.executionId, "explicit") } if (options.discard) return // Capture together so a wake that swaps in a replay cannot desync them. @@ -788,11 +860,16 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng } return (yield* exit) as any }), + awaitComplete: (_workflow, executionId) => + Effect.suspend(() => { + const state = executions.get(executionId) + return state ? Deferred.await(state.done) : Effect.never + }), interrupt: Effect.fnUntraced(function*(_workflow, executionId) { const state = executions.get(executionId) if (!state) return state.interrupted = true - yield* resume(executionId) + yield* resume(executionId, "explicit") }), interruptUnsafe: Effect.fnUntraced(function*(_workflow, executionId) { const state = executions.get(executionId) @@ -803,7 +880,7 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng } }), resume(_workflow, executionId) { - return resume(executionId) + return resume(executionId, "explicit") }, activityExecute: Effect.fnUntraced(function*(activity, attempt) { const instance = yield* WorkflowInstance @@ -820,13 +897,19 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng state = { exit: undefined } activities.set(activityId, state) } + // A pending interrupt replays only to rebuild compensations, so it must + // not run new activities. Finalizers run once `interrupted` is set. + if (executions.get(instance.executionId)?.interrupted && !instance.interrupted) { + return new Workflow.Suspended({}) + } const activityInstance = WorkflowInstance.initial(instance.workflow, instance.executionId) activityInstance.interrupted = instance.interrupted return yield* activity.executeEncoded.pipe( Workflow.intoResult, Effect.provideService(WorkflowInstance, activityInstance), Effect.onExit((exit) => { - state.exit = exit + // A preempted activity must run again on the next replay. + state.exit = Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause) ? undefined : exit return Effect.void }) ) @@ -857,7 +940,7 @@ export const layerMemory: Layer.Layer = Layer.effect(WorkflowEng deferredResults.set(id, options.exit) const wake = Effect.andThen( deferredState.deferredDone(options.executionId, options.deferredName), - resume(options.executionId) + resume(options.executionId, "automatic") ) return Effect.flatMap(Effect.serviceOption(WorkflowInstance), (instance) => // A workflow finalizer cannot wait for its own run's cleanup. diff --git a/repos/effect/packages/effect/src/workflow/WorkflowProxy.ts b/packages/effect/src/workflow/WorkflowProxy.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/WorkflowProxy.ts rename to packages/effect/src/workflow/WorkflowProxy.ts diff --git a/repos/effect/packages/effect/src/workflow/WorkflowProxyServer.ts b/packages/effect/src/workflow/WorkflowProxyServer.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/WorkflowProxyServer.ts rename to packages/effect/src/workflow/WorkflowProxyServer.ts diff --git a/repos/effect/packages/effect/src/workflow/index.ts b/packages/effect/src/workflow/index.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/index.ts rename to packages/effect/src/workflow/index.ts diff --git a/repos/effect/packages/effect/src/workflow/internal/crypto.ts b/packages/effect/src/workflow/internal/crypto.ts similarity index 100% rename from repos/effect/packages/effect/src/workflow/internal/crypto.ts rename to packages/effect/src/workflow/internal/crypto.ts diff --git a/repos/effect/packages/effect/test/Array.test.ts b/packages/effect/test/Array.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Array.test.ts rename to packages/effect/test/Array.test.ts diff --git a/repos/effect/packages/effect/test/ArrayNaNIndex.test.ts b/packages/effect/test/ArrayNaNIndex.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ArrayNaNIndex.test.ts rename to packages/effect/test/ArrayNaNIndex.test.ts diff --git a/repos/effect/packages/effect/test/AtomRef.test.ts b/packages/effect/test/AtomRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/AtomRef.test.ts rename to packages/effect/test/AtomRef.test.ts diff --git a/repos/effect/packages/effect/test/BigDecimal.test.ts b/packages/effect/test/BigDecimal.test.ts similarity index 100% rename from repos/effect/packages/effect/test/BigDecimal.test.ts rename to packages/effect/test/BigDecimal.test.ts diff --git a/repos/effect/packages/effect/test/BigInt.test.ts b/packages/effect/test/BigInt.test.ts similarity index 100% rename from repos/effect/packages/effect/test/BigInt.test.ts rename to packages/effect/test/BigInt.test.ts diff --git a/repos/effect/packages/effect/test/Boolean.test.ts b/packages/effect/test/Boolean.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Boolean.test.ts rename to packages/effect/test/Boolean.test.ts diff --git a/repos/effect/packages/effect/test/Brand.test.ts b/packages/effect/test/Brand.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Brand.test.ts rename to packages/effect/test/Brand.test.ts diff --git a/repos/effect/packages/effect/test/ByteSize.test.ts b/packages/effect/test/ByteSize.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ByteSize.test.ts rename to packages/effect/test/ByteSize.test.ts diff --git a/repos/effect/packages/effect/test/Cache.test.ts b/packages/effect/test/Cache.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Cache.test.ts rename to packages/effect/test/Cache.test.ts diff --git a/repos/effect/packages/effect/test/Cause.test.ts b/packages/effect/test/Cause.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Cause.test.ts rename to packages/effect/test/Cause.test.ts diff --git a/repos/effect/packages/effect/test/CauseMapAnnotations.test.ts b/packages/effect/test/CauseMapAnnotations.test.ts similarity index 100% rename from repos/effect/packages/effect/test/CauseMapAnnotations.test.ts rename to packages/effect/test/CauseMapAnnotations.test.ts diff --git a/repos/effect/packages/effect/test/Channel.test.ts b/packages/effect/test/Channel.test.ts similarity index 97% rename from repos/effect/packages/effect/test/Channel.test.ts rename to packages/effect/test/Channel.test.ts index a005b99ab..4144e9324 100644 --- a/repos/effect/packages/effect/test/Channel.test.ts +++ b/packages/effect/test/Channel.test.ts @@ -33,6 +33,26 @@ describe("Channel", () => { }) describe("constructors", () => { + it.live("fromAsyncIterable awaits idle iterator cleanup on early close", () => + Effect.gen(function*() { + let finalized = false + async function* source() { + try { + yield [1] as const + yield [2] as const + } finally { + await new Promise((resolve) => setTimeout(resolve, 10)) + finalized = true + } + } + yield* Channel.fromAsyncIterable(source(), (error) => error).pipe( + Stream.fromChannel, + Stream.take(1), + Stream.runDrain + ) + assert.strictEqual(finalized, true) + })) + for ( const [name, constructor] of [["callback", Channel.callback], ["callbackArray", Channel.callbackArray]] as const ) { diff --git a/repos/effect/packages/effect/test/Chunk.test.ts b/packages/effect/test/Chunk.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Chunk.test.ts rename to packages/effect/test/Chunk.test.ts diff --git a/repos/effect/packages/effect/test/Clock.test.ts b/packages/effect/test/Clock.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Clock.test.ts rename to packages/effect/test/Clock.test.ts diff --git a/repos/effect/packages/effect/test/Combiner.test.ts b/packages/effect/test/Combiner.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Combiner.test.ts rename to packages/effect/test/Combiner.test.ts diff --git a/repos/effect/packages/effect/test/Config.test.ts b/packages/effect/test/Config.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Config.test.ts rename to packages/effect/test/Config.test.ts diff --git a/repos/effect/packages/effect/test/ConfigProvider.test.ts b/packages/effect/test/ConfigProvider.test.ts similarity index 96% rename from repos/effect/packages/effect/test/ConfigProvider.test.ts rename to packages/effect/test/ConfigProvider.test.ts index e8f7d80f9..8fbb18084 100644 --- a/repos/effect/packages/effect/test/ConfigProvider.test.ts +++ b/packages/effect/test/ConfigProvider.test.ts @@ -851,10 +851,12 @@ DB_PASS=\${PASSWORD} ` SOURCE=a$&b$'c$\`d TARGET=\${SOURCE} +ESCAPED=\\$SOURCE/$SOURCE `, { expandVariables: true } ) await assertSuccess(provider, ["TARGET"], ConfigProvider.makeValue("a$&b$'c$`d")) + await assertSuccess(provider, ["ESCAPED"], ConfigProvider.makeValue("$SOURCE/a$&b$'c$`d")) }) it("expansion defaults are used only for empty or unset variables", async () => { @@ -862,15 +864,21 @@ TARGET=\${SOURCE} ` SET=actual EMPTY= +EXPANDS_EMPTY=$EMPTY +BRACED=a}b FROM_SET=\${SET:-fallback} FROM_EMPTY=\${EMPTY:-fallback} FROM_UNSET=\${UNSET:-fallback} +FROM_EXPANDS_EMPTY=\${EXPANDS_EMPTY:-fallback} +NESTED=\${UNSET:-\${BRACED}} `, { expandVariables: true } ) await assertSuccess(provider, ["FROM_SET"], ConfigProvider.makeValue("actual")) await assertSuccess(provider, ["FROM_EMPTY"], ConfigProvider.makeValue("fallback")) await assertSuccess(provider, ["FROM_UNSET"], ConfigProvider.makeValue("fallback")) + await assertSuccess(provider, ["FROM_EXPANDS_EMPTY"], ConfigProvider.makeValue("fallback")) + await assertSuccess(provider, ["NESTED"], ConfigProvider.makeValue("a}b")) }) it("does not expand missing inherited variables", async () => { @@ -879,6 +887,45 @@ FROM_UNSET=\${UNSET:-fallback} }) await assertMissing(provider, ["VALUE"]) }) + + it("expands self and circular references to empty values", async () => { + const provider = ConfigProvider.fromDotEnvContents( + ` +PATH=$PATH:/opt/bin +A=$B +B=$A +SELF_DEFAULT=\${SELF_DEFAULT:-fallback} +`, + { expandVariables: true } + ) + await assertSuccess(provider, ["PATH"], ConfigProvider.makeValue(":/opt/bin")) + await assertMissing(provider, ["A"]) + await assertSuccess(provider, ["SELF_DEFAULT"], ConfigProvider.makeValue("fallback")) + }) + + it("does not scan expanded text again", async () => { + const provider = ConfigProvider.fromDotEnvContents( + ` +USER=app +HOME_DIR=/home/$USER +LOG_DIR=\${HOME_DIR}_logs +NAME=$USER\${SUFFIX:-_v1} +LITERAL=$USER/$ +`, + { expandVariables: true } + ) + await assertSuccess(provider, ["LOG_DIR"], ConfigProvider.makeValue("/home/app_logs")) + await assertSuccess(provider, ["NAME"], ConfigProvider.makeValue("app_v1")) + await assertSuccess(provider, ["LITERAL"], ConfigProvider.makeValue("app/$")) + }) + + it("expands long sequences of adjacent references without overflowing", async () => { + const provider = ConfigProvider.fromDotEnvContents( + `A=a\nVALUE=${"$A".repeat(20_000)}`, + { expandVariables: true } + ) + await assertSuccess(provider, ["VALUE"], ConfigProvider.makeValue("a".repeat(20_000))) + }) }) describe("fromDotEnv", () => { diff --git a/repos/effect/packages/effect/test/Context.test.ts b/packages/effect/test/Context.test.ts similarity index 73% rename from repos/effect/packages/effect/test/Context.test.ts rename to packages/effect/test/Context.test.ts index 19695642c..90e8621e7 100644 --- a/repos/effect/packages/effect/test/Context.test.ts +++ b/packages/effect/test/Context.test.ts @@ -5,6 +5,10 @@ import * as Option from "effect/Option" import * as Redactable from "effect/Redactable" import { describe, it } from "vitest" +interface ContextInternals { + readonly _flat: unknown +} + describe("Context", () => { const A = Context.Service("ContextTest/A") const B = Context.Service("ContextTest/B") @@ -98,19 +102,21 @@ describe("Context", () => { }) it("distinguishes an undefined service from an absent service", () => { - const Undefined = Context.Service("ContextTest/Undefined", { fiberCached: true }) + const Undefined = Context.Service("ContextTest/Undefined", { fiberCached: true }) const Missing = Context.Service("ContextTest/Missing") const Ref = Context.Reference("ContextTest/UndefinedRef", { defaultValue: () => "default", fiberCached: true }) - const context = Context.make(Undefined, undefined).pipe(Context.add(Ref, undefined)) + const context = Context.make(Undefined, 1).pipe(Context.add(Undefined, undefined), Context.add(Ref, undefined)) assertTrue(Context.getOption(context, Undefined)._tag === "Some") assertTrue(Context.getOption(context, Missing)._tag === "None") strictEqual(Context.getUnsafe(context, Undefined), undefined) strictEqual(Context.getOrElse(context, Undefined, () => 1), undefined) deepStrictEqual(Context.getOption(context, Ref), Option.some(undefined)) + deepStrictEqual([...Context.pick(Undefined)(context).mapUnsafe], [[Undefined.key, undefined]]) + deepStrictEqual([...Context.pick(Missing)(context).mapUnsafe], []) strictEqual(Context.get(context, Ref), undefined) }) @@ -130,6 +136,50 @@ describe("Context", () => { assertTrue(Context.hasSameCache(Context.empty(), context)) }) + it("rebases a shadowing overlay chain from a cold or warm parent", () => { + const keys = Array.from({ length: 8 }, (_, i) => Context.Service(`ContextTest/Rebase${i}`)) + const base = Context.make(A, 1).pipe(Context.add(B, 2)) + const flat = Context.makeUnsafe(new Map(base.mapUnsafe)) + // Build a depth-8 chain so the next add rebases. + let parent = Context.add(flat, A, 10) + for (let i = 0; i < 7; i++) { + parent = Context.add(parent, keys[i], i) + } + const expected = [[A.key, 10], [B.key, 2], ...keys.slice(0, 7).map((key, i) => [key.key, i]), [C.key, 3]] + + const cold = Context.add(parent, C, 3) + deepStrictEqual([...cold.mapUnsafe], expected) + + const parentEntries = [...parent.mapUnsafe] + const warm = Context.add(parent, C, 3) + deepStrictEqual([...warm.mapUnsafe], expected) + deepStrictEqual([...parent.mapUnsafe], parentEntries) + strictEqual(Context.getOption(parent, C)._tag, "None") + }) + + it("rebases a large base without warming the pre-rebase context's flat cache", () => { + const baseSize = 50 + const baseKeys = Array.from({ length: baseSize }, (_, i) => Context.Service(`ContextTest/Rebase${i}`)) + const base = Context.makeUnsafe(new Map(baseKeys.map((key, i) => [key.key, i]))) + const pushKeys = Array.from({ length: 9 }, (_, i) => Context.Service(`ContextTest/Push${i}`)) + + let preRebase: Context.Context = base + for (let i = 0; i < pushKeys.length - 1; i++) { + preRebase = Context.add(preRebase, pushKeys[i], i) + } + const context = Context.add(preRebase, pushKeys[pushKeys.length - 1], pushKeys.length - 1) + + strictEqual((preRebase as any as ContextInternals)._flat, undefined) + + strictEqual(context.mapUnsafe.size, baseSize + pushKeys.length) + for (let i = 0; i < baseKeys.length; i++) { + strictEqual(Context.getUnsafe(context, baseKeys[i]), i) + } + for (let i = 0; i < pushKeys.length; i++) { + strictEqual(Context.getUnsafe(context, pushKeys[i]), i) + } + }) + it("flattens after repeated base fall-throughs", () => { const context = Context.make(A, 1).pipe(Context.add(B, 2)) const impl = context as any @@ -214,9 +264,20 @@ describe("Context", () => { deepStrictEqual([...merged.mapUnsafe], [[A.key, 3], [B.key, 2], [C.key, 4]]) }) - it("pick and omit retain source ordering", () => { - const source = Context.make(A, 1).pipe(Context.add(B, 2), Context.add(C, 3)) + it("pick and omit preserve services and source ordering", () => { + const source = Context.make(A, 1).pipe(Context.add(B, 2), Context.add(C, 0), Context.add(C, 3)) + const impl = source as any as ContextInternals + strictEqual(impl._flat, undefined) + const empty = Context.pick()(source) + strictEqual(impl._flat, undefined) + const picked = Context.pick(C)(source) + strictEqual(impl._flat, undefined) + deepStrictEqual([...picked.mapUnsafe], [[C.key, 3]]) + deepStrictEqual([...empty.mapUnsafe], []) + assertFalse(Context.hasSameCache(source, picked)) + assertFalse(Context.hasSameCache(source, empty)) + deepStrictEqual([...source.mapUnsafe], [[A.key, 1], [B.key, 2], [C.key, 3]]) deepStrictEqual([...Context.pick(C, A)(source).mapUnsafe], [[A.key, 1], [C.key, 3]]) deepStrictEqual([...Context.omit(B)(source).mapUnsafe], [[A.key, 1], [C.key, 3]]) }) @@ -230,6 +291,7 @@ describe("Context", () => { } }) const context = Context.empty() + deepStrictEqual([...Context.pick(Ref)(context).mapUnsafe], []) strictEqual(calls, 0) const first = Context.get(context, Ref) diff --git a/repos/effect/packages/effect/test/Cron.test.ts b/packages/effect/test/Cron.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Cron.test.ts rename to packages/effect/test/Cron.test.ts diff --git a/repos/effect/packages/effect/test/Crypto.test.ts b/packages/effect/test/Crypto.test.ts similarity index 81% rename from repos/effect/packages/effect/test/Crypto.test.ts rename to packages/effect/test/Crypto.test.ts index 109aad9d8..0a76d7ee9 100644 --- a/repos/effect/packages/effect/test/Crypto.test.ts +++ b/packages/effect/test/Crypto.test.ts @@ -96,16 +96,38 @@ describe("Crypto", () => { it.effect("randomUUIDv4 formats UUID bytes from randomBytes", () => Effect.gen(function*() { const crypto = yield* Crypto.Crypto - const uuid = yield* crypto.randomUUIDv4 + const uuid = yield* crypto.randomUUIDv4() assert.strictEqual(uuid, "00010203-0405-4607-8809-0a0b0c0d0e0f") + const hex = yield* crypto.randomUUIDv4({ format: "hex" }) + assert.strictEqual(hex, "00010203-0405-4607-8809-0a0b0c0d0e0f") }).pipe(Effect.provideService(Crypto.Crypto, testCrypto))) it.effect("randomUUIDv7 formats UUID bytes with the Clock timestamp", () => Effect.gen(function*() { yield* TestClock.setTime(0x0123456789ab) const crypto = yield* Crypto.Crypto - const uuid = yield* crypto.randomUUIDv7 + const uuid = yield* crypto.randomUUIDv7() assert.strictEqual(uuid, "01234567-89ab-7607-8809-0a0b0c0d0e0f") + const hex = yield* crypto.randomUUIDv7({ format: "hex" }) + assert.strictEqual(hex, "01234567-89ab-7607-8809-0a0b0c0d0e0f") + }).pipe(Effect.provideService(Crypto.Crypto, testCrypto))) + + it.effect("randomUUIDv4 returns 16 bytes with the version and variant bits set", () => + Effect.gen(function*() { + const crypto = yield* Crypto.Crypto + const bytes = yield* crypto.randomUUIDv4({ format: "bytes" }) + assert.deepStrictEqual(bytes, Uint8Array.of(0, 1, 2, 3, 4, 5, 0x46, 7, 0x88, 9, 10, 11, 12, 13, 14, 15)) + }).pipe(Effect.provideService(Crypto.Crypto, testCrypto))) + + it.effect("randomUUIDv7 returns 16 bytes containing the Clock timestamp, version and variant", () => + Effect.gen(function*() { + yield* TestClock.setTime(0x0123456789ab) + const crypto = yield* Crypto.Crypto + const bytes = yield* crypto.randomUUIDv7({ format: "bytes" }) + assert.deepStrictEqual( + bytes, + Uint8Array.of(0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0x76, 7, 0x88, 9, 10, 11, 12, 13, 14, 15) + ) }).pipe(Effect.provideService(Crypto.Crypto, testCrypto))) it.effect("randomULID encodes the Clock timestamp and random bytes", () => diff --git a/repos/effect/packages/effect/test/Data.test.ts b/packages/effect/test/Data.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Data.test.ts rename to packages/effect/test/Data.test.ts diff --git a/repos/effect/packages/effect/test/DateTime.test.ts b/packages/effect/test/DateTime.test.ts similarity index 95% rename from repos/effect/packages/effect/test/DateTime.test.ts rename to packages/effect/test/DateTime.test.ts index 30f975d8e..01cec4da4 100644 --- a/repos/effect/packages/effect/test/DateTime.test.ts +++ b/packages/effect/test/DateTime.test.ts @@ -105,6 +105,33 @@ describe("DateTime", () => { const future = Option.map(now, (value) => DateTime.add(value, { years: 1 })) assertSome(Option.map(future, DateTime.formatIso), "2025-02-28T00:00:00.000Z") })) + + it("clips fractional time units separately", () => { + strictEqual( + DateTime.toEpochMillis(DateTime.add(DateTime.makeUnsafe(1000), { milliseconds: 0.5, seconds: 0.0005 })), + 1000 + ) + }) + + it("adds and subtracts elapsed time in the repeated DST hour", () => { + const secondOneThirty = DateTime.makeZonedUnsafe("2025-11-02T06:30:00.000Z", { + timeZone: "America/New_York" + }) + strictEqual( + DateTime.formatIso(DateTime.add(secondOneThirty, { minutes: 1, seconds: 1, milliseconds: 1 })), + "2025-11-02T06:31:01.001Z" + ) + strictEqual(DateTime.formatIso(DateTime.subtract(secondOneThirty, { hours: 1 })), "2025-11-02T05:30:00.000Z") + }) + + it("adds elapsed hours before calendar days across DST", () => { + const beforeGap = DateTime.makeZonedUnsafe("2025-03-08T17:00:00.000Z", { + timeZone: "America/New_York" + }) + strictEqual(DateTime.formatIso(DateTime.add(beforeGap, { hours: 24 })), "2025-03-09T17:00:00.000Z") + strictEqual(DateTime.formatIso(DateTime.add(beforeGap, { days: 1 })), "2025-03-09T16:00:00.000Z") + strictEqual(DateTime.formatIso(DateTime.add(beforeGap, { days: 1, hours: 24 })), "2025-03-10T17:00:00.000Z") + }) }) describe("endOf", () => { @@ -498,6 +525,17 @@ describe("DateTime", () => { })) }) + describe("toDate", () => { + it("stores zoned wall-clock time in UTC fields", () => { + const zoned = DateTime.makeZonedUnsafe("2024-07-01T12:00:00Z", { + timeZone: "Europe/London" + }) + + strictEqual(DateTime.toDate(zoned).toISOString(), "2024-07-01T13:00:00.000Z") + strictEqual(DateTime.toDateUtc(zoned).toISOString(), "2024-07-01T12:00:00.000Z") + }) + }) + describe("nowAsDate", () => { it.effect("should return the current Date", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/Deferred.test.ts b/packages/effect/test/Deferred.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Deferred.test.ts rename to packages/effect/test/Deferred.test.ts diff --git a/repos/effect/packages/effect/test/Duration.test.ts b/packages/effect/test/Duration.test.ts similarity index 95% rename from repos/effect/packages/effect/test/Duration.test.ts rename to packages/effect/test/Duration.test.ts index e666f8708..f0c969ad0 100644 --- a/repos/effect/packages/effect/test/Duration.test.ts +++ b/packages/effect/test/Duration.test.ts @@ -113,6 +113,11 @@ describe("Duration", () => { ) }) + it("fromInputUnsafe normalizes infinite nanosecond totals", () => { + deepStrictEqual(Duration.fromInputUnsafe([1e300, 1]), Duration.infinity) + deepStrictEqual(Duration.fromInputUnsafe({ seconds: -Infinity, nanoseconds: 1 }), Duration.negativeInfinity) + }) + it("fromInput", () => { const millis100 = Duration.millis(100) assertSome(Duration.fromInput(millis100), millis100) @@ -240,6 +245,7 @@ describe("Duration", () => { assertFalse(Equal.equals(Duration.infinity, Duration.negativeInfinity)) assertTrue(HashSet.has(HashSet.make(millisTagged), nanosTagged)) + assertTrue(Equal.equals(Duration.millis(2 ** 1023), Duration.nanos(1_000_000n << 1023n))) }) it("Hash.symbol handles finite millis too large to convert to nanos", () => { @@ -345,6 +351,18 @@ describe("Duration", () => { it("times", () => { deepStrictEqual(Duration.times(Duration.seconds(1), 60), Duration.minutes(1)) deepStrictEqual(Duration.times(Duration.nanos(2n), 10), Duration.nanos(20n)) + deepStrictEqual(Duration.times(Duration.nanos(3n), 1.5), Duration.nanos(5n)) + deepStrictEqual( + Duration.times(Duration.nanos(5_000_000n), 0.0000003), + Duration.times(Duration.millis(5), 0.0000003) + ) + deepStrictEqual(Duration.times(Duration.nanos(3n), -0.5), Duration.nanos(-2n)) + deepStrictEqual(Duration.times(Duration.nanos(9007199254740993n), 0.5), Duration.nanos(4503599627370497n)) + deepStrictEqual(Duration.times(Duration.nanos(9007199254740991n), 1.5), Duration.nanos(13510798882111487n)) + deepStrictEqual(Duration.times(Duration.nanos(3002399751580334n), 1.5), Duration.nanos(4503599627370501n)) + deepStrictEqual(Duration.times(Duration.nanos(10n ** 400n), 0.5), Duration.nanos(10n ** 400n / 2n)) + deepStrictEqual(Duration.times(Duration.nanos(1n), Infinity), Duration.infinity) + deepStrictEqual(Duration.times(Duration.nanos(1n), NaN), Duration.zero) deepStrictEqual(Duration.times(Duration.infinity, 60), Duration.infinity) }) @@ -547,6 +565,8 @@ describe("Duration", () => { strictEqual(Duration.millis(0.0000015).pipe(Duration.toNanosUnsafe), 2n) strictEqual(Duration.millis(-0.0000015).pipe(Duration.toNanosUnsafe), -2n) strictEqual(Duration.millis(100).pipe(Duration.toNanosUnsafe), 100_000_000n) + strictEqual(Duration.millis(2 ** 1023).pipe(Duration.toNanosUnsafe), 1_000_000n << 1023n) + strictEqual(Duration.millis(1_760_450_000_123).pipe(Duration.toNanosUnsafe), 1_760_450_000_123_000_000n) }) it("toHrTime", () => { diff --git a/repos/effect/packages/effect/test/Effect.test.ts b/packages/effect/test/Effect.test.ts similarity index 96% rename from repos/effect/packages/effect/test/Effect.test.ts rename to packages/effect/test/Effect.test.ts index e10953d51..f9b6e3137 100644 --- a/repos/effect/packages/effect/test/Effect.test.ts +++ b/packages/effect/test/Effect.test.ts @@ -2,15 +2,18 @@ import { assert, describe, it, vi } from "@effect/vitest" import { assertExitFailure } from "@effect/vitest/utils" import { Cause, + Clock, Context, Data, Deferred, Duration, Effect, Effectable, + Equal, Exit, Fiber, type Filter, + Hash, Latch, Layer, Logger, @@ -2148,6 +2151,74 @@ describe("Effect", () => { }) describe("interruption", () => { + it("interrupting a fiber does not call Math.random", async () => { + // The bug made the runtime retry forever while Math.random kept throwing, + // so stop throwing after a while to keep a regression bounded. + let calls = 0 + const random = Math.random + const spy = vi.spyOn(Math, "random").mockImplementation(() => { + calls++ + if (calls > 1_000) return random() + throw new Error("Math.random is not allowed") + }) + // Fresh objects, so no earlier test has cached a random reference hash. + class DefectError extends Error { + override toString() { + return "DefectError" + } + } + try { + const exit = await Effect.runPromise(Effect.gen(function*() { + const latch = yield* Latch.make() + const child = yield* latch.await.pipe( + Effect.andThen(Effect.die(new DefectError())), + Effect.uninterruptible, + Effect.forkChild({ startImmediately: true }) + ) + // The child resumes after these synchronous interrupts. + yield* latch.open + child.interruptUnsafe(undefined, Context.make(ATag, "A")) + child.interruptUnsafe(undefined, Context.make(ATag, "A")) + return yield* Fiber.await(child) + })) + assert.strictEqual(calls, 0) + assert.isTrue(Exit.isFailure(exit) && Cause.hasDies(exit.cause) && Cause.hasInterrupts(exit.cause)) + } finally { + spy.mockRestore() + } + }) + + it.effect("a defect that throws when hashed still terminates an interrupted fiber", () => + Effect.gen(function*() { + // Throw a bounded number of times so a retrying runtime still finishes. + const limit = 1_000 + let hashCalls = 0 + const defect = { + [Hash.symbol]() { + if (++hashCalls <= limit) throw defect + return 0 + }, + [Equal.symbol](that: unknown) { + return that === defect + } + } + let finalized = 0 + const child = yield* Effect.never.pipe( + Effect.onInterrupt(() => Effect.die(defect)), + Effect.ensuring(Effect.sync(() => finalized++)), + Effect.forkChild + ) + yield* Effect.yieldNow + const exit = yield* Fiber.interrupt(child).pipe(Effect.andThen(Fiber.await(child))) + assert.isBelow(hashCalls, limit) + assert.strictEqual(finalized, 1) + assert.isTrue( + Exit.isFailure(exit) && + exit.cause.reasons.some((reason) => reason._tag === "Die" && reason.defect === defect) && + Cause.hasInterrupts(exit.cause) + ) + })) + it.effect("skipped handlers drop failures and keep defects when interrupted", () => Effect.gen(function*() { const latch = yield* Latch.make() @@ -4170,6 +4241,112 @@ describe("Effect", () => { assert.deepStrictEqual(negativeZero.pollUnsafe(), Exit.succeed(-Infinity), "-0 over 0 never woke its waiter") })) }) + + describe("transaction metadata", () => { + it.effect("starts with zero durations when the clock advances between reads", () => + Effect.gen(function*() { + const clock = yield* Clock.Clock + let now = 1000 + const currentTimeMillisUnsafe = () => now++ + yield* Effect.tx(Effect.gen(function*() { + const metadata = yield* Effect.Transaction + assert.deepStrictEqual([metadata.elapsed, metadata.elapsedSincePrevious], [0, 0]) + assert.strictEqual(metadata.start, metadata.now) + })).pipe(Effect.provideService(Clock.Clock, { + ...clock, + currentTimeMillisUnsafe, + currentTimeMillis: Effect.sync(currentTimeMillisUnsafe) + })) + })) + + it.effect("tracks nested metadata across retries and conflicts", () => + Effect.gen(function*() { + const ref = TxRef.makeUnsafe(0) + const started = yield* Deferred.make() + const readyForConflict = yield* Deferred.make() + const resume = yield* Deferred.make() + const snapshots = yield* Ref.make< + Array<{ + readonly attempt: number + readonly retryReason: Effect.TransactionRetryReason | undefined + readonly start: number + readonly now: number + readonly elapsed: number + readonly elapsedSincePrevious: number + readonly nestedSame: boolean + }> + >([]) + + const fiber = yield* Effect.forkChild(Effect.tx(Effect.gen(function*() { + const outer = yield* Effect.Transaction + const nestedSame = yield* Effect.tx(Effect.gen(function*() { + const inner = yield* Effect.Transaction + return inner === outer + })) + yield* Ref.update(snapshots, (current) => [ + ...current, + { + attempt: outer.attempt, + retryReason: outer.retryReason, + start: outer.start, + now: outer.now, + elapsed: outer.elapsed, + elapsedSincePrevious: outer.elapsedSincePrevious, + nestedSame + } + ]) + yield* TxRef.get(ref) + if (outer.attempt === 1) { + yield* Deferred.succeed(started, undefined) + return yield* Effect.txRetry + } + if (outer.attempt === 2) { + yield* Deferred.succeed(readyForConflict, undefined) + yield* Deferred.await(resume) + } + }))) + + yield* Deferred.await(started) + yield* TestClock.adjust("1 second") + yield* TxRef.set(ref, 1) + yield* Deferred.await(readyForConflict) + yield* TestClock.adjust("500 millis") + yield* TxRef.set(ref, 2) + yield* Deferred.succeed(resume, undefined) + yield* Fiber.join(fiber) + const recorded = yield* Ref.get(snapshots) + + assert.deepStrictEqual(recorded, [ + { + attempt: 1, + retryReason: undefined, + start: 0, + now: 0, + elapsed: 0, + elapsedSincePrevious: 0, + nestedSame: true + }, + { + attempt: 2, + retryReason: "retry", + start: 0, + now: 1000, + elapsed: 1000, + elapsedSincePrevious: 1000, + nestedSame: true + }, + { + attempt: 3, + retryReason: "conflict", + start: 0, + now: 1500, + elapsed: 1500, + elapsedSincePrevious: 500, + nestedSame: true + } + ]) + })) + }) }) describe("Effect.fn", () => { @@ -4471,6 +4648,25 @@ describe("Effect", () => { assert.deepStrictEqual(yield* Fiber.awaitAll(fibers), [Exit.succeed(1), Exit.succeed(1), Exit.succeed(1)]) })) + it.effect("shares the in-flight run when the body synchronously wakes a re-entrant caller", () => + Effect.gen(function*() { + const gate = yield* Deferred.make() + const cached = yield* Effect.cached( + Effect.gen(function*() { + yield* Deferred.succeed(gate, void 0) + return 42 + }) + ) + + const waiter = yield* Deferred.await(gate).pipe( + Effect.andThen(cached), + Effect.forkChild({ startImmediately: true }) + ) + + assert.strictEqual(yield* cached, 42) + assert.strictEqual(yield* Fiber.join(waiter), 42) + })) + it.effect("replays failures", () => Effect.gen(function*() { let count = 0 diff --git a/repos/effect/packages/effect/test/EffectEager.test.ts b/packages/effect/test/EffectEager.test.ts similarity index 100% rename from repos/effect/packages/effect/test/EffectEager.test.ts rename to packages/effect/test/EffectEager.test.ts diff --git a/repos/effect/packages/effect/test/EffectKeepAlive.test.ts b/packages/effect/test/EffectKeepAlive.test.ts similarity index 100% rename from repos/effect/packages/effect/test/EffectKeepAlive.test.ts rename to packages/effect/test/EffectKeepAlive.test.ts diff --git a/repos/effect/packages/effect/test/Effectable.test.ts b/packages/effect/test/Effectable.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Effectable.test.ts rename to packages/effect/test/Effectable.test.ts diff --git a/repos/effect/packages/effect/test/Equal.test.ts b/packages/effect/test/Equal.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Equal.test.ts rename to packages/effect/test/Equal.test.ts diff --git a/repos/effect/packages/effect/test/Equivalence.test.ts b/packages/effect/test/Equivalence.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Equivalence.test.ts rename to packages/effect/test/Equivalence.test.ts diff --git a/repos/effect/packages/effect/test/ExecutionPlan.test.ts b/packages/effect/test/ExecutionPlan.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ExecutionPlan.test.ts rename to packages/effect/test/ExecutionPlan.test.ts diff --git a/repos/effect/packages/effect/test/Exit.test.ts b/packages/effect/test/Exit.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Exit.test.ts rename to packages/effect/test/Exit.test.ts diff --git a/repos/effect/packages/effect/test/Fiber.test.ts b/packages/effect/test/Fiber.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Fiber.test.ts rename to packages/effect/test/Fiber.test.ts diff --git a/repos/effect/packages/effect/test/FiberHandle.test.ts b/packages/effect/test/FiberHandle.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberHandle.test.ts rename to packages/effect/test/FiberHandle.test.ts diff --git a/repos/effect/packages/effect/test/FiberMap.test.ts b/packages/effect/test/FiberMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberMap.test.ts rename to packages/effect/test/FiberMap.test.ts diff --git a/repos/effect/packages/effect/test/FiberSet.test.ts b/packages/effect/test/FiberSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberSet.test.ts rename to packages/effect/test/FiberSet.test.ts diff --git a/repos/effect/packages/effect/test/FiberSetRuntimePropagation.test.ts b/packages/effect/test/FiberSetRuntimePropagation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/FiberSetRuntimePropagation.test.ts rename to packages/effect/test/FiberSetRuntimePropagation.test.ts diff --git a/repos/effect/packages/effect/test/FileSystem.test-utils.ts b/packages/effect/test/FileSystem.test-utils.ts similarity index 88% rename from repos/effect/packages/effect/test/FileSystem.test-utils.ts rename to packages/effect/test/FileSystem.test-utils.ts index 19fa02fbc..95931d2a2 100644 --- a/repos/effect/packages/effect/test/FileSystem.test-utils.ts +++ b/packages/effect/test/FileSystem.test-utils.ts @@ -10,6 +10,8 @@ export interface TestLayerOptions { readonly accessOnDirectory?: boolean /** Whether a scoped temporary file removes its containing directory. Deno removes only the file. Defaults to `true`. */ readonly tempFileScopedRemovesDirectory?: boolean + /** Whether `open` can refuse to follow a symlink at the final path component. Defaults to `true`. */ + readonly noFollow?: boolean } export const testLayer = (layer: Layer.Layer, options: TestLayerOptions = {}) => { @@ -562,4 +564,65 @@ export const testLayer = (layer: Layer.Layer, options: Test Effect.scoped ) }))) + + it("noFollow rejects symlinks or fails explicitly when unsupported", () => + runPromise( + Effect.gen(function*() { + const fs = yield* Fs.FileSystem + const directory = yield* fs.makeTempDirectoryScoped() + const path = `${directory}/target` + yield* fs.writeFileString(path, "target") + if (options.noFollow !== false) { + yield* fs.open(path, { noFollow: true }) + yield* fs.symlink(path, `${directory}/link`) + } + const result = yield* Effect.result(fs.open( + options.noFollow === false ? path : `${directory}/link`, + { noFollow: true } + )) + expect(result).toMatchObject({ + _tag: "Failure", + failure: { reason: { _tag: options.noFollow === false ? "BadArgument" : "BadResource" } } + }) + }).pipe(Effect.scoped) + )) + + it("concurrent positional reads preserve the cursor", () => + runPromise( + Effect.gen(function*() { + const fs = yield* Fs.FileSystem + const file = yield* fs.open(`${__dirname}/fixtures/text.txt`) + yield* file.seek(BigInt(6), "start") + const buffer = new Uint8Array(5) + const [count, allocated] = yield* Effect.all([ + file.read(buffer, { position: BigInt(12) }), + file.readAlloc(4, { position: BigInt(18) }).pipe(Effect.flatMap(Effect.fromOption)) + ], { concurrency: "unbounded" }) + assert.strictEqual(count, 5) + assert.strictEqual(new TextDecoder().decode(buffer), "dolar") + assert.strictEqual(new TextDecoder().decode(allocated), "sit ") + const next = yield* file.readAlloc(5).pipe(Effect.flatMap(Effect.fromOption)) + assert.strictEqual(new TextDecoder().decode(next), "ipsum") + }).pipe(Effect.scoped) + )) + + it.each(["read", "readAlloc"] as const)( + "%s rejects a negative position", + (method) => + runPromise( + Effect.gen(function*() { + const fs = yield* Fs.FileSystem + const file = yield* fs.open(`${__dirname}/fixtures/text.txt`) + const result = yield* Effect.result( + method === "read" + ? Effect.asVoid(file.read(new Uint8Array(1), { position: BigInt(-1) })) + : Effect.asVoid(file.readAlloc(1, { position: BigInt(-1) })) + ) + expect(result).toMatchObject({ + _tag: "Failure", + failure: { reason: { _tag: "BadArgument", method } } + }) + }).pipe(Effect.scoped) + ) + ) } diff --git a/repos/effect/packages/effect/test/Formatter.test.ts b/packages/effect/test/Formatter.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Formatter.test.ts rename to packages/effect/test/Formatter.test.ts diff --git a/repos/effect/packages/effect/test/Function.test.ts b/packages/effect/test/Function.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Function.test.ts rename to packages/effect/test/Function.test.ts diff --git a/repos/effect/packages/effect/test/Graph.test.ts b/packages/effect/test/Graph.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Graph.test.ts rename to packages/effect/test/Graph.test.ts diff --git a/repos/effect/packages/effect/test/Hash.test.ts b/packages/effect/test/Hash.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Hash.test.ts rename to packages/effect/test/Hash.test.ts diff --git a/repos/effect/packages/effect/test/HashMap.test.ts b/packages/effect/test/HashMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HashMap.test.ts rename to packages/effect/test/HashMap.test.ts diff --git a/repos/effect/packages/effect/test/HashRing.test.ts b/packages/effect/test/HashRing.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HashRing.test.ts rename to packages/effect/test/HashRing.test.ts diff --git a/repos/effect/packages/effect/test/HashSet.test.ts b/packages/effect/test/HashSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HashSet.test.ts rename to packages/effect/test/HashSet.test.ts diff --git a/repos/effect/packages/effect/test/HttpClient.test.ts b/packages/effect/test/HttpClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/HttpClient.test.ts rename to packages/effect/test/HttpClient.test.ts diff --git a/repos/effect/packages/effect/test/Iterable.test.ts b/packages/effect/test/Iterable.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Iterable.test.ts rename to packages/effect/test/Iterable.test.ts diff --git a/repos/effect/packages/effect/test/JsonPatch.test.ts b/packages/effect/test/JsonPatch.test.ts similarity index 100% rename from repos/effect/packages/effect/test/JsonPatch.test.ts rename to packages/effect/test/JsonPatch.test.ts diff --git a/repos/effect/packages/effect/test/JsonPointer.test.ts b/packages/effect/test/JsonPointer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/JsonPointer.test.ts rename to packages/effect/test/JsonPointer.test.ts diff --git a/repos/effect/packages/effect/test/JsonSchema.test.ts b/packages/effect/test/JsonSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/JsonSchema.test.ts rename to packages/effect/test/JsonSchema.test.ts diff --git a/repos/effect/packages/effect/test/Latch.test.ts b/packages/effect/test/Latch.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Latch.test.ts rename to packages/effect/test/Latch.test.ts diff --git a/repos/effect/packages/effect/test/Layer.test.ts b/packages/effect/test/Layer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Layer.test.ts rename to packages/effect/test/Layer.test.ts diff --git a/repos/effect/packages/effect/test/LayerMap.test.ts b/packages/effect/test/LayerMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/LayerMap.test.ts rename to packages/effect/test/LayerMap.test.ts diff --git a/repos/effect/packages/effect/test/LayerRef.test.ts b/packages/effect/test/LayerRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/LayerRef.test.ts rename to packages/effect/test/LayerRef.test.ts diff --git a/repos/effect/packages/effect/test/LogLevel.test.ts b/packages/effect/test/LogLevel.test.ts similarity index 100% rename from repos/effect/packages/effect/test/LogLevel.test.ts rename to packages/effect/test/LogLevel.test.ts diff --git a/repos/effect/packages/effect/test/Logger.test.ts b/packages/effect/test/Logger.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Logger.test.ts rename to packages/effect/test/Logger.test.ts diff --git a/repos/effect/packages/effect/test/ManagedRuntime.test.ts b/packages/effect/test/ManagedRuntime.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ManagedRuntime.test.ts rename to packages/effect/test/ManagedRuntime.test.ts diff --git a/repos/effect/packages/effect/test/Match.test.ts b/packages/effect/test/Match.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Match.test.ts rename to packages/effect/test/Match.test.ts diff --git a/repos/effect/packages/effect/test/Metric.test.ts b/packages/effect/test/Metric.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Metric.test.ts rename to packages/effect/test/Metric.test.ts diff --git a/repos/effect/packages/effect/test/Migrator.test.ts b/packages/effect/test/Migrator.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Migrator.test.ts rename to packages/effect/test/Migrator.test.ts diff --git a/repos/effect/packages/effect/test/MutableHashMap.test.ts b/packages/effect/test/MutableHashMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/MutableHashMap.test.ts rename to packages/effect/test/MutableHashMap.test.ts diff --git a/repos/effect/packages/effect/test/MutableHashSet.test.ts b/packages/effect/test/MutableHashSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/MutableHashSet.test.ts rename to packages/effect/test/MutableHashSet.test.ts diff --git a/repos/effect/packages/effect/test/MutableList.test.ts b/packages/effect/test/MutableList.test.ts similarity index 100% rename from repos/effect/packages/effect/test/MutableList.test.ts rename to packages/effect/test/MutableList.test.ts diff --git a/repos/effect/packages/effect/test/Newtype.test.ts b/packages/effect/test/Newtype.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Newtype.test.ts rename to packages/effect/test/Newtype.test.ts diff --git a/repos/effect/packages/effect/test/Number.test.ts b/packages/effect/test/Number.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Number.test.ts rename to packages/effect/test/Number.test.ts diff --git a/repos/effect/packages/effect/test/Optic.test.ts b/packages/effect/test/Optic.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Optic.test.ts rename to packages/effect/test/Optic.test.ts diff --git a/repos/effect/packages/effect/test/Option.test.ts b/packages/effect/test/Option.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Option.test.ts rename to packages/effect/test/Option.test.ts diff --git a/repos/effect/packages/effect/test/Order.test.ts b/packages/effect/test/Order.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Order.test.ts rename to packages/effect/test/Order.test.ts diff --git a/repos/effect/packages/effect/test/Ordering.test.ts b/packages/effect/test/Ordering.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Ordering.test.ts rename to packages/effect/test/Ordering.test.ts diff --git a/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts b/packages/effect/test/PartitionedSemaphore.test.ts similarity index 100% rename from repos/effect/packages/effect/test/PartitionedSemaphore.test.ts rename to packages/effect/test/PartitionedSemaphore.test.ts diff --git a/packages/effect/test/Path.test.ts b/packages/effect/test/Path.test.ts new file mode 100644 index 000000000..18c0b9c73 --- /dev/null +++ b/packages/effect/test/Path.test.ts @@ -0,0 +1,22 @@ +import { describe, it } from "@effect/vitest" +import { assertNone, assertSome } from "@effect/vitest/utils" +import * as Effect from "effect/Effect" +import * as Path from "effect/Path" + +describe("Path", () => { + it.effect("resolveWithin keeps POSIX paths lexically inside the root", () => + Effect.gen(function*() { + const path = yield* Path.Path + + assertSome(path.resolveWithin("/srv/app", "src/index.ts"), "/srv/app/src/index.ts") + assertSome(path.resolveWithin("/srv/app/", "src/index.ts"), "/srv/app/src/index.ts") + assertSome(path.resolveWithin("/srv/app", "."), "/srv/app") + assertSome(path.resolveWithin("/srv/app", ""), "/srv/app") + assertSome(path.resolveWithin("/srv/app", "..foo"), "/srv/app/..foo") + assertSome(path.resolveWithin("/", "etc/passwd"), "/etc/passwd") + + assertNone(path.resolveWithin("/srv/app", "../app-other/x.ts")) + assertNone(path.resolveWithin("/srv/app", "a/../../b")) + assertNone(path.resolveWithin("/srv/app", "/etc/passwd")) + }).pipe(Effect.provide(Path.layer))) +}) diff --git a/repos/effect/packages/effect/test/Pathfinding.test.ts b/packages/effect/test/Pathfinding.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Pathfinding.test.ts rename to packages/effect/test/Pathfinding.test.ts diff --git a/repos/effect/packages/effect/test/Pool.test.ts b/packages/effect/test/Pool.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Pool.test.ts rename to packages/effect/test/Pool.test.ts diff --git a/repos/effect/packages/effect/test/Predicate.test.ts b/packages/effect/test/Predicate.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Predicate.test.ts rename to packages/effect/test/Predicate.test.ts diff --git a/repos/effect/packages/effect/test/PubSub.test.ts b/packages/effect/test/PubSub.test.ts similarity index 100% rename from repos/effect/packages/effect/test/PubSub.test.ts rename to packages/effect/test/PubSub.test.ts diff --git a/repos/effect/packages/effect/test/Pull.test.ts b/packages/effect/test/Pull.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Pull.test.ts rename to packages/effect/test/Pull.test.ts diff --git a/repos/effect/packages/effect/test/Queue.test.ts b/packages/effect/test/Queue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Queue.test.ts rename to packages/effect/test/Queue.test.ts diff --git a/repos/effect/packages/effect/test/Random.test.ts b/packages/effect/test/Random.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Random.test.ts rename to packages/effect/test/Random.test.ts diff --git a/repos/effect/packages/effect/test/RcMap.test.ts b/packages/effect/test/RcMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/RcMap.test.ts rename to packages/effect/test/RcMap.test.ts diff --git a/repos/effect/packages/effect/test/RcRef.test.ts b/packages/effect/test/RcRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/RcRef.test.ts rename to packages/effect/test/RcRef.test.ts diff --git a/repos/effect/packages/effect/test/Record.test.ts b/packages/effect/test/Record.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Record.test.ts rename to packages/effect/test/Record.test.ts diff --git a/repos/effect/packages/effect/test/Redacted.test.ts b/packages/effect/test/Redacted.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Redacted.test.ts rename to packages/effect/test/Redacted.test.ts diff --git a/repos/effect/packages/effect/test/Reducer.test.ts b/packages/effect/test/Reducer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Reducer.test.ts rename to packages/effect/test/Reducer.test.ts diff --git a/repos/effect/packages/effect/test/Ref.test.ts b/packages/effect/test/Ref.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Ref.test.ts rename to packages/effect/test/Ref.test.ts diff --git a/repos/effect/packages/effect/test/Request.test.ts b/packages/effect/test/Request.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Request.test.ts rename to packages/effect/test/Request.test.ts diff --git a/packages/effect/test/RequestResolver.test.ts b/packages/effect/test/RequestResolver.test.ts new file mode 100644 index 000000000..0b89aa1a0 --- /dev/null +++ b/packages/effect/test/RequestResolver.test.ts @@ -0,0 +1,63 @@ +import { assert, describe, it } from "@effect/vitest" +import { Data, Deferred, Effect, Exit, Fiber, Request, RequestResolver } from "effect" + +class Key extends Data.Class<{ id: number }> {} +class GetValue extends Request.Class<{ key: Key }, number> {} + +describe("RequestResolver.makeGrouped", () => { + it.effect("keeps equal keys batched across derived resolvers and releases them before the next window", () => + Effect.gen(function*() { + const gate = yield* Deferred.make() + const batches: Array<{ key: Key; size: number }> = [] + const resolver = RequestResolver.makeGrouped({ + key: ({ request }) => request.key, + resolver: (entries, key) => + Effect.sync(() => { + batches.push({ key, size: entries.length }) + for (const entry of entries) entry.completeUnsafe(Exit.succeed(key.id)) + }) + }).pipe(RequestResolver.setDelayEffect(Effect.andThen(Effect.yieldNow, Deferred.await(gate)))) + const first = yield* Effect.request(new GetValue({ key: new Key({ id: 1 }) }), resolver).pipe( + Effect.forkChild({ startImmediately: true }) + ) + // Running a derived resolver must not split the delayed resolver's pending batch. + yield* Effect.request(new GetValue({ key: new Key({ id: 1 }) }), RequestResolver.batchN(resolver, 1)) + const rest = yield* Effect.forEach( + [1, 2], + (id) => + Effect.request(new GetValue({ key: new Key({ id }) }), resolver).pipe( + Effect.forkChild({ startImmediately: true }) + ) + ) + yield* Deferred.succeed(gate, undefined) + const values = yield* Effect.forEach([first, ...rest], Fiber.join) + assert.deepStrictEqual(values, [1, 1, 2]) + assert.deepStrictEqual(batches.map(({ size }) => size), [1, 2, 1]) + + const next = new Key({ id: 1 }) + yield* Effect.request(new GetValue({ key: next }), resolver) + assert.strictEqual(batches[3].key, next) + })) + + it.effect("releases the key when the last pending request is interrupted", () => + Effect.gen(function*() { + const keys: Array = [] + const resolver = RequestResolver.makeGrouped({ + key: ({ request }) => request.key, + resolver: (entries, key) => + Effect.sync(() => { + keys.push(key) + for (const entry of entries) entry.completeUnsafe(Exit.succeed(key.id)) + }) + }) + const fiber = yield* Effect.request(new GetValue({ key: new Key({ id: 1 }) }), resolver).pipe( + Effect.forkChild({ startImmediately: true }) + ) + yield* Fiber.interrupt(fiber) + const next = new Key({ id: 1 }) + yield* Effect.request(new GetValue({ key: next }), resolver) + + assert.strictEqual(keys.length, 1) + assert.strictEqual(keys[0], next) + })) +}) diff --git a/repos/effect/packages/effect/test/Resource.test.ts b/packages/effect/test/Resource.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Resource.test.ts rename to packages/effect/test/Resource.test.ts diff --git a/repos/effect/packages/effect/test/Result.test.ts b/packages/effect/test/Result.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Result.test.ts rename to packages/effect/test/Result.test.ts diff --git a/repos/effect/packages/effect/test/Schedule.test.ts b/packages/effect/test/Schedule.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Schedule.test.ts rename to packages/effect/test/Schedule.test.ts diff --git a/repos/effect/packages/effect/test/Scheduler.test.ts b/packages/effect/test/Scheduler.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Scheduler.test.ts rename to packages/effect/test/Scheduler.test.ts diff --git a/repos/effect/packages/effect/test/Scope.test.ts b/packages/effect/test/Scope.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Scope.test.ts rename to packages/effect/test/Scope.test.ts diff --git a/repos/effect/packages/effect/test/ScopedCache.test.ts b/packages/effect/test/ScopedCache.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ScopedCache.test.ts rename to packages/effect/test/ScopedCache.test.ts diff --git a/repos/effect/packages/effect/test/ScopedRef.test.ts b/packages/effect/test/ScopedRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ScopedRef.test.ts rename to packages/effect/test/ScopedRef.test.ts diff --git a/repos/effect/packages/effect/test/Semaphore.test.ts b/packages/effect/test/Semaphore.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Semaphore.test.ts rename to packages/effect/test/Semaphore.test.ts diff --git a/repos/effect/packages/effect/test/Sink.test.ts b/packages/effect/test/Sink.test.ts similarity index 91% rename from repos/effect/packages/effect/test/Sink.test.ts rename to packages/effect/test/Sink.test.ts index 39c9e1a24..86f600e4d 100644 --- a/repos/effect/packages/effect/test/Sink.test.ts +++ b/packages/effect/test/Sink.test.ts @@ -303,6 +303,40 @@ describe("Sink", () => { })) }) + describe("takeUntil", () => { + it.effect("takeUntil preserves leftovers and stops at the match", () => + Effect.gen(function*() { + const result = yield* Stream.make(1, 2, 3, 4).pipe( + Stream.concat(Stream.fail("pulled past the match")), + Stream.transduce(Sink.takeUntil((n) => n % 2 === 0)), + Stream.take(2), + Stream.runCollect + ) + deepStrictEqual(result, [[1, 2], [3, 4]]) + })) + + it.effect("takeUntilEffect preserves leftovers and stops at the match", () => + Effect.gen(function*() { + for (const chunkSize of [1, 4]) { + const inputs: Array = [] + const result = yield* Stream.make(1, 2, 3, 4).pipe( + Stream.rechunk(chunkSize), + Stream.concat(Stream.fail("pulled past the match")), + Stream.transduce(Sink.takeUntilEffect((n) => + Effect.sync(() => { + inputs.push(n) + return n % 2 === 0 + }) + )), + Stream.take(2), + Stream.runCollect + ) + deepStrictEqual(result, [[1, 2], [3, 4]]) + deepStrictEqual(inputs, [1, 2, 3, 4]) + } + })) + }) + describe("withDuration", () => { it.effect("uses monotonic time when wall time moves backward", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/StackCapture.test.ts b/packages/effect/test/StackCapture.test.ts similarity index 50% rename from repos/effect/packages/effect/test/StackCapture.test.ts rename to packages/effect/test/StackCapture.test.ts index 24244827e..26a86aebf 100644 --- a/repos/effect/packages/effect/test/StackCapture.test.ts +++ b/packages/effect/test/StackCapture.test.ts @@ -1,5 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Effect, References } from "effect" +import { vi } from "vitest" const withCaptureDisabled = (run: () => A): A => { const limit = Error.stackTraceLimit @@ -30,6 +31,61 @@ const countErrors = (run: () => void): number => { } describe("stack capture", { concurrent: false }, () => { + it("module loading bounds probes and restores the stack trace limit", async () => { + const original = globalThis.Error + const previous = original.stackTraceLimit + const limits: Array = [] + vi.resetModules() + original.stackTraceLimit = 10 + globalThis.Error = new Proxy(original, { + construct(target, args, newTarget) { + limits.push(target.stackTraceLimit) + return Reflect.construct(target, args, newTarget) + } + }) + try { + await import("effect/Utils") + assert.deepStrictEqual(limits.splice(0), [2]) + assert.strictEqual(original.stackTraceLimit, 10) + await import("effect/internal/effect") + assert.deepStrictEqual(limits, [1]) + assert.strictEqual(original.stackTraceLimit, 10) + } finally { + globalThis.Error = original + original.stackTraceLimit = previous + vi.resetModules() + } + }) + + it("module loading skips stack formatting at limit zero", async () => { + const original = globalThis.Error + const limit = original.stackTraceLimit + let stackReads = 0 + vi.resetModules() + original.stackTraceLimit = 0 + globalThis.Error = new Proxy(original, { + construct(target, args, newTarget) { + return new Proxy(Reflect.construct(target, args, newTarget), { + get(target, key, receiver) { + if (key === "stack") { + stackReads++ + } + return Reflect.get(target, key, receiver) + } + }) + } + }) + try { + // A dynamic import after clearing the cache exercises the load-time probe. + await import("effect/internal/effect") + } finally { + globalThis.Error = original + original.stackTraceLimit = limit + vi.resetModules() + } + assert.strictEqual(stackReads, 0) + }) + it("Effect.fn skips definition capture at limit zero", () => { const count = withCaptureDisabled(() => countErrors(() => Effect.fn("test")(() => Effect.void))) assert.strictEqual(count, 0) diff --git a/repos/effect/packages/effect/test/StackTraceLimit.test.ts b/packages/effect/test/StackTraceLimit.test.ts similarity index 100% rename from repos/effect/packages/effect/test/StackTraceLimit.test.ts rename to packages/effect/test/StackTraceLimit.test.ts diff --git a/repos/effect/packages/effect/test/Stdio.test.ts b/packages/effect/test/Stdio.test.ts similarity index 54% rename from repos/effect/packages/effect/test/Stdio.test.ts rename to packages/effect/test/Stdio.test.ts index bfb56fb18..ebbbbc2fe 100644 --- a/repos/effect/packages/effect/test/Stdio.test.ts +++ b/packages/effect/test/Stdio.test.ts @@ -1,13 +1,28 @@ import { assert, describe, it } from "@effect/vitest" import * as Effect from "effect/Effect" +import * as Sink from "effect/Sink" import * as Stdio from "effect/Stdio" +import * as Stream from "effect/Stream" describe("Stdio", () => { + it.effect("make defaults stderr terminal state to false", () => + Effect.gen(function*() { + const stdio = Stdio.make({ + args: Effect.succeed([]), + stdin: Stream.empty, + stdout: () => Sink.drain, + stderr: () => Sink.drain, + stdoutIsTerminal: Effect.succeed(true) + }) + assert.isFalse(yield* stdio.stderrIsTerminal) + })) + it.effect("layerTest defaults terminal state to false", () => Effect.gen(function*() { const stdio = yield* Stdio.Stdio assert.isFalse(yield* stdio.stdinIsTerminal) assert.isFalse(yield* stdio.stdoutIsTerminal) + assert.isFalse(yield* stdio.stderrIsTerminal) }).pipe(Effect.provide(Stdio.layerTest({})))) it.effect("layerTest allows terminal state overrides", () => @@ -15,8 +30,10 @@ describe("Stdio", () => { const stdio = yield* Stdio.Stdio assert.isTrue(yield* stdio.stdinIsTerminal) assert.isTrue(yield* stdio.stdoutIsTerminal) + assert.isTrue(yield* stdio.stderrIsTerminal) }).pipe(Effect.provide(Stdio.layerTest({ stdinIsTerminal: Effect.succeed(true), - stdoutIsTerminal: Effect.succeed(true) + stdoutIsTerminal: Effect.succeed(true), + stderrIsTerminal: Effect.succeed(true) })))) }) diff --git a/repos/effect/packages/effect/test/Stream.test.ts b/packages/effect/test/Stream.test.ts similarity index 99% rename from repos/effect/packages/effect/test/Stream.test.ts rename to packages/effect/test/Stream.test.ts index dc4b64634..84cd7f48a 100644 --- a/repos/effect/packages/effect/test/Stream.test.ts +++ b/packages/effect/test/Stream.test.ts @@ -5514,6 +5514,25 @@ describe("Stream", () => { deepStrictEqual(result, [4]) })) + it.live.each(["timeout", "timeoutOrElse"] as const)( + "%s finishes when an async iterator pull never settles", + (kind) => + Effect.gen(function*() { + async function* source() { + yield "a" + await new Promise(() => undefined) + } + const result = yield* Stream.fromAsyncIterable(source(), (error) => error).pipe( + kind === "timeout" ? Stream.timeout("10 millis") : Stream.timeoutOrElse({ + duration: "10 millis", + orElse: () => Stream.succeed("fallback") + }), + Stream.runCollect + ) + deepStrictEqual(result, kind === "timeout" ? ["a"] : ["a", "fallback"]) + }) + ) + it.effect("timeoutOrElse - should not apply timeout after switch", () => Effect.gen(function*() { const fiber = yield* pipe( diff --git a/repos/effect/packages/effect/test/String.test.ts b/packages/effect/test/String.test.ts similarity index 96% rename from repos/effect/packages/effect/test/String.test.ts rename to packages/effect/test/String.test.ts index 3ee1777bb..6409aeca6 100644 --- a/repos/effect/packages/effect/test/String.test.ts +++ b/packages/effect/test/String.test.ts @@ -598,6 +598,11 @@ describe("String", () => { it("handles an empty string", () => { strictEqual(S.snakeToCamel(""), "") }) + + it("handles trailing and separator-only inputs", () => { + strictEqual(S.snakeToCamel("foo_"), "foo") + strictEqual(S.snakeToCamel("__"), "") + }) }) describe("snakeToPascal", () => { @@ -609,6 +614,11 @@ describe("String", () => { it("handles an empty string", () => { strictEqual(S.snakeToPascal(""), "") }) + + it("handles trailing and separator-only inputs", () => { + strictEqual(S.snakeToPascal("foo_"), "Foo") + strictEqual(S.snakeToPascal("__"), "") + }) }) describe("snakeToKebab", () => { @@ -674,6 +684,17 @@ describe("String", () => { strictEqual(S.noCase("field2value"), "field 2 value") strictEqual(S.noCase("field2Value"), "field 2 value") }) + + it("does not transform a word for inputs without words", () => { + const parts: Array = [] + const transform = (part: string) => { + parts.push(part) + return part + } + strictEqual(S.noCase("", { transform }), "") + strictEqual(S.noCase("---", { transform }), "") + deepStrictEqual(parts, []) + }) }) describe("pascalCase", () => { diff --git a/repos/effect/packages/effect/test/Struct.test.ts b/packages/effect/test/Struct.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Struct.test.ts rename to packages/effect/test/Struct.test.ts diff --git a/repos/effect/packages/effect/test/SubscriptionRef.test.ts b/packages/effect/test/SubscriptionRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/SubscriptionRef.test.ts rename to packages/effect/test/SubscriptionRef.test.ts diff --git a/repos/effect/packages/effect/test/Symbol.test.ts b/packages/effect/test/Symbol.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Symbol.test.ts rename to packages/effect/test/Symbol.test.ts diff --git a/repos/effect/packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts b/packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts similarity index 100% rename from repos/effect/packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts rename to packages/effect/test/SynchronizedRef.modifySomeEffect.test.ts diff --git a/repos/effect/packages/effect/test/SynchronizedRef.test.ts b/packages/effect/test/SynchronizedRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/SynchronizedRef.test.ts rename to packages/effect/test/SynchronizedRef.test.ts diff --git a/repos/effect/packages/effect/test/TestClock.test.ts b/packages/effect/test/TestClock.test.ts similarity index 63% rename from repos/effect/packages/effect/test/TestClock.test.ts rename to packages/effect/test/TestClock.test.ts index 54fa0ddfb..2f76af33e 100644 --- a/repos/effect/packages/effect/test/TestClock.test.ts +++ b/packages/effect/test/TestClock.test.ts @@ -1,10 +1,41 @@ import { assert, describe, it } from "@effect/vitest" +import * as Clock from "effect/Clock" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" +import * as Fiber from "effect/Fiber" +import * as Scheduler from "effect/Scheduler" import * as TestClock from "effect/testing/TestClock" describe("TestClock", () => { + for (const cancellation of ["interrupted sleep", "successful timeout"] as const) { + it.effect(`adjust - skips the cancelled deadline (${cancellation})`, () => + Effect.gen(function*() { + const testClock = yield* TestClock.make() + if (cancellation === "interrupted sleep") { + const sleeper = yield* testClock.sleep(Duration.minutes(1)).pipe( + Effect.forkChild({ startImmediately: true }) + ) + yield* Effect.yieldNow + yield* Fiber.interrupt(sleeper) + } else { + yield* Effect.timeout(Effect.yieldNow, "1 minute").pipe( + Effect.provideService(Clock.Clock, testClock) + ) + } + + // Record the clock whenever adjustment yields to other fibers + const observed = new Set() + yield* Effect.sync(() => observed.add(testClock.currentTimeMillisUnsafe())).pipe( + Effect.andThen(Effect.yieldNow), + Effect.forever, + Effect.forkChild + ) + yield* testClock.adjust("2 minutes") + assert.isFalse(observed.has(60_000)) + })) + } + it.effect("sleep - does not require passage of wall time", () => Effect.gen(function*() { let elapsed = false @@ -131,6 +162,72 @@ describe("TestClock", () => { assert.strictEqual(testClock.monotonicTimeNanosUnsafe(), 2_000_000_000n) })) + it.effect("adjust - runs sleeps registered after a yield", () => + Effect.gen(function*() { + let ticks = 0 + const fiber = yield* Effect.sleep("1 second").pipe( + Effect.andThen(Effect.sync(() => { + ticks++ + })), + Effect.andThen(Effect.yieldNow), + Effect.forever, + Effect.forkChild + ) + yield* TestClock.adjust("5 seconds") + yield* Fiber.interrupt(fiber) + assert.strictEqual(ticks, 5) + })) + + it.effect("setTime - runs sleeps registered after a fork and join", () => + Effect.gen(function*() { + let ticks = 0 + const fiber = yield* Effect.sleep("1 second").pipe( + Effect.andThen(Effect.sync(() => { + ticks++ + })), + Effect.andThen(Effect.forkChild(Effect.yieldNow).pipe(Effect.flatMap(Fiber.join))), + Effect.forever, + Effect.forkChild + ) + yield* TestClock.setTime(5_000) + yield* Fiber.interrupt(fiber) + assert.strictEqual(ticks, 5) + })) + + it.effect("adjust - completes when a nested clock wakes a busy fiber", () => + Effect.gen(function*() { + let later = false + yield* Effect.sleep("1 second").pipe( + Effect.andThen(Effect.forever(Effect.yieldNow)), + Effect.forkChild + ) + yield* Effect.sleep("2 seconds").pipe( + Effect.andThen(Effect.sync(() => { + later = true + })), + Effect.forkChild + ) + yield* TestClock.adjust("5 seconds") + assert.isTrue(later) + }).pipe(Effect.provide(TestClock.layer({ warningDelay: "10 millis" })))) + + it.effect("adjust - waits for woken fibers when the advancing fiber yields on its operation budget", () => + Effect.gen(function*() { + let done = false + yield* Effect.sleep("1 second").pipe( + Effect.andThen(Effect.yieldNow), + Effect.andThen(Effect.sync(() => { + done = true + })), + Effect.forkChild + ) + yield* TestClock.adjust("1 second") + assert.isTrue(done) + }).pipe( + Effect.provide(TestClock.layer({ warningDelay: Duration.infinity })), + Effect.provideService(Scheduler.MaxOpsBeforeYield, 4) + )) + // `it.effect` and `it.live` provide an ambient Scope, defeating the #2244 regression guard. it("layer - can adjust when provided without an ambient Scope", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/Tracer.test.ts b/packages/effect/test/Tracer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Tracer.test.ts rename to packages/effect/test/Tracer.test.ts diff --git a/repos/effect/packages/effect/test/Trie.test.ts b/packages/effect/test/Trie.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Trie.test.ts rename to packages/effect/test/Trie.test.ts diff --git a/repos/effect/packages/effect/test/Tuple.test.ts b/packages/effect/test/Tuple.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Tuple.test.ts rename to packages/effect/test/Tuple.test.ts diff --git a/repos/effect/packages/effect/test/TxChunk.test.ts b/packages/effect/test/TxChunk.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxChunk.test.ts rename to packages/effect/test/TxChunk.test.ts diff --git a/repos/effect/packages/effect/test/TxDeferred.test.ts b/packages/effect/test/TxDeferred.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxDeferred.test.ts rename to packages/effect/test/TxDeferred.test.ts diff --git a/repos/effect/packages/effect/test/TxHashMap.test.ts b/packages/effect/test/TxHashMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxHashMap.test.ts rename to packages/effect/test/TxHashMap.test.ts diff --git a/repos/effect/packages/effect/test/TxHashSet.test.ts b/packages/effect/test/TxHashSet.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxHashSet.test.ts rename to packages/effect/test/TxHashSet.test.ts diff --git a/repos/effect/packages/effect/test/TxPriorityQueue.test.ts b/packages/effect/test/TxPriorityQueue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxPriorityQueue.test.ts rename to packages/effect/test/TxPriorityQueue.test.ts diff --git a/repos/effect/packages/effect/test/TxPubSub.test.ts b/packages/effect/test/TxPubSub.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxPubSub.test.ts rename to packages/effect/test/TxPubSub.test.ts diff --git a/repos/effect/packages/effect/test/TxQueue.test.ts b/packages/effect/test/TxQueue.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxQueue.test.ts rename to packages/effect/test/TxQueue.test.ts diff --git a/repos/effect/packages/effect/test/TxReentrantLock.test.ts b/packages/effect/test/TxReentrantLock.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxReentrantLock.test.ts rename to packages/effect/test/TxReentrantLock.test.ts diff --git a/repos/effect/packages/effect/test/TxSemaphore.test.ts b/packages/effect/test/TxSemaphore.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxSemaphore.test.ts rename to packages/effect/test/TxSemaphore.test.ts diff --git a/repos/effect/packages/effect/test/TxSubscriptionRef.test.ts b/packages/effect/test/TxSubscriptionRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/TxSubscriptionRef.test.ts rename to packages/effect/test/TxSubscriptionRef.test.ts diff --git a/repos/effect/packages/effect/test/UndefinedOr.test.ts b/packages/effect/test/UndefinedOr.test.ts similarity index 100% rename from repos/effect/packages/effect/test/UndefinedOr.test.ts rename to packages/effect/test/UndefinedOr.test.ts diff --git a/repos/effect/packages/effect/test/Version.test.ts b/packages/effect/test/Version.test.ts similarity index 100% rename from repos/effect/packages/effect/test/Version.test.ts rename to packages/effect/test/Version.test.ts diff --git a/repos/effect/packages/effect/test/ai/AiError.test.ts b/packages/effect/test/ai/AiError.test.ts similarity index 98% rename from repos/effect/packages/effect/test/ai/AiError.test.ts rename to packages/effect/test/ai/AiError.test.ts index 0c63c81eb..dfa3cc54b 100644 --- a/repos/effect/packages/effect/test/ai/AiError.test.ts +++ b/packages/effect/test/ai/AiError.test.ts @@ -1,9 +1,22 @@ import { assert, describe, it } from "@effect/vitest" -import { Duration, Effect, Schema } from "effect" +import { Duration, Effect, Redacted, Schema } from "effect" import { AiError } from "effect/ai" +import { HttpClientError, HttpClientRequest } from "effect/http" describe("AiError", () => { describe("reason types", () => { + describe("NetworkError", () => { + it("preserves redacted query params from request errors", () => { + const request = HttpClientRequest.get("https://example.com").pipe( + HttpClientRequest.setUrlParam("remote", Redacted.make("param-secret")) + ) + const error = AiError.NetworkError.fromRequestError(new HttpClientError.TransportError({ request })) + const output = JSON.stringify(error) + assert.include(output, "remote") + assert.notInclude(output, "param-secret") + }) + }) + describe("RateLimitError", () => { it("should be retryable", () => { const error = new AiError.RateLimitError({}) diff --git a/repos/effect/packages/effect/test/ai/AnthropicStructuredOutput.test.ts b/packages/effect/test/ai/AnthropicStructuredOutput.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/AnthropicStructuredOutput.test.ts rename to packages/effect/test/ai/AnthropicStructuredOutput.test.ts diff --git a/repos/effect/packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts b/packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts rename to packages/effect/test/ai/AnthropicStructuredOutputRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/Chat.test.ts b/packages/effect/test/ai/Chat.test.ts similarity index 88% rename from repos/effect/packages/effect/test/ai/Chat.test.ts rename to packages/effect/test/ai/Chat.test.ts index 516728bc8..9c473eb81 100644 --- a/repos/effect/packages/effect/test/ai/Chat.test.ts +++ b/packages/effect/test/ai/Chat.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from "@effect/vitest" -import { Effect, Layer, Predicate, Ref, Schema } from "effect" +import { Effect, Layer, Predicate, Ref, Schema, Stream } from "effect" import { Chat, IdGenerator, Prompt, Tool, Toolkit } from "effect/ai" import { Persistence } from "effect/persistence" import { TestClock } from "effect/testing" @@ -16,6 +16,34 @@ const PersistenceLayer = Layer.provideMerge( ) describe("Chat", () => { + it.effect("keeps partial assistant text when streamText is interrupted", () => + Effect.gen(function*() { + const chat = yield* Chat.empty + + yield* chat.streamText({ prompt: "hi" }).pipe( + Stream.take(2), + Stream.runDrain, + TestUtils.withLanguageModel({ + streamText: () => + Stream.fromIterable( + [ + { type: "text-start", id: "t1" }, + { type: "text-delta", id: "t1", delta: "Hello" }, + { type: "text-end", id: "t1" } + ] as const + ).pipe(Stream.rechunk(1)) + }) + ) + + assert.deepStrictEqual( + yield* Ref.get(chat.history), + Prompt.make([ + { role: "user", content: "hi" }, + { role: "assistant", content: [{ type: "text", text: "Hello" }] } + ]) + ) + })) + it.effect("does not replay a completed approved tool", () => { let executions = 0 const toolkit = Toolkit.make(Tool.make("Counter", { diff --git a/repos/effect/packages/effect/test/ai/DecisionModel.test.ts b/packages/effect/test/ai/DecisionModel.test.ts similarity index 96% rename from repos/effect/packages/effect/test/ai/DecisionModel.test.ts rename to packages/effect/test/ai/DecisionModel.test.ts index cb58a61fc..58aeb65e6 100644 --- a/repos/effect/packages/effect/test/ai/DecisionModel.test.ts +++ b/packages/effect/test/ai/DecisionModel.test.ts @@ -105,7 +105,11 @@ describe("Decision", () => { technical: "bugs", sales: "pricing" }) - assert.deepStrictEqual(TicketTriage.decisions.frustration.criteria, ["calm", "frustrated", "angry"]) + assert.deepStrictEqual(TicketTriage.decisions.frustration.criteria, [ + { label: "calm", description: "calm" }, + { label: "frustrated", description: "frustrated" }, + { label: "angry", description: "angry" } + ]) assert.deepStrictEqual(TicketTriage.decisions.urgent.criteria, { false: "No time pressure", true: "Needs action now" @@ -672,6 +676,30 @@ describe("Decision validation contracts", () => { it("rate rejects duplicate levels", () => { assert.throws(() => Decision.rate({ instructions: "Rate", criteria: ["low", "high", "low"] })) + assert.throws(() => + Decision.rate({ instructions: "Rate", criteria: ["low", { label: "low", description: { what: "Also low" } }] }) + ) + }) + + it.effect("rate validates and labels answers by the labels of structured levels", () => { + const definition = Decision.make({ + input: Schema.String, + decisions: { + severity: Decision.rate({ + instructions: { question: "How severe?" }, + criteria: ["low", { label: "high", description: { impact: "outage" } }] + }) + } + }) + + return Effect.gen(function*() { + const { answers } = yield* DecisionModel.decide(definition, { input: "Export fails" }) + + assert.strictEqual(answers.severity.label, "high") + assert.deepStrictEqual(answers.severity.probabilities, { low: 0.1, high: 0.9 }) + }).pipe(Effect.provide(succeedWith({ + severity: { _tag: "Rate", rating: 0.9, probabilities: { low: 0.1, high: 0.9 } } + }))) }) it.effect("rate derives the argmax label without a provider label", () => diff --git a/repos/effect/packages/effect/test/ai/EmbeddingModel.test.ts b/packages/effect/test/ai/EmbeddingModel.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/EmbeddingModel.test.ts rename to packages/effect/test/ai/EmbeddingModel.test.ts diff --git a/repos/effect/packages/effect/test/ai/LanguageModel.test.ts b/packages/effect/test/ai/LanguageModel.test.ts similarity index 92% rename from repos/effect/packages/effect/test/ai/LanguageModel.test.ts rename to packages/effect/test/ai/LanguageModel.test.ts index 11b6f708a..8637f92c5 100644 --- a/repos/effect/packages/effect/test/ai/LanguageModel.test.ts +++ b/packages/effect/test/ai/LanguageModel.test.ts @@ -69,6 +69,38 @@ describe("LanguageModel", () => { response: undefined } + for (const method of ["generateText", "streamText"] as const) { + it.effect(`${method} uses current tool schemas and handlers across requests`, () => + Effect.gen(function*() { + const run = (tool: typeof MyTool, result: string, disableToolCallResolution = false) => { + const toolkit = Toolkit.make(tool) + const options = { prompt: "hi", toolkit, disableToolCallResolution } + return (method === "generateText" + ? LanguageModel.generateText(options).pipe(Effect.map((response) => response.toolResults[0])) + : LanguageModel.streamText(options).pipe( + Stream.runCollect, + Effect.map((parts) => parts.find((part) => part.type === "tool-result")) + )).pipe( + Effect.provide(toolkit.toLayer({ MyTool: () => Effect.succeed({ testSuccess: result }) })) + ) + } + for (const result of ["first", "second"]) { + const toolResult = yield* run(MyTool, result) + assertDefined(toolResult) + deepStrictEqual(toolResult.result, { testSuccess: result }) + } + yield* run(MyTool, "unused", true) + const changed = MyTool.setParameters( + Schema.Struct({ testParam: Schema.String.check(Schema.isPattern(/^changed$/)) }) + ) + const error = yield* run(changed, "unused", true).pipe(Effect.flip) + strictEqual(error.reason._tag, "InvalidOutputError") + }).pipe(TestUtils.withLanguageModel({ + generateText: [{ type: "tool-call", id: "call", name: "MyTool", params: { testParam: "test-param" } }], + streamText: [{ type: "tool-call", id: "call", name: "MyTool", params: { testParam: "test-param" } }] + }))) + } + describe("generateText", () => { it.effect("records resolved request options on a client span", () => LanguageModel.generateText({ prompt: "hi" }).pipe( @@ -411,6 +443,40 @@ describe("LanguageModel", () => { deepStrictEqual(response.toolCalls[0]!.params, { testParam: "test-param" }) strictEqual(yield* Ref.get(calls), 0) })) + + it.effect("ignores deferred tool call validation when tool call resolution is enabled", () => + Effect.gen(function*() { + const error = yield* LanguageModel.generateText({ + prompt: [], + toolkit: MyToolkit, + toolCallValidation: "deferred" + }).pipe( + TestUtils.withLanguageModel({ + generateText: [{ type: "tool-call", id: "call_1", name: "Write", params: { path: "a" } }] + }), + Effect.provide(MyToolkitLayer), + Effect.flip + ) + + strictEqual(error.reason._tag, "InvalidOutputError") + })) + + it.effect("returns tool calls without a toolkit when tool call validation is deferred", () => + Effect.gen(function*() { + const response = yield* LanguageModel.generateText({ + prompt: [], + disableToolCallResolution: true, + toolCallValidation: "deferred" + }).pipe( + TestUtils.withLanguageModel({ + generateText: [{ type: "tool-call", id: "call_1", name: "Write", params: { path: "a" } }] + }) + ) + + deepStrictEqual(response.toolCalls.map(({ name, params }) => ({ name, params })), [ + { name: "Write", params: { path: "a" } } + ]) + })) }) describe("streamText", () => { @@ -1287,6 +1353,31 @@ describe("LanguageModel", () => { strictEqual(yield* Ref.get(active), 0) strictEqual(yield* Ref.get(maxActive), 1) })) + + it.effect("validates provider-executed tool calls when tool call validation is deferred", () => + Effect.gen(function*() { + const error = yield* LanguageModel.streamText({ + prompt: [], + toolkit: MyToolkit, + disableToolCallResolution: true, + toolCallValidation: "deferred" + }).pipe( + Stream.runDrain, + TestUtils.withLanguageModel({ + streamText: [{ + type: "tool-call", + id: "call_1", + name: "MyTool", + params: { testParam: 42 }, + providerExecuted: true + }] + }), + Effect.provide(MyToolkitLayer), + Effect.flip + ) + + strictEqual(error.reason._tag, "InvalidOutputError") + })) }) describe("generateObject", () => { @@ -2108,6 +2199,107 @@ describe("LanguageModel", () => { }) describe("tool approval", () => { + for (const method of ["generateText", "streamText"] as const) { + it.effect(`resolves only pending approvals in mixed approval history (${method})`, () => + Effect.gen(function*() { + const executed: Array = [] + let capturedPrompt: LanguageModel.ProviderOptions["prompt"] | undefined + const prompt: Array = [] + + for ( + const [id, approved, resolved] of [ + ["previous-approved", true, true], + ["previous-denied", false, true], + ["pending-approved", true, false], + ["pending-denied", false, false] + ] as const + ) { + prompt.push( + Prompt.assistantMessage({ + content: [ + Prompt.toolCallPart({ + id, + name: "ApprovalTool", + params: { action: id }, + providerExecuted: false + }), + Prompt.toolApprovalRequestPart({ approvalId: id, toolCallId: id }) + ] + }), + Prompt.toolMessage({ + content: [ + Prompt.toolApprovalResponsePart({ approvalId: id, approved, reason: "User declined" }), + ...(resolved + ? [Prompt.toolResultPart({ + id, + name: "ApprovalTool", + result: { result: id }, + isFailure: !approved, + providerExecuted: false + })] + : []) + ] + }) + ) + } + + const options = { prompt, toolkit: ApprovalToolkit } + yield* (method === "generateText" + ? LanguageModel.generateText(options) + : LanguageModel.streamText(options).pipe(Stream.runDrain)).pipe( + TestUtils.withLanguageModel({ + generateText: (opts) => { + capturedPrompt = opts.prompt + return Effect.succeed([finishPart]) + }, + streamText: (opts) => { + capturedPrompt = opts.prompt + return [finishPart] + } + }), + Effect.provide(ApprovalToolkit.toLayer({ + ApprovalTool: ({ action }) => + Effect.sync(() => { + executed.push(action) + return { result: "approved-result" } + }), + DynamicApprovalTool: () => Effect.succeed({ result: "dynamic-result" }) + })) + ) + + deepStrictEqual(executed, ["pending-approved"]) + assertDefined(capturedPrompt) + const parts = capturedPrompt.content.flatMap(( + message + ) => message.role === "assistant" || message.role === "tool" ? message.content : []) + deepStrictEqual(parts.filter((part) => part.type === "tool-approval-request"), []) + deepStrictEqual(parts.filter((part) => part.type === "tool-approval-response"), []) + deepStrictEqual(parts.filter((part) => part.type === "tool-call").map((part) => part.id), [ + "previous-approved", + "previous-denied", + "pending-approved", + "pending-denied" + ]) + deepStrictEqual( + parts.filter((part) => part.type === "tool-result").map((part) => ({ + id: part.id, + result: part.result, + isFailure: part.isFailure + })), + [ + { id: "previous-approved", result: { result: "previous-approved" }, isFailure: false }, + { id: "previous-denied", result: { result: "previous-denied" }, isFailure: true }, + { id: "pending-approved", result: { result: "approved-result" }, isFailure: false }, + { + id: "pending-denied", + result: { type: "execution-denied", reason: "User declined" }, + isFailure: true + } + ] + ) + })) + } + it.effect("emits tool-approval-request when tool has needsApproval: true", () => Effect.gen(function*() { const parts: Array, "opaque">> = [] diff --git a/repos/effect/packages/effect/test/ai/LanguageModelRepresentation.test.ts b/packages/effect/test/ai/LanguageModelRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/LanguageModelRepresentation.test.ts rename to packages/effect/test/ai/LanguageModelRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts b/packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts rename to packages/effect/test/ai/LanguageModelTrackerLifecycle.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpSchema.test.ts b/packages/effect/test/ai/McpSchema.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpSchema.test.ts rename to packages/effect/test/ai/McpSchema.test.ts diff --git a/packages/effect/test/ai/McpServer/LayerHttpRequestScoped.test.ts b/packages/effect/test/ai/McpServer/LayerHttpRequestScoped.test.ts new file mode 100644 index 000000000..1ae13563e --- /dev/null +++ b/packages/effect/test/ai/McpServer/LayerHttpRequestScoped.test.ts @@ -0,0 +1,364 @@ +import { assert, describe, it } from "@effect/vitest" +import * as McpProtocol from "effect/ai/McpProtocol" +import * as McpSchema from "effect/ai/McpSchema" +import * as McpServer from "effect/ai/McpServer" +import * as Context from "effect/Context" +import * as Deferred from "effect/Deferred" +import * as Effect from "effect/Effect" +import * as HttpRouter from "effect/http/HttpRouter" +import * as Layer from "effect/Layer" +import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" +import * as BaseProtocolTest from "./McpConformance/BaseProtocolTest.ts" +import * as CompletionTest from "./McpConformance/CompletionTest.ts" +import { layer as makeMcpConformanceLayer } from "./McpConformance/McpConformance.ts" +import * as MultiRoundTripTest from "./McpConformance/MultiRoundTripTest.ts" +import * as PromptsTest from "./McpConformance/PromptsTest.ts" +import * as ResourcesTest from "./McpConformance/ResourcesTest.ts" +import * as ToolsTest from "./McpConformance/ToolsTest.ts" +import { makeHttpHarness, MCP_ENDPOINT } from "./TestUtils/McpHttpHarness.ts" +import { makeMcpSseReader, readMcpHttpResponse } from "./TestUtils/McpHttpResponse.ts" +import { makeServerLayer } from "./TestUtils/McpServerLayer.ts" +import { makeWorkerdHarness } from "./TestUtils/McpWorkerdHarness.ts" + +const protocol = McpProtocol.v2026_07_28 +const conformance = makeMcpConformanceLayer(protocol, { mode: "request" }) + +describe("McpServer.layerHttpRequestScoped conformance", () => { + BaseProtocolTest.statelessModernSuite(protocol, conformance) + ToolsTest.suite(protocol, conformance) + ToolsTest.statelessModernSuite(protocol, conformance) + ResourcesTest.suite(protocol, conformance) + PromptsTest.suite(protocol, conformance, { additionalPromptNames: ["MrtrPrompt"] }) + CompletionTest.suite(protocol, conformance) + MultiRoundTripTest.suite(protocol, conformance) +}) + +const progressToken = "progress-1" + +const callTool = ( + name: string, + id: number, + args: Record = {}, + metadata: Record = {} +) => ({ + body: { + jsonrpc: "2.0", + id, + method: "tools/call", + params: { + name, + arguments: args, + _meta: { + progressToken, + ...metadata, + "io.modelcontextprotocol/protocolVersion": protocol.protocolVersion, + "io.modelcontextprotocol/clientCapabilities": {}, + "io.modelcontextprotocol/clientInfo": { name: "test", version: "1.0.0" } + } + } + }, + headers: { + accept: "application/json, text/event-stream", + "content-type": "application/json", + "MCP-Protocol-Version": protocol.protocolVersion, + "Mcp-Method": "tools/call", + "Mcp-Name": name + } +}) + +const sseMessages = (body: string): Array> => + body.split("\n").filter((line) => line.startsWith("data: ")).map((line) => JSON.parse(line.slice(6))) + +const toolRegistration = ( + name: string, + handle: (server: McpServer.McpServer["Service"], args: any) => Effect.Effect +) => + Layer.effectDiscard(Effect.gen(function*() { + const server = yield* McpServer.McpServer + yield* server.addTool({ + tool: new McpSchema.Tool({ name, inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: (args) => handle(server, args) + }) + })) + +const toolLayer = ( + name: string, + handle: (server: McpServer.McpServer["Service"], args: any) => Effect.Effect +) => + toolRegistration(name, handle).pipe( + Layer.provideMerge(makeServerLayer({ name: "RequestMode", protocols: [protocol], mode: "request" })) + ) + +const reportProgress = (server: McpServer.McpServer["Service"]) => + server.notifications["notifications/progress"]({ progressToken, progress: 1 }).pipe( + Effect.as(new McpSchema.CallToolResult({ content: [{ type: "text", text: "done" }] })) + ) + +describe("McpServer.layerHttpRequestScoped", () => { + it.live("should expose tools registered after the first POST in later lists and discovery", () => + Effect.gen(function*() { + const serverReady = yield* Deferred.make() + const harness = yield* makeHttpHarness( + Layer.effectDiscard(Effect.gen(function*() { + yield* Deferred.succeed(serverReady, yield* McpServer.McpServer) + })).pipe( + Layer.provideMerge(makeServerLayer({ name: "LateRegistration", protocols: [protocol], mode: "request" })) + ) + ) + let id = 0 + const post = Effect.fnUntraced(function*(method: "tools/list" | "server/discover") { + const response = yield* harness.post({ + jsonrpc: "2.0", + id: ++id, + method, + params: { + _meta: { + "io.modelcontextprotocol/protocolVersion": protocol.protocolVersion, + "io.modelcontextprotocol/clientCapabilities": {}, + "io.modelcontextprotocol/clientInfo": { name: "test", version: "1.0.0" } + } + } + }, { + "MCP-Protocol-Version": protocol.protocolVersion, + "Mcp-Method": method + }) + assert.strictEqual(response.status, 200) + const message = Schema.decodeUnknownSync(Schema.Struct({ result: Schema.JsonObject }))( + yield* readMcpHttpResponse(response) + ) + return message.result + }) + + assert.deepStrictEqual((yield* post("tools/list")).tools, []) + const before = yield* post("server/discover") + assert(Predicate.isObject(before.capabilities)) + assert.notProperty(before.capabilities, "tools") + + const server = yield* Deferred.await(serverReady) + yield* server.addTool({ + tool: new McpSchema.Tool({ name: "LateTool", inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: () => Effect.succeed(new McpSchema.CallToolResult({ content: [] })) + }) + + assert.deepStrictEqual((yield* post("tools/list")).tools, [ + { name: "LateTool", inputSchema: { type: "object" } } + ]) + const after = yield* post("server/discover") + assert(Predicate.isObject(after.capabilities)) + assert.deepStrictEqual(after.capabilities.tools, { listChanged: false }) + }).pipe(Effect.timeout("2 seconds"))) + + // https://github.com/Effect-TS/effect/issues/9026 + it.live("should let each request's services override the layer services seen by tools", () => + Effect.gen(function*() { + const Label = Context.Reference("test/RequestScoped/Label", { defaultValue: () => "default" }) + const { dispose, handler } = HttpRouter.toWebHandler( + toolLayer("Label", () => + Effect.gen(function*() { + return new McpSchema.CallToolResult({ content: [{ type: "text", text: yield* Label }] }) + })).pipe(Layer.provide(Layer.succeed(Label)("layer"))), + { disableLogger: true } + ) + yield* Effect.addFinalizer(() => Effect.promise(dispose)) + const call = (override?: string) => { + const { body, headers } = callTool("Label", 1) + return Effect.promise(() => + handler( + new Request(MCP_ENDPOINT, { method: "POST", headers, body: JSON.stringify(body) }), + override === undefined ? undefined : Label.context(override) + ) + ).pipe(Effect.flatMap(readMcpHttpResponse)) + } + + for (const override of [undefined, "first", "second"]) { + assert.nestedPropertyVal(yield* call(override), "result.content[0].text", override ?? "layer") + } + })) + + it.live("should serve each POST when timers from completed requests never fire", () => + Effect.gen(function*() { + const worker = yield* makeWorkerdHarness(toolLayer("ReportProgress", reportProgress)) + // The first request builds the server and completes without serving MCP. + const first = yield* worker.request(new Request(MCP_ENDPOINT, { method: "GET" })) + assert.strictEqual(first.status, 405) + + for (const id of [1, 2]) { + const { body, headers } = callTool("ReportProgress", id) + const response = yield* worker.request( + new Request(MCP_ENDPOINT, { method: "POST", headers, body: JSON.stringify(body) }) + ).pipe(Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.die(`POST ${id} stalled`) + })) + assert.deepStrictEqual(sseMessages(response.body).map((message) => message.method ?? message.id), [ + "notifications/progress", + id + ]) + } + })) + + it.live("should interrupt the handler when the client aborts its POST", () => + Effect.gen(function*() { + const entered = yield* Deferred.make() + const interrupted = yield* Deferred.make() + const { dispose, handler } = HttpRouter.toWebHandler( + toolLayer("Block", () => + Deferred.succeed(entered, undefined).pipe( + Effect.andThen(Effect.never), + Effect.onInterrupt(() => Deferred.succeed(interrupted, undefined)) + )), + { disableLogger: true } + ) + yield* Effect.addFinalizer(() => Effect.promise(dispose)) + const abort = new AbortController() + const { body, headers } = callTool("Block", 1) + const response = handler( + new Request(MCP_ENDPOINT, { method: "POST", headers, body: JSON.stringify(body), signal: abort.signal }) + ) + response.catch(() => undefined) + + yield* Deferred.await(entered) + abort.abort() + yield* Deferred.await(interrupted).pipe(Effect.timeout("1 second")) + })) + + for (const cancel of ["abort", "body"] as const) { + it.live(`should interrupt a streaming handler when the client cancels by ${cancel} after headers`, () => + Effect.gen(function*() { + const interrupted = yield* Deferred.make() + const { dispose, handler } = HttpRouter.toWebHandler( + toolLayer("ProgressThenBlock", (server) => + reportProgress(server).pipe( + Effect.andThen(Effect.never), + Effect.onInterrupt(() => Deferred.succeed(interrupted, undefined)) + )), + { disableLogger: true } + ) + yield* Effect.addFinalizer(() => Effect.promise(dispose)) + const abort = new AbortController() + const { body, headers } = callTool("ProgressThenBlock", 1) + const response = yield* Effect.promise(() => + handler( + new Request(MCP_ENDPOINT, { method: "POST", headers, body: JSON.stringify(body), signal: abort.signal }) + ) + ) + const reader = makeMcpSseReader(response) + yield* Effect.addFinalizer(() => reader.cancel) + assert.strictEqual((yield* reader.take()).method, "notifications/progress") + + if (cancel === "abort") abort.abort() + else yield* reader.cancel + yield* Deferred.await(interrupted).pipe(Effect.timeout("1 second")) + })) + } + + it.live("should interrupt a streaming handler when the client aborts while its unread body is full", () => + Effect.gen(function*() { + const headersSent = yield* Deferred.make() + const backpressured = yield* Deferred.make() + const interrupted = yield* Deferred.make() + const { dispose, handler } = HttpRouter.toWebHandler( + toolLayer("ProgressUnread", (server) => + Effect.gen(function*() { + yield* reportProgress(server) + yield* Deferred.await(headersSent) + // Later chunks arrive while the client reads nothing, so the body stops pulling. + yield* reportProgress(server) + yield* reportProgress(server) + yield* Deferred.succeed(backpressured, undefined) + return yield* Effect.never + }).pipe(Effect.onInterrupt(() => Deferred.succeed(interrupted, undefined)))), + { disableLogger: true } + ) + yield* Effect.addFinalizer(() => Effect.promise(dispose)) + const abort = new AbortController() + const { body, headers } = callTool("ProgressUnread", 1) + const response = yield* Effect.promise(() => + handler( + new Request(MCP_ENDPOINT, { method: "POST", headers, body: JSON.stringify(body), signal: abort.signal }) + ) + ) + yield* Effect.addFinalizer(() => Effect.promise(() => response.body!.cancel())) + yield* Deferred.succeed(headersSent, undefined) + yield* Deferred.await(backpressured) + // Let the body stream take the next chunk and wait for a pull that never comes. + yield* Effect.sleep("50 millis") + + abort.abort() + yield* Deferred.await(interrupted).pipe(Effect.timeout("1 second")) + })) + + it.live("should deliver overlapping requests only their own notifications", () => + Effect.gen(function*() { + const arrived = { a: yield* Deferred.make(), b: yield* Deferred.make() } + const harness = yield* makeHttpHarness(toolLayer("Overlap", (server, { marker }: { marker: "a" | "b" }) => + Effect.gen(function*() { + // Both requests are in flight before either emits a notification. + yield* Deferred.succeed(arrived[marker], undefined) + yield* Deferred.await(arrived[marker === "a" ? "b" : "a"]) + yield* server.notifications["notifications/progress"]({ progressToken, progress: 1, message: marker }) + yield* server.notifications["notifications/message"]({ level: "info", data: marker }) + if (marker === "a") { + yield* server.notifications["notifications/tools/list_changed"]({}) + } + return new McpSchema.CallToolResult({ content: [{ type: "text", text: marker }] }) + }))) + + const responses = yield* Effect.forEach(["a", "b"] as const, (marker) => { + // Both requests reuse the same JSON-RPC id and progress token. + const { body, headers } = callTool("Overlap", 1, { marker }, { + "io.modelcontextprotocol/logLevel": "debug" + }) + return harness.post(body, headers).pipe( + Effect.flatMap((response) => + Effect.promise(() => response.text()) + ), + Effect.map((body) => [marker, sseMessages(body)] as const) + ) + }, { concurrency: "unbounded" }).pipe(Effect.timeout("2 seconds")) + + for (const [marker, messages] of responses) { + assert.deepStrictEqual( + messages.map((message) => [message.method ?? message.id, message.params ?? message.result.content]), + [ + ["notifications/progress", { progressToken, progress: 1, message: marker }], + ["notifications/message", { level: "info", data: marker }], + [1, [{ type: "text", text: marker }]] + ] + ) + } + })) + + it.live("should keep layerHttp notifications when a request-scoped endpoint shares the server", () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness( + toolRegistration("ReportProgress", reportProgress).pipe( + Layer.provideMerge(Layer.merge( + makeServerLayer({ name: "DefaultMode", protocols: [protocol] }), + McpServer.layerHttpRequestScoped({ + name: "RequestMode", + version: "1.0.0", + path: "/request", + protocols: [protocol] + }) + )) + ) + ) + for (const path of ["/mcp", "/request"]) { + const { body, headers } = callTool("ReportProgress", 1) + const response = yield* Effect.promise(() => + harness.handler( + new Request(new URL(path, MCP_ENDPOINT), { method: "POST", headers, body: JSON.stringify(body) }) + ) + ) + const messages = sseMessages(yield* Effect.promise(() => response.text())) + assert.deepStrictEqual(messages.map((message) => message.method ?? message.id), [ + "notifications/progress", + 1 + ], path) + } + }).pipe(Effect.timeout("2 seconds"))) +}) diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts b/packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/BaseProtocolTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts b/packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/CompletionTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts b/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts similarity index 85% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts index 4d8877dcf..c35456eb3 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts +++ b/packages/effect/test/ai/McpServer/McpConformance/ElicitationTest.ts @@ -82,6 +82,64 @@ export const suite = ( ) => it.layer(layer)(`Mcp Conformance (${protocol.protocolVersion})`, (it) => { describe("Elicitation", () => { + // https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation + it.effect("MUST cancel the pending HTTP elicitation when its tool call is cancelled", () => + Effect.gen(function*() { + const toolkit = Toolkit.make(Tool.make("Approve", { + parameters: Tool.EmptyParams, + success: Schema.Struct({ approved: Schema.Boolean }), + dependencies: [McpSchema.McpServerClient] + })) + const registration = McpServer.toolkit(toolkit).pipe(Layer.provide( + toolkit.toLayer({ + Approve: () => + McpServer.elicit({ + message: "Approve this operation", + schema: Schema.Struct({ approved: Schema.Boolean }) + }).pipe(Effect.orDie) + }) + )) + const harness = yield* makeHttpHarness(registration.pipe(Layer.provideMerge( + makeServerLayer({ name: "ElicitationCancellationConformance", protocols: [protocol] }) + ))) + const initialized = yield* harness.post({ + jsonrpc: "2.0", + id: "initialize", + method: "initialize", + params: { + protocolVersion: protocol.protocolVersion, + capabilities: { elicitation: {} }, + clientInfo: { name: "approval-client", version: "1.0.0" } + } + }) + yield* readMcpHttpResponse(initialized) + const sessionId = initialized.headers.get("Mcp-Session-Id") + assert.isNotNull(sessionId) + const headers = { "Mcp-Session-Id": sessionId, "Mcp-Protocol-Version": protocol.protocolVersion } + yield* harness.post({ jsonrpc: "2.0", method: "notifications/initialized" }, headers) + const response = yield* harness.post({ + jsonrpc: "2.0", + id: "approval-tool", + method: "tools/call", + params: { name: "Approve", arguments: {} } + }, headers) + const stream = makeMcpSseReader(response) + yield* Effect.addFinalizer(() => stream.cancel) + const reverse = yield* stream.take() + assert.strictEqual(reverse.method, "elicitation/create") + assert.isDefined(reverse.id) + const cancelled = yield* harness.post({ + jsonrpc: "2.0", + method: "notifications/cancelled", + params: { requestId: "approval-tool" } + }, headers) + assert.strictEqual(cancelled.status, 202) + const remaining = yield* stream.drain().pipe(Effect.timeout("1 second")) + assert.deepStrictEqual(remaining.map((message) => message.method), ["notifications/cancelled"]) + assert.propertyVal(remaining[0].params, "requestId", reverse.id) + assert.isFalse(remaining.some((message) => message.method?.startsWith("@effect/rpc/"))) + })) + for (const responseSession of ["missing", "unknown", "unrelated", "owner"] as const) { // https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#session-management // A reverse response must belong to the session that requested the input. diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts b/packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/LifecycleTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts b/packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/LoggingTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts b/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts similarity index 98% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts rename to packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts index 4aacd9165..5f2504bdc 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts +++ b/packages/effect/test/ai/McpServer/McpConformance/McpConformance.ts @@ -205,13 +205,17 @@ export class McpConformance extends Context.Service -export const layer = (protocol: McpProtocol.ProtocolAdapter) => +export const layer = ( + protocol: McpProtocol.ProtocolAdapter, + options?: { readonly mode?: "server" | "request" | undefined } +) => Layer.effect( McpConformance, Effect.gen(function*() { const defaultHarness = yield* makeHttpHarness(makeServerLayer({ name: SERVER_NAME, - protocols: [protocol] + protocols: [protocol], + mode: options?.mode })) const observations = yield* Ref.make({ toolInvocations: 0, @@ -219,7 +223,7 @@ export const layer = (protocol: McpProtocol.ProtocolAdapter) => resourceTemplateInvocations: 0 }) const requestCount = yield* Ref.make(0) - const featuresHarness = yield* makeHttpHarness(makeFeaturesServerLayer(protocol, observations)) + const featuresHarness = yield* makeHttpHarness(makeFeaturesServerLayer(protocol, observations, options?.mode)) const post = ( harness: typeof defaultHarness, body: unknown, diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts b/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts similarity index 99% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts rename to packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts index a73c3602a..b6602957d 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts +++ b/packages/effect/test/ai/McpServer/McpConformance/McpConformanceFixtures.ts @@ -462,7 +462,8 @@ const ContextCompletionPrompt = McpServer.prompt({ export const makeFeaturesServerLayer = ( protocol: McpProtocol.ProtocolAdapter, - observations: Ref.Ref + observations: Ref.Ref, + mode?: "server" | "request" ) => Layer.mergeAll( makeTestToolkitLayer(observations, protocol.protocolVersion), @@ -549,6 +550,7 @@ export const makeFeaturesServerLayer = ( name: "McpConformance", instructions: "Follow the test server instructions.", protocols: [protocol], - extensions: { "example/lifecycle": { enabled: true } } + extensions: { "example/lifecycle": { enabled: true } }, + mode })) ) diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts b/packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts rename to packages/effect/test/ai/McpServer/McpConformance/McpTestPeer.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts b/packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/MultiRoundTripTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/PromptsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts b/packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/ResourcesTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/RootsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts b/packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/SamplingTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts similarity index 99% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts index 984cb02af..1226f1467 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts +++ b/packages/effect/test/ai/McpServer/McpConformance/SubscriptionsTest.ts @@ -634,7 +634,10 @@ export const suite = (protocol: McpProtocol.ProtocolAdapter, layer: McpConforman params: { _meta: httpMetadata(protocol) } }, { "MCP-Protocol-Version": protocol.protocolVersion, "Mcp-Method": "tools/list" }) assert.match(tools.headers.get("content-type") ?? "", /^application\/json/) - const listed = yield* Effect.promise(() => tools.json()) + const listed = (yield* Effect.promise(() => tools.json())) as { + readonly id: string + readonly result: { readonly tools: ReadonlyArray } + } assert.strictEqual(listed.id, "tools-during-subscription") assert.deepStrictEqual(listed.result.tools, [ { name: "http-subscription-baseline", inputSchema: { type: "object", properties: {} } } diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/ToolsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts b/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/TransportsTest.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts b/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts similarity index 92% rename from repos/effect/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts rename to packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts index 0dd9a581b..fe6ee9bf4 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts +++ b/packages/effect/test/ai/McpServer/McpConformance/UtilitiesTest.ts @@ -8,6 +8,7 @@ import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" import * as Fiber from "effect/Fiber" import * as Layer from "effect/Layer" +import * as Queue from "effect/Queue" import { initializeHttpSession, makeHttpHarness } from "../TestUtils/McpHttpHarness.ts" import { makeMcpSseReader } from "../TestUtils/McpHttpResponse.ts" import { makeServerLayer } from "../TestUtils/McpServerLayer.ts" @@ -404,6 +405,7 @@ export const statefulLegacySuite = (protocol: McpProtocol.ProtocolAdapter, layer assert.isFalse(yield* Deferred.isDone(otherInterrupted)) })) + // https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#sending-messages-to-the-server it.effect("should close without a reply when a tool is cancelled before emitting HTTP output", () => Effect.gen(function*() { const entered = yield* Deferred.make() @@ -436,6 +438,8 @@ export const statefulLegacySuite = (protocol: McpProtocol.ProtocolAdapter, layer assert.strictEqual(cancelled.status, 202) yield* Deferred.await(interrupted).pipe(Effect.timeout("1 second")) const response = yield* Fiber.join(pending).pipe(Effect.timeout("1 second")) + assert.strictEqual(response.status, 200) + assert.strictEqual(response.headers.get("content-type"), "text/event-stream") assert.strictEqual(yield* Effect.promise(() => response.text()), "") })) @@ -509,6 +513,38 @@ export const statefulLegacySuite = (protocol: McpProtocol.ProtocolAdapter, layer assert.strictEqual(yield* Deferred.isDone(otherInterrupted), cancelled === "all") })) } + + it.effect("should close without a reply when every batch request is cancelled before emitting HTTP output", () => + Effect.gen(function*() { + const entered = yield* Queue.unbounded() + const registration = Layer.effectDiscard(McpServer.McpServer.use((server) => + server.addTool({ + tool: new McpSchema.Tool({ name: "Wait", inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: () => Queue.offer(entered, void 0).pipe(Effect.andThen(Effect.never)) + }) + )) + const harness = yield* makeHttpHarness(registration.pipe(Layer.provideMerge(serverLayer))) + const headers = yield* initializeHttpSession(harness, protocol) + const pending = yield* harness.post( + [1, 2].map((id) => ({ + jsonrpc: "2.0", + id, + method: "tools/call", + params: { name: "Wait", arguments: {} } + })), + headers + ).pipe(Effect.forkChild) + yield* Queue.takeN(entered, 2) + yield* harness.post( + [1, 2].map((requestId) => ({ jsonrpc: "2.0", method: "notifications/cancelled", params: { requestId } })), + headers + ) + const response = yield* Fiber.join(pending).pipe(Effect.timeout("1 second")) + assert.strictEqual(response.status, 200) + assert.strictEqual(response.headers.get("content-type"), "text/event-stream") + assert.strictEqual(yield* Effect.promise(() => response.text()), "") + })) } }) }) diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpProtocol.test.ts b/packages/effect/test/ai/McpServer/McpProtocol.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/McpProtocol.test.ts rename to packages/effect/test/ai/McpServer/McpProtocol.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts b/packages/effect/test/ai/McpServer/McpServer.test.ts similarity index 96% rename from repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts rename to packages/effect/test/ai/McpServer/McpServer.test.ts index 05dd823f3..7e7bc1ba7 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/McpServer.test.ts +++ b/packages/effect/test/ai/McpServer/McpServer.test.ts @@ -538,6 +538,8 @@ describe("McpServer", () => { const outboundMessage = yield* Queue.take(outbound) assert.strictEqual(outboundMessage._tag, clientId === 1 ? "Request" : "Exit") if (clientId === 1) { + assert(outboundMessage._tag === "Request") + assert.strictEqual(outboundMessage.tag, "elicitation/create") yield* send(clientId, { _tag: "Request", id: "", @@ -547,8 +549,12 @@ describe("McpServer", () => { headers: [] }) yield* Deferred.await(cancelled) - // The reverse client emits a control Interrupt; consume it before the next connection. - assert.strictEqual((yield* Queue.take(outbound))._tag, "Interrupt") + // Consume the MCP cancellation before the next connection. + const cancellation = yield* Queue.take(outbound) + assert(cancellation._tag === "Request") + assert.strictEqual(cancellation.tag, "notifications/cancelled") + assert.strictEqual(cancellation.isNotification, true) + assert.deepStrictEqual(cancellation.payload, { requestId: outboundMessage.id }) } yield* send(clientId, { _tag: "Eof" }) connected.delete(clientId) @@ -2163,6 +2169,72 @@ describe("McpServer", () => { })) }) + for ( + const [label, makeLayer] of [ + ["allowSubscriptions is false", () => + McpServer.layerHttp({ + name: "SubscriptionsTest", + version: "1.0.0", + path: "/mcp", + protocols: [McpProtocol.v2026_07_28], + allowSubscriptions: false + })], + ["request scoped", () => + McpServer.layerHttpRequestScoped({ + name: "SubscriptionsTest", + version: "1.0.0", + path: "/mcp", + protocols: [McpProtocol.v2026_07_28] + })] + ] as const + ) { + it.effect(`disables HTTP subscription discovery and listening when ${label}`, () => + Effect.gen(function*() { + const harness = yield* makeHttpHarness( + Layer.effectDiscard(Effect.gen(function*() { + const server = yield* McpServer.McpServer + yield* server.addTool({ + tool: new McpSchema.Tool({ name: "Test", inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: () => Effect.succeed(new McpSchema.CallToolResult({ content: [] })) + }) + })).pipe(Layer.provideMerge(makeLayer())) + ) + const post = (method: string, params: Record = {}) => + harness.post({ + jsonrpc: "2.0", + id: 1, + method, + params: { + ...params, + _meta: { + "io.modelcontextprotocol/protocolVersion": "2026-07-28", + "io.modelcontextprotocol/clientCapabilities": {} + } + } + }, { "mcp-protocol-version": "2026-07-28", "mcp-method": method }) + + assert.nestedPropertyVal( + yield* readMcpHttpResponse(yield* post("server/discover")), + "result.capabilities.tools.listChanged", + false + ) + const response = yield* post("subscriptions/listen", { notifications: { toolsListChanged: true } }) + // A live subscription never reaches EOF; inspect only its first frame. + if (response.headers.get("content-type")?.includes("text/event-stream")) { + const reader = makeMcpSseReader(response) + yield* Effect.addFinalizer(() => reader.cancel) + const message = yield* reader.take() + assert.strictEqual(message.id, 1) + assert.deepInclude(message.error, { code: -32601 }) + } else { + const message = yield* readMcpHttpResponse(response) + assert.propertyVal(message, "id", 1) + assert.nestedPropertyVal(message, "error.code", -32601) + } + })) + } + it.effect("rejects unsupported HTTP methods without disturbing an initialized session", () => Effect.gen(function*() { const { client, httpClient } = yield* makeTestClient @@ -2564,6 +2636,41 @@ describe("McpServer", () => { }) describe("list-change notification scheduling", () => { + it.live("should not replay startup tool changes to the first HTTP subscription", () => + Effect.gen(function*() { + const kit = Toolkit.make(Tool.make("ping", { success: Schema.String })) + const harness = yield* makeHttpHarness( + McpServer.toolkit(kit).pipe( + Layer.provide(kit.toLayer({ ping: () => Effect.succeed("pong") })), + Layer.provide(makeServerLayer({ name: "StartupNotifications", protocols: [McpProtocol.v2026_07_28] })) + ) + ) + const subscription = makeMcpSseReader( + yield* harness.post({ + jsonrpc: "2.0", + id: 2, + method: "subscriptions/listen", + params: { + notifications: { toolsListChanged: true }, + _meta: { + "io.modelcontextprotocol/protocolVersion": "2026-07-28", + "io.modelcontextprotocol/clientCapabilities": {}, + "io.modelcontextprotocol/clientInfo": { name: "test", version: "1.0.0" } + } + } + }, { "mcp-protocol-version": "2026-07-28", "mcp-method": "subscriptions/listen" }) + ) + yield* Effect.addFinalizer(() => subscription.cancel) + assert.deepInclude(yield* subscription.take(), { + method: "notifications/subscriptions/acknowledged", + params: { + notifications: { toolsListChanged: true }, + _meta: { "io.modelcontextprotocol/subscriptionId": 2 } + } + }) + assert.deepStrictEqual(yield* subscription.take().pipe(Effect.timeoutOption("100 millis")), Option.none()) + })) + it.effect("should coalesce notifications when one registration kind changes repeatedly in a scheduling window", () => Effect.gen(function*() { const fixture = yield* makeMcpStdioHarness(McpProtocol.v2026_07_28) diff --git a/repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts b/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts rename to packages/effect/test/ai/McpServer/ProtocolAdapters.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts b/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpHttpHarness.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts b/packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpHttpResponse.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts b/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts similarity index 68% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts index 625d38b68..c3573c466 100644 --- a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts +++ b/packages/effect/test/ai/McpServer/TestUtils/McpServerLayer.ts @@ -21,19 +21,28 @@ export const makeServerLayer = (options: { readonly extensions?: Readonly> | undefined readonly allowedOrigins?: ReadonlyArray | undefined readonly allowSessionTermination?: boolean | undefined -}) => - McpServer.layerHttp({ + readonly mode?: "server" | "request" | undefined +}) => { + const common = { name: options.name, version: options.version ?? "1.0.0", instructions: options.instructions, - path: "/mcp", + path: "/mcp" as const, protocols: options.protocols ?? [McpProtocol.v2025_06_18], allowedOrigins: ["https://allowed.example"], - extensions: options.extensions, - allowSessionTermination: options.allowSessionTermination - }).pipe( + extensions: options.extensions + } + const layer = options.mode === "request" + ? McpServer.layerHttpRequestScoped({ + ...common, + // Request-mode callers supply stateless adapters. + protocols: common.protocols as Parameters[0]["protocols"] + }) + : McpServer.layerHttp({ ...common, allowSessionTermination: options.allowSessionTermination }) + return layer.pipe( Layer.provideMerge(Layer.succeed( References.CurrentLoggers, new Set([noopLogger]) )) ) +} diff --git a/repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts b/packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts rename to packages/effect/test/ai/McpServer/TestUtils/McpStdioHarness.ts diff --git a/packages/effect/test/ai/McpServer/TestUtils/McpWorkerdHarness.ts b/packages/effect/test/ai/McpServer/TestUtils/McpWorkerdHarness.ts new file mode 100644 index 000000000..c166386ec --- /dev/null +++ b/packages/effect/test/ai/McpServer/TestUtils/McpWorkerdHarness.ts @@ -0,0 +1,58 @@ +import * as Effect from "effect/Effect" +import * as HttpRouter from "effect/http/HttpRouter" +import * as Layer from "effect/Layer" +import * as Scheduler from "effect/Scheduler" +import { AsyncLocalStorage } from "node:async_hooks" + +export interface WorkerdResponse { + readonly status: number + readonly headers: Headers + readonly body: string +} + +/** + * Serves requests like a Cloudflare Workers isolate: the app is built while + * the first request is served, and a timer belongs to the request that set it. + * Once a request has finished sending its body, its pending timers never fire. + */ +export const makeWorkerdHarness = Effect.fnUntraced(function*( + serverLayer: Layer.Layer +) { + const owners = new AsyncLocalStorage<{ completed: boolean }>() + const scheduler = new Scheduler.MixedScheduler("async", (task) => { + const owner = owners.getStore() + const timer = setTimeout(() => { + if (owner?.completed !== true) task() + }, 0) + return () => clearTimeout(timer) + }) + const appLayer = serverLayer.pipe( + Layer.provideMerge(Layer.succeed(Scheduler.Scheduler, scheduler)) + ) + let app: { + readonly handler: (request: Request) => Promise + readonly dispose: () => Promise + } | undefined + yield* Effect.addFinalizer(() => Effect.promise(() => app?.dispose() ?? Promise.resolve())) + + const request = (input: Request): Effect.Effect => + Effect.promise((signal) => { + const owner = { completed: false } + signal.addEventListener("abort", () => { + owner.completed = true + }) + return owners.run(owner, async () => { + try { + app ??= HttpRouter.toWebHandler(appLayer, { disableLogger: true }) + const response = await app.handler(input) + // A Workers request completes once its whole body, including SSE, is sent. + const body = await response.text() + return { status: response.status, headers: response.headers, body } + } finally { + owner.completed = true + } + }) + }) + + return { request } as const +}) diff --git a/packages/effect/test/ai/McpServer/Workerd.test.ts b/packages/effect/test/ai/McpServer/Workerd.test.ts new file mode 100644 index 000000000..01f60510a --- /dev/null +++ b/packages/effect/test/ai/McpServer/Workerd.test.ts @@ -0,0 +1,95 @@ +import { assert, describe, it } from "@effect/vitest" +import * as Effect from "effect/Effect" +import * as esbuild from "esbuild" +import { Miniflare } from "miniflare" +import { fileURLToPath } from "node:url" + +const workerPath = fileURLToPath(new URL("./fixtures/workerd.ts", import.meta.url)) + +const bundle = Effect.promise(() => + esbuild.build({ + entryPoints: [workerPath], + bundle: true, + format: "esm", + platform: "browser", + target: "es2022", + conditions: ["workerd", "worker", "browser"], + write: false + }) +).pipe(Effect.map((result) => result.outputFiles[0].text), Effect.cached, Effect.runSync) + +const makeWorker = Effect.gen(function*() { + const contents = yield* bundle + return yield* Effect.acquireRelease( + Effect.sync(() => + new Miniflare({ + workers: [{ + config: { + name: "mcp", + compatibilityDate: "2026-09-11", + compatibilityFlags: ["nodejs_compat"], + manifest: { mainModule: "index.mjs", modules: { "index.mjs": { type: "esm", contents } } } + } + }] + }) + ), + (miniflare) => Effect.promise(() => miniflare.dispose()) + ) +}) + +const progressCall = (id: number): RequestInit => ({ + method: "POST", + headers: { + accept: "application/json, text/event-stream", + "content-type": "application/json", + "MCP-Protocol-Version": "2026-07-28", + "Mcp-Method": "tools/call", + "Mcp-Name": "ReportProgress" + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id, + method: "tools/call", + params: { + name: "ReportProgress", + arguments: {}, + _meta: { + progressToken: "progress-1", + "io.modelcontextprotocol/protocolVersion": "2026-07-28", + "io.modelcontextprotocol/clientCapabilities": {}, + "io.modelcontextprotocol/clientInfo": { name: "test", version: "1.0.0" } + } + } + }) +}) + +// https://github.com/Effect-TS/effect/issues/9026 +describe("McpServer.layerHttpRequestScoped on workerd", () => { + for (const first of ["GET", "warm-up POST"] as const) { + it.live(`should serve repeated progress calls after a ${first} builds the app`, () => + Effect.gen(function*() { + const worker = yield* makeWorker + const fetch = (init: RequestInit) => + Effect.promise(async () => { + const response = await worker.dispatchFetch("http://localhost/mcp", init as any) + return { status: response.status, body: await response.text() } + }).pipe(Effect.timeoutOrElse({ duration: "5 seconds", orElse: () => Effect.die("request stalled") })) + + const built = yield* fetch(first === "GET" ? { method: "GET" } : progressCall(0)) + assert.strictEqual(built.status, first === "GET" ? 405 : 200, built.body) + + for (const id of [1, 2]) { + const response = yield* fetch(progressCall(id)) + // workerd answers 500 when it detects that a request can never complete. + assert.strictEqual(response.status, 200, response.body) + const messages = response.body.split("\n") + .filter((line) => line.startsWith("data: ")) + .map((line) => JSON.parse(line.slice(6))) + assert.deepStrictEqual(messages.map((message) => message.method ?? message.id), [ + "notifications/progress", + id + ]) + } + }), { timeout: 60_000 }) + } +}) diff --git a/packages/effect/test/ai/McpServer/fixtures/workerd.ts b/packages/effect/test/ai/McpServer/fixtures/workerd.ts new file mode 100644 index 000000000..bec9a9374 --- /dev/null +++ b/packages/effect/test/ai/McpServer/fixtures/workerd.ts @@ -0,0 +1,36 @@ +import * as McpProtocol from "effect/ai/McpProtocol" +import * as McpSchema from "effect/ai/McpSchema" +import * as McpServer from "effect/ai/McpServer" +import * as Context from "effect/Context" +import * as Effect from "effect/Effect" +import * as HttpRouter from "effect/http/HttpRouter" +import * as Layer from "effect/Layer" + +const ReportProgress = Layer.effectDiscard(Effect.gen(function*() { + const server = yield* McpServer.McpServer + yield* server.addTool({ + tool: new McpSchema.Tool({ name: "ReportProgress", inputSchema: { type: "object" } }), + annotations: Context.empty(), + handle: () => + server.notifications["notifications/progress"]({ progressToken: "progress-1", progress: 1 }).pipe( + Effect.as(new McpSchema.CallToolResult({ content: [{ type: "text", text: "done" }] })) + ) + }) +})) + +const app = ReportProgress.pipe(Layer.provideMerge(McpServer.layerHttpRequestScoped({ + name: "Workerd", + version: "1.0.0", + path: "/mcp", + protocols: [McpProtocol.v2026_07_28] +}))) + +let handler: ((request: Request) => Promise) | undefined + +export default { + fetch(request: Request): Promise { + // Build the app while serving the isolate's first request, as Workers apps commonly do. + handler ??= HttpRouter.toWebHandler(app, { disableLogger: true }).handler + return handler(request) + } +} diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2024_11_05.test.ts b/packages/effect/test/ai/McpServer/v2024_11_05.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2024_11_05.test.ts rename to packages/effect/test/ai/McpServer/v2024_11_05.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2025_03_26.test.ts b/packages/effect/test/ai/McpServer/v2025_03_26.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2025_03_26.test.ts rename to packages/effect/test/ai/McpServer/v2025_03_26.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2025_06_18.test.ts b/packages/effect/test/ai/McpServer/v2025_06_18.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2025_06_18.test.ts rename to packages/effect/test/ai/McpServer/v2025_06_18.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2025_11_25.test.ts b/packages/effect/test/ai/McpServer/v2025_11_25.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2025_11_25.test.ts rename to packages/effect/test/ai/McpServer/v2025_11_25.test.ts diff --git a/repos/effect/packages/effect/test/ai/McpServer/v2026_07_28.test.ts b/packages/effect/test/ai/McpServer/v2026_07_28.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/McpServer/v2026_07_28.test.ts rename to packages/effect/test/ai/McpServer/v2026_07_28.test.ts diff --git a/repos/effect/packages/effect/test/ai/Model.test.ts b/packages/effect/test/ai/Model.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Model.test.ts rename to packages/effect/test/ai/Model.test.ts diff --git a/repos/effect/packages/effect/test/ai/OpenAiStructuredOutput.test.ts b/packages/effect/test/ai/OpenAiStructuredOutput.test.ts similarity index 97% rename from repos/effect/packages/effect/test/ai/OpenAiStructuredOutput.test.ts rename to packages/effect/test/ai/OpenAiStructuredOutput.test.ts index a974271da..334f72166 100644 --- a/repos/effect/packages/effect/test/ai/OpenAiStructuredOutput.test.ts +++ b/packages/effect/test/ai/OpenAiStructuredOutput.test.ts @@ -365,10 +365,14 @@ describe("toCodecOpenAI", () => { describe("Int", () => { it("Int", () => { assertJsonSchema(Schema.Int, { - "type": "integer" + "type": "integer", + "minimum": globalThis.Number.MIN_SAFE_INTEGER, + "maximum": globalThis.Number.MAX_SAFE_INTEGER }) assertJsonSchema(Schema.Int.annotate({ description: "description" }), { "type": "integer", + "minimum": globalThis.Number.MIN_SAFE_INTEGER, + "maximum": globalThis.Number.MAX_SAFE_INTEGER, "description": "description" }) }) @@ -376,6 +380,8 @@ describe("toCodecOpenAI", () => { it("Int + string format", () => { assertJsonSchema(Schema.Int.annotate({ format: "duration" }), { "type": "integer", + "minimum": globalThis.Number.MIN_SAFE_INTEGER, + "maximum": globalThis.Number.MAX_SAFE_INTEGER, "description": "a value with a format of duration" }) }) @@ -383,6 +389,8 @@ describe("toCodecOpenAI", () => { it("Int + unsupported format", () => { assertJsonSchema(Schema.Int.annotate({ format: "int32" }), { "type": "integer", + "minimum": globalThis.Number.MIN_SAFE_INTEGER, + "maximum": globalThis.Number.MAX_SAFE_INTEGER, "description": "a value with a format of int32" }) }) @@ -390,6 +398,7 @@ describe("toCodecOpenAI", () => { it("Int + isGreaterThan", () => { assertJsonSchema(Schema.Int.check(Schema.isGreaterThan(1)), { "type": "integer", + "maximum": globalThis.Number.MAX_SAFE_INTEGER, "description": "a value greater than 1", "exclusiveMinimum": 1 }) @@ -443,7 +452,11 @@ describe("toCodecOpenAI", () => { assertJsonSchema(Schema.Union([Schema.NonEmptyString, Schema.Int]), { "anyOf": [ { "type": "string", "description": "a value with a length of at least 1" }, - { "type": "integer" } + { + "type": "integer", + "minimum": globalThis.Number.MIN_SAFE_INTEGER, + "maximum": globalThis.Number.MAX_SAFE_INTEGER + } ] }) }) diff --git a/repos/effect/packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts b/packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts rename to packages/effect/test/ai/OpenAiStructuredOutputRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/Prompt.test.ts b/packages/effect/test/ai/Prompt.test.ts similarity index 94% rename from repos/effect/packages/effect/test/ai/Prompt.test.ts rename to packages/effect/test/ai/Prompt.test.ts index 3a734a3d8..0fa562b31 100644 --- a/repos/effect/packages/effect/test/ai/Prompt.test.ts +++ b/packages/effect/test/ai/Prompt.test.ts @@ -78,6 +78,40 @@ describe("Prompt", () => { }) describe("fromResponseParts", () => { + it("keeps text before an interleaved tool call", () => { + assert.deepStrictEqual( + Prompt.fromResponseParts([ + Response.makePart("text-start", { id: "t1" }), + Response.makePart("text-delta", { id: "t1", delta: "Hello" }), + Response.makePart("tool-call", { + id: "call-1", + name: "get_weather", + params: {}, + providerExecuted: false + }), + Response.makePart("text-end", { id: "t1" }) + ]), + Prompt.make([{ + role: "assistant", + content: [ + { type: "text", text: "Hello" }, + { type: "tool-call", id: "call-1", name: "get_weather", params: {}, providerExecuted: false } + ] + }]) + ) + }) + + it("omits unended reasoning and empty unended text", () => { + assert.deepStrictEqual( + Prompt.fromResponseParts([ + Response.makePart("text-start", { id: "t1" }), + Response.makePart("reasoning-start", { id: "r1" }), + Response.makePart("reasoning-delta", { id: "r1", delta: "Unsigned thinking" }) + ]), + Prompt.empty + ) + }) + it("preserves file-only responses as assistant messages", () => { const file = Response.makePart("file", { data: new Uint8Array([0, 127, 128, 255]), diff --git a/repos/effect/packages/effect/test/ai/Response.test.ts b/packages/effect/test/ai/Response.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Response.test.ts rename to packages/effect/test/ai/Response.test.ts diff --git a/repos/effect/packages/effect/test/ai/ResponseIdTracker.test.ts b/packages/effect/test/ai/ResponseIdTracker.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/ResponseIdTracker.test.ts rename to packages/effect/test/ai/ResponseIdTracker.test.ts diff --git a/repos/effect/packages/effect/test/ai/Tokenizer.test.ts b/packages/effect/test/ai/Tokenizer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Tokenizer.test.ts rename to packages/effect/test/ai/Tokenizer.test.ts diff --git a/repos/effect/packages/effect/test/ai/Tool.test.ts b/packages/effect/test/ai/Tool.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/Tool.test.ts rename to packages/effect/test/ai/Tool.test.ts diff --git a/repos/effect/packages/effect/test/ai/ToolRepresentation.test.ts b/packages/effect/test/ai/ToolRepresentation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/ToolRepresentation.test.ts rename to packages/effect/test/ai/ToolRepresentation.test.ts diff --git a/repos/effect/packages/effect/test/ai/utils.ts b/packages/effect/test/ai/utils.ts similarity index 100% rename from repos/effect/packages/effect/test/ai/utils.ts rename to packages/effect/test/ai/utils.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.array.test.ts b/packages/effect/test/arbitrary/Arbitrary.array.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.array.test.ts rename to packages/effect/test/arbitrary/Arbitrary.array.test.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.causes.test.ts b/packages/effect/test/arbitrary/Arbitrary.causes.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.causes.test.ts rename to packages/effect/test/arbitrary/Arbitrary.causes.test.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.composition.test.ts b/packages/effect/test/arbitrary/Arbitrary.composition.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.composition.test.ts rename to packages/effect/test/arbitrary/Arbitrary.composition.test.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.configuration.test.ts b/packages/effect/test/arbitrary/Arbitrary.configuration.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.configuration.test.ts rename to packages/effect/test/arbitrary/Arbitrary.configuration.test.ts diff --git a/repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts b/packages/effect/test/arbitrary/Arbitrary.test.ts similarity index 100% rename from repos/effect/packages/effect/test/arbitrary/Arbitrary.test.ts rename to packages/effect/test/arbitrary/Arbitrary.test.ts diff --git a/repos/effect/packages/effect/test/cli/Ansi.test.ts b/packages/effect/test/cli/Ansi.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Ansi.test.ts rename to packages/effect/test/cli/Ansi.test.ts diff --git a/repos/effect/packages/effect/test/cli/Arguments.test.ts b/packages/effect/test/cli/Arguments.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Arguments.test.ts rename to packages/effect/test/cli/Arguments.test.ts diff --git a/repos/effect/packages/effect/test/cli/Command.test.ts b/packages/effect/test/cli/Command.test.ts similarity index 95% rename from repos/effect/packages/effect/test/cli/Command.test.ts rename to packages/effect/test/cli/Command.test.ts index 377ec7420..667e88219 100644 --- a/repos/effect/packages/effect/test/cli/Command.test.ts +++ b/packages/effect/test/cli/Command.test.ts @@ -1,6 +1,6 @@ import { assert, describe, expect, it } from "@effect/vitest" import { Context, Effect, Fiber, FileSystem, Layer, Option, Path, Redacted, Runtime, Stdio } from "effect" -import { Argument, CliConfig, CliError, CliOutput, Command, Flag, GlobalFlag } from "effect/cli" +import { Argument, CliConfig, CliError, CliOutput, Command, Flag, GlobalFlag, Prompt } from "effect/cli" import { toImpl } from "effect/cli/internal/command" import { ChildProcessSpawner } from "effect/process" import { TestConsole } from "effect/testing" @@ -47,6 +47,21 @@ const TestLayerWithoutFormatter = Layer.mergeAll( ) describe("Command", () => { + it.effect("omits styles from wizard headings and command blocks when colors are disabled", () => + Effect.gen(function*() { + const command = Command.make("greet", { name: Flag.String("name") }, () => Effect.void) + + const fiber = yield* Command.runWith(command, { version: "1.0.0" })(["--wizard"]).pipe(Effect.forkChild) + yield* MockTerminal.inputText("Alice") + yield* MockTerminal.inputKey("enter") + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + + const logs = (yield* TestConsole.logLines).join("\n") + assert.include(logs, "$ greet --name Alice") + assert.notMatch(logs, new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`)) + }).pipe(Effect.provide([TestLayer, Layer.succeed(Prompt.Theme, Prompt.makeTheme({ colors: false }))]))) + describe("annotations", () => { it.effect("should expose annotations in help docs", () => Effect.gen(function*() { @@ -350,11 +365,23 @@ describe("Command", () => { assert.isFalse(invoked) assert.isTrue( stderr.some((line) => - String(line).includes("Missing value for flag --completions. Expected: bash | zsh | fish | sh") + String(line).includes( + "Missing value for flag --completions. Expected: bash | zsh | fish | powershell | sh | pwsh" + ) ) ) }).pipe(Effect.provide(TestLayer))) + it.effect("should print PowerShell completions for --completions pwsh", () => + Effect.gen(function*() { + const command = Command.make("demo") + + yield* Command.runWith(command, { version: "1.0.0" })(["--completions", "pwsh"]) + + const stdout = (yield* TestConsole.logLines).join("\n") + assert.include(stdout, "Register-ArgumentCompleter -Native -CommandName 'demo'") + }).pipe(Effect.provide(TestLayer))) + const missingValueCases: ReadonlyArray<{ readonly flag: Flag.Flag readonly name: string @@ -406,6 +433,62 @@ describe("Command", () => { ) }).pipe(Effect.provide(TestLayer))) + describe("withOptionalValue", () => { + const watchFlag = Flag.Literals("watch", ["first-failure", "until-done"]).pipe( + Flag.withAlias("w"), + Flag.withOptionalValue("first-failure") + ) + + it.effect("uses the optional value for a bare flag without consuming the next token", () => + Effect.gen(function*() { + const captured: Array = [] + const command = Command.make("app", { + watch: watchFlag, + files: Argument.String("file").pipe(Argument.variadic()) + }, (config) => Effect.sync(() => captured.push(config))) + const run = Command.runWith(command, { version: "1.0.0" }) + + yield* run(["-w", "until-done"]) + yield* run(["--watch=until-done"]) + + assert.deepStrictEqual(captured, [ + { watch: "first-failure", files: ["until-done"] }, + { watch: "until-done", files: [] } + ]) + }).pipe(Effect.provide(TestLayer))) + + it.effect("does not consume a boolean literal after a negated flag", () => + Effect.gen(function*() { + const captured: Array = [] + const command = Command.make("app", { + watch: Flag.Boolean("watch").pipe(Flag.withOptionalValue("true")), + files: Argument.String("file").pipe(Argument.variadic()) + }, (config) => Effect.sync(() => captured.push(config))) + + yield* Command.runWith(command, { version: "1.0.0" })(["--no-watch", "false"]) + + assert.deepStrictEqual(captured, [{ watch: false, files: ["false"] }]) + }).pipe(Effect.provide(TestLayer))) + + it.effect("emits the wizard value inline", () => + Effect.gen(function*() { + const captured: Array> = [] + const target = Command.make("app", { watch: watchFlag }) + const command = Command.make("launcher", {}, () => + Effect.flatMap(Command.wizard(target), (args) => + Effect.sync(() => { + captured.push(args) + }))) + + const fiber = yield* Command.runWith(command, { version: "1.0.0" })([]).pipe(Effect.forkChild) + yield* MockTerminal.inputKey("down") + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + + assert.deepStrictEqual(captured, [["app", "--watch=until-done"]]) + }).pipe(Effect.provide(TestLayer))) + }) + it.effect("should execute handler with parsed config", () => Effect.gen(function*() { const path = yield* Path.Path diff --git a/repos/effect/packages/effect/test/cli/Errors.test.ts b/packages/effect/test/cli/Errors.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Errors.test.ts rename to packages/effect/test/cli/Errors.test.ts diff --git a/repos/effect/packages/effect/test/cli/Help.test.ts b/packages/effect/test/cli/Help.test.ts similarity index 94% rename from repos/effect/packages/effect/test/cli/Help.test.ts rename to packages/effect/test/cli/Help.test.ts index 2d05ebb7f..989d3fade 100644 --- a/repos/effect/packages/effect/test/cli/Help.test.ts +++ b/packages/effect/test/cli/Help.test.ts @@ -38,6 +38,17 @@ const runCommand = Effect.fnUntraced( } ) +describe("CliOutput.displayWidth", () => { + it("measures terminal cells", () => { + assert.strictEqual(CliOutput.displayWidth(""), 0) + assert.strictEqual(CliOutput.displayWidth("file"), 4) + assert.strictEqual(CliOutput.displayWidth("ファイル"), 8) + assert.strictEqual(CliOutput.displayWidth("e\u0301"), 1) + assert.strictEqual(CliOutput.displayWidth("1️⃣"), 2) + assert.strictEqual(CliOutput.displayWidth("\u001B[31mファイル\u001B[0m"), 8) + }) +}) + describe("Command help output", () => { it("marks omittable flags as not required in structured help", () => { const command = Command.make("app", { @@ -50,6 +61,14 @@ describe("Command help output", () => { assert.deepStrictEqual(help.flags.map((flag) => flag.required), [true, false, false]) }) + it("renders the type of a flag with an optional value as optional", () => { + const command = Command.make("app", { + watch: Flag.Literals("watch", ["first-failure", "until-done"]).pipe(Flag.withOptionalValue("first-failure")) + }) + const help = toImpl(command).buildHelpDoc(["app"]) + assert.deepStrictEqual(help.flags.map((flag) => flag.type), ["[=choice]"]) + }) + it("marks omittable arguments as not required in structured help", () => { const requiredVariadic = Command.make("app", { files: Argument.String("files").pipe(Argument.variadic({ min: 1 })) @@ -91,7 +110,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none) SUBCOMMANDS @@ -341,7 +360,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none) EXAMPLES @@ -386,7 +405,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)" `) }).pipe(Effect.provide(TestLayer))) @@ -417,7 +436,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)" `) }).pipe(Effect.provide(TestLayer))) @@ -446,7 +465,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)" `) }).pipe(Effect.provide(TestLayer))) @@ -478,7 +497,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)" `) }).pipe(Effect.provide(TestLayer))) @@ -505,7 +524,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none) SUBCOMMANDS @@ -540,7 +559,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)" `) }).pipe(Effect.provide(TestLayer))) @@ -641,7 +660,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none) SUBCOMMANDS @@ -684,7 +703,7 @@ describe("Command help output", () => { --help, -h Show help information --version, -v Show version information --wizard Start wizard mode for a command - --completions Print shell completion script (choices: bash, zsh, fish, sh) + --completions Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh) --log-level Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none) SUBCOMMANDS diff --git a/repos/effect/packages/effect/test/cli/Lexer.test.ts b/packages/effect/test/cli/Lexer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Lexer.test.ts rename to packages/effect/test/cli/Lexer.test.ts diff --git a/repos/effect/packages/effect/test/cli/LogLevel.test.ts b/packages/effect/test/cli/LogLevel.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/LogLevel.test.ts rename to packages/effect/test/cli/LogLevel.test.ts diff --git a/repos/effect/packages/effect/test/cli/Param.test.ts b/packages/effect/test/cli/Param.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Param.test.ts rename to packages/effect/test/cli/Param.test.ts diff --git a/repos/effect/packages/effect/test/cli/Primitive.test.ts b/packages/effect/test/cli/Primitive.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/Primitive.test.ts rename to packages/effect/test/cli/Primitive.test.ts diff --git a/repos/effect/packages/effect/test/cli/Prompt.test.ts b/packages/effect/test/cli/Prompt.test.ts similarity index 95% rename from repos/effect/packages/effect/test/cli/Prompt.test.ts rename to packages/effect/test/cli/Prompt.test.ts index a4a5c917c..0ebf0ff4c 100644 --- a/repos/effect/packages/effect/test/cli/Prompt.test.ts +++ b/packages/effect/test/cli/Prompt.test.ts @@ -1,16 +1,35 @@ import { assert, describe, it } from "@effect/vitest" -import { Data, DateTime, Deferred, Effect, Fiber, FileSystem, Layer, Match, Path, Queue, Redacted } from "effect" +import { + Data, + DateTime, + Deferred, + Effect, + Fiber, + FileSystem, + Layer, + Match, + Path, + Queue, + Redacted, + Terminal +} from "effect" import { Prompt } from "effect/cli" +import { afterEach, vi } from "vitest" import * as MockTerminal from "./services/MockTerminal.ts" const FileSystemLayer = FileSystem.layerNoop({}) const PathLayer = Path.layer const TerminalLayer = MockTerminal.layer +// Styling assertions assume a color-capable terminal, independent of the test +// process environment. +const ThemeLayer = Layer.succeed(Prompt.Theme, Prompt.makeTheme({ colors: true })) + const TestLayer = Layer.mergeAll( FileSystemLayer, PathLayer, - TerminalLayer + TerminalLayer, + ThemeLayer ) const Action = Data.taggedEnum() @@ -49,6 +68,44 @@ const toRawFrames = (lines: ReadonlyArray) => const findFrame = (frames: ReadonlyArray, text: string) => frames.find((frame) => frame.includes(text)) +describe("Prompt colors", () => { + describe("makeTheme detection", { concurrent: false }, () => { + const isTTY = Object.getOwnPropertyDescriptor(process.stdout, "isTTY") + + afterEach(() => { + vi.unstubAllEnvs() + if (isTTY === undefined) { + delete (process.stdout as { isTTY?: boolean }).isTTY + } else { + Object.defineProperty(process.stdout, "isTTY", isTTY) + } + }) + + it("disables colors when NO_COLOR is set on a TTY", () => { + Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true }) + vi.stubEnv("NO_COLOR", "1") + + assert.isFalse(Prompt.makeTheme().colors) + }) + }) + + it.effect("omits styles when the context theme disables colors", () => + Effect.gen(function*() { + yield* MockTerminal.inputKey("enter") + yield* Prompt.run(Prompt.Select({ + message: "Choose a color", + choices: [{ title: "Blue", value: "blue" }] + })) + + const output = (yield* MockTerminal.displayLines).join("\n") + assert.include(output, "Choose a color") + assert.notMatch(output, new RegExp(`${escape}\\[[0-9;]*m`)) + }).pipe( + Effect.provideService(Prompt.Theme, Prompt.makeTheme({ colors: false })), + Effect.provide(TestLayer) + )) +}) + describe("Prompt.Date", () => { it.effect("renders two-digit years, teen ordinals, and noon meridiem correctly", () => Effect.gen(function*() { @@ -116,6 +173,31 @@ describe("Prompt.Date", () => { }).pipe(Effect.provide(TestLayer))) }) +describe("Prompt.succeed", () => { + it.effect("does not acquire terminal input", () => + Effect.gen(function*() { + const terminal = yield* Terminal.Terminal + let acquisitions = 0 + const result = yield* Prompt.run(Prompt.succeed("x")).pipe( + Effect.provideService( + Terminal.Terminal, + Terminal.make({ + ...terminal, + readInput: Effect.andThen( + Effect.sync(() => { + acquisitions++ + }), + terminal.readInput + ) + }) + ) + ) + + assert.strictEqual(result, "x") + assert.strictEqual(acquisitions, 0) + }).pipe(Effect.provide(TestLayer))) +}) + describe("Prompt.all", () => { it.effect("supports an empty record", () => Effect.gen(function*() { @@ -252,6 +334,7 @@ describe("Prompt.String", () => { Effect.provideService( Prompt.Theme, Prompt.makeTheme({ + colors: true, primaryColor: `${escape}[31m`, mutedColor: `${escape}[34m`, successColor: `${escape}[33m`, diff --git a/repos/effect/packages/effect/test/cli/completions/completions.test.ts b/packages/effect/test/cli/completions/completions.test.ts similarity index 84% rename from repos/effect/packages/effect/test/cli/completions/completions.test.ts rename to packages/effect/test/cli/completions/completions.test.ts index d48ba776a..c03a32536 100644 --- a/repos/effect/packages/effect/test/cli/completions/completions.test.ts +++ b/packages/effect/test/cli/completions/completions.test.ts @@ -4,6 +4,7 @@ import * as Completions from "effect/cli/Completions" import * as Bash from "effect/cli/internal/completions/bash" import { fromCommand } from "effect/cli/internal/completions/descriptor" import * as Fish from "effect/cli/internal/completions/fish" +import * as PowerShell from "effect/cli/internal/completions/powershell" import * as Zsh from "effect/cli/internal/completions/zsh" import { ComprehensiveCli } from "../fixtures/ComprehensiveCli.ts" @@ -124,6 +125,14 @@ const emptyCmd = Command.make("noop").pipe( Command.withDescription("Does nothing") ) +const leaf = (name: string, flag: string): Completions.CommandDescriptor => ({ + name, + description: undefined, + flags: [{ name: flag, aliases: [], description: undefined, type: { _tag: "Boolean" } }], + arguments: [], + subcommands: [] +}) + const choicesHelperSource = (script: string): string => { const start = script.indexOf("_deploy--choices()") const end = script.indexOf("\n}\n", start) @@ -465,6 +474,14 @@ describe("Zsh completions", () => { ) }) + it("escapes a closing bracket in flag descriptions", () => { + const cmd = Command.make("deploy", { + level: Flag.Literals("level", ["debug", "info"]).pipe(Flag.withDescription("Log level [info]")) + }) + const script = Zsh.generate("deploy", fromCommand(cmd)) + assert.include(script, `'(--level)--level[Log level [info\\]]:value:(debug info)'`) + }) + it("uses alternative argument sets for positional arguments and subcommands", () => { const desc = fromCommand(withOptionalDirectoryAndSubcommands) const script = Zsh.generate("example", desc) @@ -550,13 +567,6 @@ describe("Zsh completions", () => { describe("Fish completions", () => { it("scopes nested completions by the full command path", () => { - const leaf = (name: string, flag: string): Completions.CommandDescriptor => ({ - name, - description: undefined, - flags: [{ name: flag, aliases: [], description: undefined, type: { _tag: "Boolean" } }], - arguments: [], - subcommands: [] - }) const descriptor: Completions.CommandDescriptor = { name: "tool", description: undefined, @@ -782,6 +792,103 @@ describe("Fish completions", () => { }) }) +// --------------------------------------------------------------------------- +// PowerShell completions +// --------------------------------------------------------------------------- + +describe("PowerShell completions", () => { + it("registers a native completer with a context per subcommand path", () => { + const script = PowerShell.generate("top", fromCommand(nested3Levels)) + assert.include(script, `Register-ArgumentCompleter -Native -CommandName 'top'`) + assert.include(script, `['sub'] = @{`) + assert.include(script, `['sub action'] = @{`) + assert.include(script, `@{ name = 'action'; description = 'Perform action' }`) + }) + + it("emits flag forms, choice values and path types", () => { + const simple = PowerShell.generate("greet", fromCommand(simpleCmd)) + assert.include( + linesWith(simple, "name = 'loud'"), + `forms = @('--loud', '-l'); takesValue = $false` + ) + assert.include(linesWith(simple, "name = 'times'"), `forms = @('--times'); takesValue = $true`) + + const choices = PowerShell.generate("deploy", fromCommand(withChoices)) + assert.include(choices, `values = @('dev', 'staging', 'prod')`) + assert.include(choices, `values = @('us-east', 'eu-west', 'ap-south')`) + + const paths = PowerShell.generate("process", fromCommand(withPaths)) + assert.include(paths, `pathType = 'file'`) + assert.include(paths, `pathType = 'directory'`) + assert.include(paths, `[System.Management.Automation.CompletionCompleters]::CompleteFilename($typed)`) + assert.include(paths, `$entry.pathType -eq 'directory' -and $file.ResultType -ne 'ProviderContainer'`) + }) + + it("offers only positional values after --", () => { + const script = PowerShell.generate("server", fromCommand(withSubcommands)) + assert.include(script, `if ($endOfOptions) { $position++; continue }`) + assert.include(script, `if ($word -ceq '--') { $endOfOptions = $true; continue }`) + assert.include(script, `if (-not $endOfOptions) {`) + }) + + it("does not treat an option as a pending flag value", () => { + const script = PowerShell.generate("server", fromCommand(withSubcommands)) + assert.match(script, /if \(\$flag\.takesValue\) \{\s+if \(-not \$isOption\) \{ continue \}/) + assert.include(script, "if ($null -ne $pending -and $pending.takesValue -and -not $typedIsOption) {") + }) + + it("completes a negative number as a value, not a flag", () => { + const script = PowerShell.generate("server", fromCommand(withSubcommands)) + assert.notInclude(script, "$typed.StartsWith('-')") + assert.include(script, "if ($typedIsOption) {") + }) + + it("offers option-shaped choices only where the CLI reads them as values", () => { + const script = PowerShell.generate("server", fromCommand(withSubcommands)) + assert.include(script, "& $optionLike $value") + }) + + it("tracks used flags case-sensitively", () => { + const script = PowerShell.generate("server", fromCommand(withSubcommands)) + assert.notInclude(script, "$used = @{}") + assert.include(script, "$used = [hashtable]::new([System.StringComparer]::Ordinal)") + }) + + it("emits non-ASCII text as [char] code units", () => { + const script = PowerShell.generate("deploy", fromCommand(withTrickyChoices)) + assert.notMatch(script, /[^\n\x20-\x7e]/) + assert.include(script, `('a' + [char]0xD83D + [char]0xDE00 + 'b')`) + }) + + it("preserves line breaks in choice values", () => { + const cmd = Command.make("tool", { mode: Flag.Literals("mode", ["line\nbreak"]) }) + const script = PowerShell.generate("tool", fromCommand(cmd)) + assert.include(script, `values = @(('line' + [char]0x000A + 'break'))`) + }) + + it("escapes values for single-quoted PowerShell literals", () => { + const script = PowerShell.generate("deploy", fromCommand(withTrickyChoices)) + assert.include(script, `'it''s-fine'`) + assert.include(script, `'foo'''`) + assert.include(script, `'o''clock'`) + assert.include(script, `'$HOME'`) + }) + + it("keeps case-distinct subcommand contexts and emits a repeated path once", () => { + const script = PowerShell.generate("tool", { + name: "tool", + description: undefined, + flags: [], + arguments: [], + subcommands: [leaf("config", "lower"), leaf("config", "lower"), leaf("Config", "upper")] + }) + assert.lengthOf(script.match(/\['config'\] = @\{/g) ?? [], 1) + assert.lengthOf(script.match(/\['Config'\] = @\{/g) ?? [], 1) + assert.lengthOf(script.match(/forms = @\('--lower'\)/g) ?? [], 1) + assert.lengthOf(script.match(/forms = @\('--upper'\)/g) ?? [], 1) + }) +}) + // --------------------------------------------------------------------------- // Completions dispatcher // --------------------------------------------------------------------------- @@ -804,6 +911,12 @@ describe("Completions", () => { const script = Completions.generate("greet", "fish", desc) assert.include(script, "complete -c greet") }) + + it("dispatches to powershell generator", () => { + const desc = fromCommand(simpleCmd) + const script = Completions.generate("greet", "powershell", desc) + assert.include(script, `Register-ArgumentCompleter -Native -CommandName 'greet'`) + }) }) // --------------------------------------------------------------------------- @@ -913,3 +1026,26 @@ describe("Completions integration", () => { assert.include(script, "__fish_seen_subcommand_from git") }) }) + +describe("Flags with an optional value", () => { + it("do not take the next word as the flag value", () => { + const descriptor = fromCommand(Command.make("app", { + watch: Flag.Literals("watch", ["first-failure", "until-done"]).pipe( + Flag.withAlias("w"), + Flag.withDescription("Watch mode"), + Flag.withOptionalValue("first-failure") + ), + file: Argument.Literals("file", ["one", "two"]) + })) + + const bash = Bash.generate("app", descriptor) + assert.notInclude(bash, "--watch|-w)\n") + assert.include(bash, "--watch|-w) ;;") + assert.include( + Zsh.generate("app", descriptor), + `'(--watch -w)--watch=-[Watch mode]::value:(first-failure until-done)'` + ) + assert.notInclude(Fish.generate("app", descriptor), "-a 'first-failure until-done'") + assert.include(PowerShell.generate("app", descriptor), "optionalValue = $true;") + }) +}) diff --git a/repos/effect/packages/effect/test/cli/completions/descriptor.test.ts b/packages/effect/test/cli/completions/descriptor.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/completions/descriptor.test.ts rename to packages/effect/test/cli/completions/descriptor.test.ts diff --git a/repos/effect/packages/effect/test/cli/fixtures/ComprehensiveCli.ts b/packages/effect/test/cli/fixtures/ComprehensiveCli.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/fixtures/ComprehensiveCli.ts rename to packages/effect/test/cli/fixtures/ComprehensiveCli.ts diff --git a/repos/effect/packages/effect/test/cli/services/MockTerminal.ts b/packages/effect/test/cli/services/MockTerminal.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/services/MockTerminal.ts rename to packages/effect/test/cli/services/MockTerminal.ts diff --git a/repos/effect/packages/effect/test/cli/services/TestActions.ts b/packages/effect/test/cli/services/TestActions.ts similarity index 100% rename from repos/effect/packages/effect/test/cli/services/TestActions.ts rename to packages/effect/test/cli/services/TestActions.ts diff --git a/repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts b/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts similarity index 78% rename from repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts rename to packages/effect/test/cluster/ClusterWorkflowEngine.test.ts index 69dd94828..f1d2792ac 100644 --- a/repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts +++ b/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts @@ -12,6 +12,7 @@ import { Logger, Option, Result, + Schedule, Schema, Scope, Tracer @@ -21,6 +22,9 @@ import { ClusterSchema, ClusterWorkflowEngine, Entity, + EntityAddress, + EntityId, + EntityType, MessageStorage, RunnerHealth, Runners, @@ -40,6 +44,188 @@ import { } from "effect/workflow/WorkflowEngine" describe.concurrent("ClusterWorkflowEngine", () => { + it.effect("wakes an activity deferred completed before a parallel sibling lets the workflow suspend", () => + Effect.gen(function*() { + const gate = DurableDeferred.make("EarlyActivityWake/Gate", { success: Schema.String }) + const workflow = Workflow.make("EarlyActivityWake", { + payload: {}, + success: Schema.Array(Schema.String), + idempotencyKey: () => "one" + }) + const siblingStarted = yield* Latch.make() + const releaseSibling = yield* Latch.make() + const activityParked = yield* Latch.make() + const awaitGate = Activity.make({ + name: "await-gate", + success: Schema.String, + execute: siblingStarted.await.pipe( + Effect.andThen(DurableDeferred.await(gate)), + Effect.onInterrupt(() => activityParked.open) + ) + }) + const block = Activity.make({ + name: "block", + success: Schema.String, + execute: siblingStarted.open.pipe(Effect.andThen(releaseSibling.await), Effect.as("blocked")) + }) + let activations = 0 + const context = yield* Layer.build( + workflow.toLayer(() => { + activations++ + // Yield the activities themselves so both run through the workflow engine. + return Effect.all([awaitGate, block], { concurrency: "unbounded" }) + }).pipe(Layer.provideMerge(makeTestWorkflowEngine())) + ) + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + const executionId = yield* workflow.execute({}, { discard: true }) + yield* activityParked.await + for (let i = 0; i < 5; i++) { + yield* TestClock.adjust(1) + yield* sharding.pollStorage + } + assert.isTrue(Option.isNone(yield* workflow.poll(executionId))) + const completion = yield* DurableDeferred.succeed(gate, { + token: DurableDeferred.tokenFromExecutionId(gate, { workflow, executionId }), + value: "done" + }).pipe(Effect.forkChild({ startImmediately: true })) + // Deliver the callback while the sibling still prevents the parent from suspending. + for (let i = 0; i < 5; i++) { + yield* TestClock.adjust(1) + yield* sharding.pollStorage + } + assert.isTrue(Option.isNone(yield* workflow.poll(executionId))) + yield* releaseSibling.open + assert.deepStrictEqual( + yield* pollUntil(workflow, executionId, "Complete"), + new Workflow.Complete({ exit: Exit.succeed(["done", "blocked"]) }) + ) + yield* Fiber.join(completion) + assert.strictEqual(activations, 2) + }).pipe(Effect.provide(context)) + })) + + it.effect("answers an orphaned activity after restart even when replay never registers it", () => + Effect.gen(function*() { + const release = DurableDeferred.make("OrphanActivity/Release") + const workflow = Workflow.make("OrphanActivity", { + payload: {}, + success: Schema.Void, + idempotencyKey: () => "one" + }) + const storage = yield* makeSharedStorage + const storageLayer = Layer.succeed(MessageStorage.MessageStorage, storage) + const layer = (crash = false) => + workflow.toLayer(() => DurableDeferred.await(release)).pipe( + Layer.provideMerge(makeTestWorkflowEngine({ + storageLayer: crash ? unansweredActivityStorage(storage) : storageLayer + })) + ) + const executionId = yield* workflow.executionId({}) + const requestId = yield* Effect.gen(function*() { + yield* workflow.execute({}, { discard: true }) + yield* pollUntil(workflow, executionId, "Suspended") + // Crash residue: the losing request is durable, but its Interrupt is absent. + return yield* sendUnregisteredActivity(workflow._tag, executionId) + }).pipe(Effect.provide(layer(true))) + assert.deepStrictEqual(yield* storage.repliesForUnfiltered([requestId]), []) + + yield* Effect.gen(function*() { + yield* (yield* Sharding.Sharding).pollStorage + assert.deepStrictEqual(yield* activityReply(storage, requestId), Exit.succeed(new Workflow.Suspended({}))) + yield* DurableDeferred.succeed(release, { + token: DurableDeferred.tokenFromExecutionId(release, { workflow, executionId }), + value: undefined + }) + assert.deepStrictEqual( + yield* pollUntil(workflow, executionId, "Complete"), + new Workflow.Complete({ exit: Exit.void }) + ) + assert.deepStrictEqual(yield* activityReply(storage, requestId), Exit.succeed(new Workflow.Suspended({}))) + }).pipe(Effect.provide(layer())) + })) + + it.effect("retries an early activity registered while its Suspended reply is still being persisted", () => + Effect.gen(function*() { + const workflow = Workflow.make("EarlyActivity", { + payload: {}, + success: Schema.Number, + idempotencyKey: () => "one" + }) + const register = yield* Latch.make() + const savingReply = yield* Latch.make() + const releaseReply = yield* Latch.make() + const registrationRequested = yield* Latch.make() + let executions = 0 + let activityRequests = 0 + const activity = Activity.make({ + name: "loser", + success: Schema.Number, + execute: Effect.sync(() => ++executions) + }) + const storage = yield* makeSharedStorage + const storageLayer = Layer.succeed(MessageStorage.MessageStorage, { + ...storage, + saveReply: (reply) => { + if (reply.rpc._tag === "activity" && !savingReply.isOpen()) { + assert(reply.reply._tag === "WithExit") + assert(reply.reply.exit._tag === "Success") + assert(Schema.is(Workflow.Suspended)(reply.reply.exit.value)) + return savingReply.open.pipe( + Effect.andThen(releaseReply.await), + Effect.andThen(storage.saveReply(reply)) + ) + } + return storage.saveReply(reply) + }, + saveRequest: (request) => + storage.saveRequest(request).pipe( + Effect.tap(() => { + if (request.rpc._tag !== "activity") return Effect.void + // Bound a broken reset/resend loop so this regression fails instead of exhausting memory. + assert.isAtMost(++activityRequests, 20, "activity reset/resend must not livelock on Suspended") + return registrationRequested.open + }) + ) + }) + const executionId = yield* workflow.executionId({}) + const requestId = yield* sendUnregisteredActivity(workflow._tag, executionId).pipe(Effect.provide( + workflow.toLayer(() => activity).pipe(Layer.provideMerge(makeTestWorkflowEngine({ + storageLayer: unansweredActivityStorage(storage) + }))) + )) + assert.deepStrictEqual(yield* storage.repliesForUnfiltered([requestId]), []) + yield* Effect.gen(function*() { + yield* workflow.execute({}, { discard: true }) + yield* (yield* Sharding.Sharding).pollStorage + for (let i = 0; i < 200 && !savingReply.isOpen(); i++) yield* TestClock.adjust(1) + assert.isTrue(savingReply.isOpen()) + assert.deepStrictEqual(yield* storage.repliesForUnfiltered([requestId]), []) + assert.strictEqual(executions, 0) + yield* register.open + // The replay has registered and requested the same activity before the early reply commits. + for (let i = 0; i < 200 && !registrationRequested.isOpen(); i++) yield* TestClock.adjust(1) + assert.isTrue(registrationRequested.isOpen()) + assert.deepStrictEqual(yield* storage.repliesForUnfiltered([requestId]), []) + assert.strictEqual(executions, 0) + yield* releaseReply.open + assert.deepStrictEqual( + yield* pollUntil(workflow, executionId, "Complete"), + new Workflow.Complete({ exit: Exit.succeed(1) }) + ) + assert.strictEqual(executions, 1) + // Registration must reset the stored Suspended reply, not allocate a new attempt. + assert.deepStrictEqual( + yield* activityReply(storage, requestId), + Exit.succeed(new Workflow.Complete({ exit: Exit.succeed(1) })) + ) + }).pipe(Effect.provide( + workflow.toLayer(() => register.await.pipe(Effect.andThen(activity))).pipe( + Layer.provideMerge(makeTestWorkflowEngine({ storageLayer })) + ) + )) + })) + it.effect("does not replay for an unawaited deferred checkpoint", () => Effect.gen(function*() { const checkpoint = DurableDeferred.make("UnawaitedCheckpoint/Checkpoint") @@ -79,6 +265,74 @@ describe.concurrent("ClusterWorkflowEngine", () => { }).pipe(Effect.provide(context)) })) + it.effect("advances a durable race continuation while a sibling is parked", () => + expectContinuesBesideParkedSibling( + "ParkedSiblingRace", + DurableDeferred.raceAll({ + name: "race", + success: Schema.String, + error: Schema.Never, + effects: [ + Workflow.wrapActivityResult(Effect.as(Effect.sleep(1000), "timer"), () => false), + DurableDeferred.await(DurableDeferred.make("ParkedSiblingRace/Cancel")).pipe(Effect.as("cancel")) + ] + }) + ), 20_000) + + it.effect( + "advances a DurableDeferred.into continuation while a sibling is parked", + () => + expectContinuesBesideParkedSibling( + "ParkedSiblingInto", + // Not an activity: `into` itself keeps the run active until the exit is recorded. + Effect.as(Effect.sleep(1000), "timer").pipe( + DurableDeferred.into(DurableDeferred.make("ParkedSiblingInto/Result", { success: Schema.String })) + ) + ), + 20_000 + ) + + it.effect( + "lets an active sibling finish before a DurableDeferred.into body parks the run", + () => + Effect.gen(function*() { + const gate = DurableDeferred.make("ActiveSiblingInto/Gate", { success: Schema.String }) + const result = DurableDeferred.make("ActiveSiblingInto/Result", { success: Schema.String }) + const workflow = Workflow.make("ActiveSiblingInto", { + payload: {}, + success: Schema.String, + idempotencyKey: () => "one" + }) + const nextStepRuns: Array = [] + const context = yield* Layer.build( + workflow.toLayer(() => + Effect.all([ + Workflow.wrapActivityResult(Effect.sleep(100), () => false).pipe( + Effect.andThen(nextStepActivity(nextStepRuns, "sibling")) + ), + DurableDeferred.await(gate).pipe(DurableDeferred.into(result)) + ], { concurrency: "unbounded" }).pipe(Effect.map(([sibling, value]) => `${sibling}:${value}`)) + ).pipe(Layer.provideMerge(makeTestWorkflowEngine())) + ) + yield* Effect.gen(function*() { + const executionId = yield* workflow.execute({}, { discard: true }) + // The parked body must not suspend the run while the sibling is counted. + yield* pollUntil(workflow, executionId, "Suspended") + assert.deepStrictEqual(nextStepRuns, ["sibling"]) + yield* DurableDeferred.succeed(gate, { + token: DurableDeferred.tokenFromExecutionId(gate, { workflow, executionId }), + value: "gate" + }) + assert.deepStrictEqual( + yield* pollUntil(workflow, executionId, "Complete"), + new Workflow.Complete({ exit: Exit.succeed("sibling:next:gate") }) + ) + assert.deepStrictEqual(nextStepRuns, ["sibling"]) + }).pipe(Effect.provide(context)) + }), + 20_000 + ) + it.effect("retries a deferred wake after a transient run reset failure", () => Effect.gen(function*() { const gate = DurableDeferred.make("ResetRetry/Gate", { success: Schema.String }) @@ -478,6 +732,84 @@ describe.concurrent("ClusterWorkflowEngine", () => { }), 30_000) } + it.effect("wakes a suspended caller when its workflow completes", () => + Effect.gen(function*() { + const retrying = yield* Latch.make() + const gate = DurableDeferred.make("CompletionWake/Gate", { success: Schema.String }) + const workflow = Workflow.make("CompletionWake", { + payload: {}, + success: Schema.String, + idempotencyKey: () => "one", + // The retry stays the fallback, but is too slow to deliver within the test. + suspendedRetrySchedule: Schedule.spaced("1 hour").pipe(Schedule.tap(() => retrying.open)) + }) + const context = yield* Layer.build( + workflow.toLayer(() => DurableDeferred.await(gate)).pipe(Layer.provideMerge(makeTestWorkflowEngine())) + ) + yield* Effect.gen(function*() { + const executionId = yield* workflow.executionId({}) + const fiber = yield* workflow.execute({}).pipe(Effect.forkChild({ startImmediately: true })) + yield* pollUntil(workflow, executionId, "Suspended") + yield* advanceUntil(() => retrying.isOpen(), "caller must wait on its retry schedule") + yield* DurableDeferred.succeed(gate, { + token: DurableDeferred.tokenFromExecutionId(gate, { workflow, executionId }), + value: "done" + }) + yield* pollUntil(workflow, executionId, "Complete") + yield* advanceUntil(() => fiber.pollUnsafe() !== undefined, "completion must wake the caller", 1, 1000) + assert.strictEqual(yield* Fiber.join(fiber), "done") + }).pipe(Effect.provide(context)) + })) + + it.effect("delivers a completion through the retry schedule when the wake is lost", () => + Effect.gen(function*() { + let dropWakes = false + let handlers = 0 + const gate = DurableDeferred.make("LostWake/Gate", { success: Schema.String }) + const workflow = Workflow.make("LostWake", { + payload: {}, + success: Schema.String, + idempotencyKey: () => "one", + // Once the caller first waits, no reply notification reaches it. + suspendedRetrySchedule: Schedule.spaced("10 seconds").pipe(Schedule.tap(() => + Effect.sync(() => { + dropWakes = true + }) + )) + }) + const storageLayer = Layer.effect( + MessageStorage.MessageStorage, + Effect.map(Effect.service(MessageStorage.MessageStorage), (storage) => ({ + ...storage, + registerReplyHandler: (message) => + Effect.suspend(() => { + handlers++ + return dropWakes ? Effect.never : storage.registerReplyHandler(message) + }).pipe(Effect.ensuring(Effect.sync(() => handlers--))) + })) + ).pipe(Layer.provide(MessageStorage.layerMemory)) + const context = yield* Layer.build( + workflow.toLayer(() => DurableDeferred.await(gate)).pipe( + Layer.provideMerge(makeTestWorkflowEngine({ storageLayer })) + ) + ) + yield* Effect.gen(function*() { + const executionId = yield* workflow.executionId({}) + const fiber = yield* workflow.execute({}).pipe(Effect.forkChild({ startImmediately: true })) + yield* pollUntil(workflow, executionId, "Suspended") + yield* advanceUntil(() => dropWakes, "caller must wait on its retry schedule") + yield* DurableDeferred.succeed(gate, { + token: DurableDeferred.tokenFromExecutionId(gate, { workflow, executionId }), + value: "done" + }) + yield* pollUntil(workflow, executionId, "Complete") + assert.isUndefined(fiber.pollUnsafe(), "a lost wake must leave the caller on its schedule") + yield* advanceUntil(() => fiber.pollUnsafe() !== undefined, "the schedule must deliver the completion", 1000) + assert.strictEqual(yield* Fiber.join(fiber), "done") + assert.strictEqual(handlers, 0, "the caller must release its reply handlers") + }).pipe(Effect.provide(context)) + })) + it.effect("executes, resumes, deduplicates, and polls a suspended workflow", () => Effect.gen(function*() { const sharding = yield* Sharding.Sharding @@ -742,7 +1074,7 @@ describe.concurrent("ClusterWorkflowEngine", () => { ) it.effect( - "DurableDeferred.raceAll replays the run when a losing deferred completes late", + "DurableDeferred.raceAll does not replay the run when a losing deferred completes late", () => Effect.gen(function*() { const flags = yield* Flags @@ -769,8 +1101,8 @@ describe.concurrent("ClusterWorkflowEngine", () => { } expect(yield* Fiber.join(fiber)).toEqual("activity:tail") - // The late completion preempts the tail; the replay re-executes it. - expect(flags.get("losing-deferred-tail-runs")).toEqual(2) + // The race already settled, so the late completion must not preempt the tail. + expect(flags.get("losing-deferred-tail-runs")).toEqual(1) }).pipe(Effect.provide(TestWorkflowLayer)), 20_000 ) @@ -1337,6 +1669,227 @@ describe.concurrent("ClusterWorkflowEngine", () => { }).pipe(Effect.provide(Layer.mergeAll(ParentLayer, ChildLayer).pipe(Layer.provideMerge(TestWorkflowLayer)))) })) + it.effect("keeps a failure-suspended workflow parked when a discarded child completes", () => + Effect.gen(function*() { + let failures = 0 + const ChildGate = DurableDeferred.make("FailureParkedChildGate") + const Parent = Workflow.make("FailureParked", { + payload: {}, + idempotencyKey: () => "parent" + }).annotate(Workflow.SuspendOnFailure, true) + const Child = Workflow.make("FailureParkedChild", { + payload: {}, + idempotencyKey: () => "child" + }) + const ParentLayer = Parent.toLayer(Effect.fnUntraced(function*() { + yield* Child.execute({}, { discard: true }) + yield* Activity.make({ + name: "fail", + execute: Effect.suspend(() => { + failures++ + return Effect.die("boom") + }) + }) + })) + const ChildLayer = Child.toLayer(() => DurableDeferred.await(ChildGate)) + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + const settle = Effect.gen(function*() { + for (let i = 0; i < 20; i++) { + yield* TestClock.adjust(1) + yield* sharding.pollStorage + } + }) + yield* TestClock.adjust(1) + const executionId = yield* Parent.execute({}, { discard: true }) + const childId = yield* Child.executionId({}) + yield* pollUntil(Parent, executionId, "Suspended") + + yield* DurableDeferred.succeed(ChildGate, { + token: DurableDeferred.tokenFromExecutionId(ChildGate, { workflow: Child, executionId: childId }), + value: void 0 + }) + yield* pollUntil(Child, childId, "Complete") + yield* settle + assert.strictEqual(failures, 1, "only an explicit resume may replay the failed activity") + + yield* Parent.resume(executionId) + yield* settle + assert.strictEqual(failures, 2) + }).pipe(Effect.provide(Layer.mergeAll(ParentLayer, ChildLayer).pipe(Layer.provideMerge(TestWorkflowLayer)))) + })) + + it.effect("persists a detached child's parent without waking it", () => + Effect.gen(function*() { + let parentRuns = 0 + const ParentGate = DurableDeferred.make("DetachedParentGate") + const ChildGate = DurableDeferred.make("DetachedChildGate") + const Parent = Workflow.make("DetachedParent", { + payload: {}, + idempotencyKey: () => "parent" + }) + const Child = Workflow.make("DetachedChild", { + payload: {}, + idempotencyKey: () => "child" + }) + const ParentLayer = Parent.toLayer(Effect.fnUntraced(function*() { + parentRuns++ + yield* Child.execute({}, { discard: true, detached: true }) + yield* DurableDeferred.await(ParentGate) + })) + const ChildLayer = Child.toLayer(() => DurableDeferred.await(ChildGate)) + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + const driver = yield* MessageStorage.MemoryDriver + yield* TestClock.adjust(1) + const executionId = yield* Parent.execute({}, { discard: true }) + const childId = yield* Child.executionId({}) + yield* pollUntil(Parent, executionId, "Suspended") + assert.strictEqual(parentRuns, 1) + + // The parent stays recorded on the child for observability. + const childRun = Array.from(driver.requests.values()).find(({ envelope }) => + envelope._tag === "Request" && envelope.tag === "run" && + envelope.address.entityType === `Workflow/${Child._tag}` + ) + assert(childRun && childRun.envelope._tag === "Request") + assert.deepStrictEqual( + (childRun.envelope.payload as Record)[ + "~effect/cluster/ClusterWorkflowEngine/payloadParentKey" + ], + { workflowName: Parent._tag, executionId, detached: true } + ) + + yield* DurableDeferred.succeed(ChildGate, { + token: DurableDeferred.tokenFromExecutionId(ChildGate, { workflow: Child, executionId: childId }), + value: void 0 + }) + yield* pollUntil(Child, childId, "Complete") + for (let i = 0; i < 20; i++) { + yield* TestClock.adjust(1) + yield* sharding.pollStorage + } + assert.strictEqual(parentRuns, 1, "a detached child must not wake its parent") + }).pipe(Effect.provide(Layer.mergeAll(ParentLayer, ChildLayer).pipe(Layer.provideMerge(TestWorkflowLayer)))) + })) + + it.effect("interrupts a failure-suspended workflow", () => + Effect.gen(function*() { + let attempts = 0 + const Failing = Workflow.make("FailureInterrupted", { + payload: {}, + idempotencyKey: () => "failing" + }).annotate(Workflow.SuspendOnFailure, true) + const FailingLayer = Failing.toLayer(() => + Activity.make({ + name: "fail", + execute: Effect.suspend(() => { + attempts++ + return Effect.die("boom") + }) + }) + ) + yield* Effect.gen(function*() { + yield* TestClock.adjust(1) + const executionId = yield* Failing.execute({}, { discard: true }) + yield* pollUntil(Failing, executionId, "Suspended") + yield* Failing.interrupt(executionId) + const result = yield* pollUntil(Failing, executionId, "Complete") + assert(result._tag === "Complete" && Exit.hasInterrupts(result.exit)) + assert.strictEqual(attempts, 1, "interrupt must not re-run the failed activity") + }).pipe(Effect.provide(FailingLayer.pipe(Layer.provideMerge(TestWorkflowLayer)))) + })) + + it.effect("interrupts a workflow parked inside an activity", () => + Effect.gen(function*() { + const Gate = DurableDeferred.make("ActivityParkedGate") + const Parent = Workflow.make("ActivityParkedParent", { + payload: {}, + idempotencyKey: () => "parent" + }) + const Child = Workflow.make("ActivityParkedChild", { + payload: {}, + idempotencyKey: () => "child" + }) + // The activity's stored reply is `Suspended` while it awaits the child. + const ParentLayer = Parent.toLayer(() => Activity.make({ name: "await-child", execute: Child.execute({}) })) + const ChildLayer = Child.toLayer(() => DurableDeferred.await(Gate)) + yield* Effect.gen(function*() { + yield* TestClock.adjust(1) + const executionId = yield* Parent.execute({}, { discard: true }) + yield* pollUntil(Parent, executionId, "Suspended") + yield* Parent.interrupt(executionId) + const result = yield* pollUntil(Parent, executionId, "Complete") + assert(result._tag === "Complete" && Exit.hasInterrupts(result.exit)) + }).pipe(Effect.provide(Layer.mergeAll(ParentLayer, ChildLayer).pipe(Layer.provideMerge(TestWorkflowLayer)))) + })) + + it.effect("applies a persisted interrupt when a fresh owner replays a failure-suspended workflow", () => + Effect.gen(function*() { + let attempts = 0 + const Failing = Workflow.make("FailureInterruptedRecovery", { + payload: {}, + idempotencyKey: () => "failing" + }).annotate(Workflow.SuspendOnFailure, true) + const storage = yield* makeSharedStorage + // The first owner records the interrupt and resets the run, but crashes + // before the reset run is redelivered. + let crashed = false + const crashingStorage = Layer.succeed(MessageStorage.MessageStorage, { + ...storage, + unprocessedMessages: (shardIds, options) => + Effect.map( + storage.unprocessedMessages(shardIds, options), + (messages) => + crashed + ? messages.filter((message) => message.envelope._tag !== "Request" || message.envelope.tag !== "run") + : messages + ) + }) + const layer = (storageLayer: Layer.Layer) => + Failing.toLayer(() => + Activity.make({ + name: "flaky", + // A retry would succeed, so a replay that runs it completes normally. + execute: Effect.suspend(() => ++attempts === 1 ? Effect.die("boom") : Effect.void) + }) + ).pipe(Layer.provideMerge(makeTestWorkflowEngine({ storageLayer }))) + const executionId = yield* Failing.executionId({}) + + yield* Effect.gen(function*() { + yield* TestClock.adjust(1) + yield* Failing.execute({}, { discard: true }) + yield* pollUntil(Failing, executionId, "Suspended") + crashed = true + yield* Failing.interrupt(executionId) + let result = yield* Failing.poll(executionId) + for (let i = 0; i < 2000 && Option.isSome(result); i++) { + yield* TestClock.adjust(1) + result = yield* Failing.poll(executionId) + } + assert(Option.isNone(result), "the interrupt must reset the run") + }).pipe(Effect.provide(layer(crashingStorage))) + + // The fresh owner's first read of the persisted interrupt fails once. + let lookupFailed = false + const flakyStorage = Layer.succeed(MessageStorage.MessageStorage, { + ...storage, + requestIdForPrimaryKey: (options) => { + if (!lookupFailed && options.tag === "deferred" && options.id === "Workflow/InterruptSignal") { + lookupFailed = true + return Effect.fail(new ClusterError.PersistenceError({ cause: "transient interrupt lookup failure" })) + } + return storage.requestIdForPrimaryKey(options) + } + }) + yield* Effect.gen(function*() { + const result = yield* pollUntil(Failing, executionId, "Complete") + assert(lookupFailed, "the interrupt lookup must fail once") + assert(result._tag === "Complete" && Exit.hasInterrupts(result.exit), "the replay must complete as interrupted") + assert.strictEqual(attempts, 1, "a fresh owner must not re-run the failed activity") + }).pipe(Effect.provide(layer(flakyStorage))) + })) + it.effect("coalesces parked child wakeups while parent cleanup is pending", () => Effect.gen(function*() { const cleaningUp = yield* Latch.make() @@ -1863,6 +2416,59 @@ describe.concurrent("ClusterWorkflowEngine", () => { }).pipe(Effect.provide(TestWorkflowLayer))) }) +const RecoveryActivityRpc = Rpc.make("activity", { + payload: { name: Schema.String, attempt: Schema.Int, withTransaction: Schema.Boolean }, + primaryKey: ({ name, attempt }) => `${name}/${attempt}`, + success: Workflow.Result({ success: Schema.Any, error: Schema.Any }) +}).annotate(ClusterSchema.Persisted, true) + +// Shared across runners so a later runner sees the previous runner's residue. +const makeSharedStorage = Effect.map( + Layer.build(MessageStorage.layerMemory.pipe(Layer.provide(ShardingConfig.layerDefaults))), + (context) => Context.get(context, MessageStorage.MessageStorage) +) + +// Preserve the unanswered request across runner teardown, even once recovery is fixed. +const unansweredActivityStorage = (storage: MessageStorage.MessageStorage["Service"]) => + Layer.succeed(MessageStorage.MessageStorage, { + ...storage, + saveReply: (reply) => reply.rpc._tag === "activity" ? Effect.never : storage.saveReply(reply) + }) + +const sendUnregisteredActivity = (workflowName: string, executionId: string) => + Effect.gen(function*() { + const entity = Entity.make(`Workflow/${workflowName}`, [RecoveryActivityRpc]) + const client = yield* entity.client + yield* client(executionId).activity({ name: "loser", attempt: 1, withTransaction: false }, { discard: true }) + const sharding = yield* Sharding.Sharding + const storage = yield* MessageStorage.MessageStorage + const entityId = EntityId.make(executionId) + const requestId = yield* storage.requestIdForPrimaryKey({ + address: EntityAddress.make({ + entityType: EntityType.make(entity.type), + entityId, + shardId: sharding.getShardId(entityId, "default") + }), + tag: "activity", + id: "loser/1" + }) + assert(Option.isSome(requestId)) + return requestId.value + }) + +const activityReply = (storage: MessageStorage.MessageStorage["Service"], requestId: Snowflake.Snowflake) => + Effect.gen(function*() { + for (let i = 0; i < 200; i++) { + const replies = yield* storage.repliesForUnfiltered([requestId]) + const reply = replies[replies.length - 1] + if (reply?._tag === "WithExit") { + return Schema.decodeUnknownSync(Schema.toCodecJson(Rpc.exitSchema(RecoveryActivityRpc)))(reply.exit) + } + yield* TestClock.adjust(1) + } + assert.fail("unregistered activity must receive a terminal Suspended reply without waiting for registration") + }) + const makeTestWorkflowEngine = (options?: { readonly config?: Partial | undefined readonly shardingLayer?: typeof Sharding.layer | undefined @@ -1912,6 +2518,65 @@ const pollUntil = ( return result.value }) +const nextStepActivity = (runs: Array, label: string) => + Activity.make({ + name: "next-step", + success: Schema.String, + execute: Effect.sync(() => { + runs.push(label) + return `${label}:next` + }) + }) + +/** + * Runs `body` and then a `next-step` activity beside a branch parked on an + * unrelated durable event. `next-step` must run before that event arrives, + * and exactly once. Deferred writes made by `body` are delayed to stand in + * for a persistence round trip. + */ +const expectContinuesBesideParkedSibling = ( + name: string, + body: Effect.Effect +) => + Effect.gen(function*() { + const workflow = Workflow.make(name, { + payload: {}, + success: Schema.String, + idempotencyKey: () => "one" + }) + const event = DurableDeferred.make(`${name}/Event`) + const nextStepRuns: Array = [] + const context = yield* Layer.build( + workflow.toLayer(() => + Effect.gen(function*() { + const engine = yield* WorkflowEngine + const delayed = Effect.provideService(body, WorkflowEngine, { + ...engine, + deferredDone: (deferred, done) => Effect.delay(engine.deferredDone(deferred, done), 10) + }) + const [result] = yield* Effect.all([ + Effect.flatMap(delayed, (value) => nextStepActivity(nextStepRuns, value)), + DurableDeferred.await(event) + ], { concurrency: "unbounded" }) + return result + }) + ).pipe(Layer.provideMerge(makeTestWorkflowEngine())) + ) + yield* Effect.gen(function*() { + const executionId = yield* workflow.execute({}, { discard: true }) + yield* advanceUntil(() => nextStepRuns.length > 0, "next-step must run before the event", 10, 300) + yield* DurableDeferred.succeed(event, { + token: DurableDeferred.tokenFromExecutionId(event, { workflow, executionId }), + value: undefined + }) + assert.deepStrictEqual( + yield* pollUntil(workflow, executionId, "Complete"), + new Workflow.Complete({ exit: Exit.succeed("timer:next") }) + ) + assert.deepStrictEqual(nextStepRuns, ["timer"]) + }).pipe(Effect.provide(context)) + }) + const advanceUntil = (ready: () => boolean, message: string, step = 1, rounds = 2000) => Effect.gen(function*() { for (let i = 0; i < rounds && !ready(); i++) yield* TestClock.adjust(step) diff --git a/repos/effect/packages/effect/test/cluster/Entity.test.ts b/packages/effect/test/cluster/Entity.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/Entity.test.ts rename to packages/effect/test/cluster/Entity.test.ts diff --git a/repos/effect/packages/effect/test/cluster/EntityProxy.test.ts b/packages/effect/test/cluster/EntityProxy.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/EntityProxy.test.ts rename to packages/effect/test/cluster/EntityProxy.test.ts diff --git a/repos/effect/packages/effect/test/cluster/Envelope.test.ts b/packages/effect/test/cluster/Envelope.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/Envelope.test.ts rename to packages/effect/test/cluster/Envelope.test.ts diff --git a/repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts b/packages/effect/test/cluster/K8sHttpClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts rename to packages/effect/test/cluster/K8sHttpClient.test.ts diff --git a/repos/effect/packages/effect/test/cluster/MessageStorage.test.ts b/packages/effect/test/cluster/MessageStorage.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/MessageStorage.test.ts rename to packages/effect/test/cluster/MessageStorage.test.ts diff --git a/repos/effect/packages/effect/test/cluster/ResourceMap.test.ts b/packages/effect/test/cluster/ResourceMap.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ResourceMap.test.ts rename to packages/effect/test/cluster/ResourceMap.test.ts diff --git a/repos/effect/packages/effect/test/cluster/ResourceRef.test.ts b/packages/effect/test/cluster/ResourceRef.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ResourceRef.test.ts rename to packages/effect/test/cluster/ResourceRef.test.ts diff --git a/repos/effect/packages/effect/test/cluster/RunnerServer.test.ts b/packages/effect/test/cluster/RunnerServer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/RunnerServer.test.ts rename to packages/effect/test/cluster/RunnerServer.test.ts diff --git a/repos/effect/packages/effect/test/cluster/RunnerStorage.test.ts b/packages/effect/test/cluster/RunnerStorage.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/RunnerStorage.test.ts rename to packages/effect/test/cluster/RunnerStorage.test.ts diff --git a/repos/effect/packages/effect/test/cluster/Runners.test.ts b/packages/effect/test/cluster/Runners.test.ts similarity index 81% rename from repos/effect/packages/effect/test/cluster/Runners.test.ts rename to packages/effect/test/cluster/Runners.test.ts index 94e1d598f..bd200c213 100644 --- a/repos/effect/packages/effect/test/cluster/Runners.test.ts +++ b/packages/effect/test/cluster/Runners.test.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from "@effect/vitest" -import { Cause, Context, Deferred, Effect, Exit, Layer, Option, Queue, Schema, Stream } from "effect" +import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, Option, Queue, Schema, Stream } from "effect" import { ClusterError, ClusterSchema, @@ -11,7 +11,7 @@ import { HttpRunner, Message, MessageStorage, - type Reply, + Reply, RunnerAddress, RunnerHealth, Runners, @@ -312,6 +312,89 @@ describe.concurrent("Runners.makeRpc", () => { Layer.provide(TestShardingConfig) ) + const layerLocalRunners = ( + wrap: (storage: MessageStorage.MessageStorage["Service"]) => MessageStorage.MessageStorage["Service"] = (storage) => + storage + ) => + Runners.layerNoop.pipe( + Layer.provideMerge( + Layer.effect(MessageStorage.MessageStorage)( + Effect.map(MessageStorage.MessageStorage, wrap) + ).pipe(Layer.provide(MessageStorage.layerMemory)) + ), + Layer.provideMerge(Snowflake.layerGenerator), + Layer.provide(ShardingConfig.layer({ entityReplyPollInterval: "1 hour" })) + ) + + const saveReply = ( + storage: MessageStorage.MessageStorage["Service"], + message: Message.OutgoingRequest, + id: Snowflake.Snowflake + ) => + storage.saveReply( + new Reply.ReplyWithContext({ + reply: new Reply.WithExit({ id, requestId: message.envelope.requestId, exit: Exit.succeed(42) }), + rpc: message.rpc, + context: message.context + }) + ) + + it.effect("delivers a persisted local reply completed before saveRequest returns", () => { + let complete: Effect.Effect = Effect.void + return Effect.gen(function*() { + const runners = yield* Runners.Runners + const storage = yield* MessageStorage.MessageStorage + const snowflake = yield* Snowflake.Generator + const replies: Array> = [] + const message = makeOutgoingRequest(TestRpcPersisted, snowflake.nextUnsafe(), (reply) => + Effect.sync(() => { + if (reply._tag === "WithExit") replies.push(reply.exit) + })) + complete = saveReply(storage, message, snowflake.nextUnsafe()).pipe(Effect.orDie) + const pending = yield* runners.notifyLocal({ message, discard: false, notify: () => Effect.void }).pipe( + Effect.timeoutOption("1 second"), + Effect.forkScoped + ) + yield* TestClock.adjust("1 second") + assert.isTrue(Option.isSome(yield* Fiber.join(pending)), "the caller must complete") + assert.deepStrictEqual(replies, [Exit.succeed(42)]) + }).pipe(Effect.provide(layerLocalRunners((storage) => ({ + ...storage, + saveRequest: (message) => storage.saveRequest(message).pipe(Effect.tap(() => complete)) + })))) + }) + + it.effect("keeps a duplicate local waiter alive when the first caller cancels", () => + Effect.gen(function*() { + const runners = yield* Runners.Runners + const storage = yield* MessageStorage.MessageStorage + const snowflake = yield* Snowflake.Generator + const requestId = snowflake.nextUnsafe() + const firstReplies: Array> = [] + const duplicateReplies: Array> = [] + const first = makeOutgoingRequest(TestRpcPersisted, requestId, (reply) => + Effect.sync(() => { + firstReplies.push(reply) + })) + const duplicate = makeOutgoingRequest(TestRpcPersisted, requestId, (reply) => + Effect.sync(() => { + if (reply._tag === "WithExit") duplicateReplies.push(reply.exit) + })) + const notify = (message: Message.OutgoingRequest) => + runners.notifyLocal({ message, discard: false, notify: () => Effect.void }) + const a = yield* notify(first).pipe(Effect.forkScoped) + yield* TestClock.adjust(0) + const b = yield* notify(duplicate).pipe(Effect.timeoutOption("1 second"), Effect.forkScoped) + yield* TestClock.adjust(0) + yield* Fiber.interrupt(a) + yield* saveReply(storage, first, snowflake.nextUnsafe()) + // Drain runnable work without reaching the one-hour polling deadline. + yield* TestClock.adjust(0) + assert.deepStrictEqual(firstReplies, []) + assert.deepStrictEqual(duplicateReplies, [Exit.succeed(42)]) + assert.isTrue(Option.isSome(yield* Fiber.join(b)), "the surviving caller must complete without polling") + }).pipe(Effect.provide(layerLocalRunners()))) + it.effect("uses the client protocol codec for simulated remote serialization", () => { let codecCalls = 0 const codecFor = ((schema: S) => { diff --git a/repos/effect/packages/effect/test/cluster/Sharding.test.ts b/packages/effect/test/cluster/Sharding.test.ts similarity index 81% rename from repos/effect/packages/effect/test/cluster/Sharding.test.ts rename to packages/effect/test/cluster/Sharding.test.ts index c2f63a21b..93b5146fa 100644 --- a/repos/effect/packages/effect/test/cluster/Sharding.test.ts +++ b/packages/effect/test/cluster/Sharding.test.ts @@ -6,6 +6,7 @@ import { Context, Deferred, Effect, + Equal, Exit, Fiber, Latch, @@ -18,6 +19,7 @@ import { Result, Schedule, Schema, + Scope, Stream } from "effect" import { @@ -46,7 +48,7 @@ import * as EntityManager from "effect/cluster/internal/entityManager" import { EntityReaper } from "effect/cluster/internal/entityReaper" import * as ActiveTeardown from "effect/cluster/internal/interruptors" import { Headers } from "effect/http" -import { Rpc, type RpcGroup } from "effect/rpc" +import { Rpc, type RpcGroup, type RpcSerialization } from "effect/rpc" import { TestClock } from "effect/testing" import { CallerId, @@ -59,6 +61,8 @@ import { } from "./TestEntity.ts" // Isolate the long-lived stream from concurrent shard-metric tests. +// Use a nonzero assignment refresh interval when advancing through claim expiry +// so the runner-storage sync loop does not busy-poll between clock ticks. describe("Sharding claim release regressions", { concurrent: false }, () => { it.effect("keeps the claim of an active request replayed after an entity defect", () => Effect.gen(function*() { @@ -94,7 +98,7 @@ describe("Sharding claim release regressions", { concurrent: false }, () => { }) }), { defectRetryPolicy: Schedule.forever } - ).pipe(Layer.provideMerge(CappedSharding({}, (storage) => ({ + ).pipe(Layer.provideMerge(CappedSharding({ refreshAssignmentsInterval: 60_000 }, (storage) => ({ ...storage, resetRequests: (ids) => Effect.gen(function*() { @@ -173,7 +177,7 @@ describe("Sharding claim release regressions", { concurrent: false }, () => { yield* Queue.offer(started, starts) return yield* Effect.never }).pipe(Effect.ensuring(Effect.sync(() => active--))) - }).pipe(Layer.provideMerge(CappedSharding({}, (storage) => ({ + }).pipe(Layer.provideMerge(CappedSharding({ refreshAssignmentsInterval: 60_000 }, (storage) => ({ ...storage, resetRequests: (ids) => Effect.gen(function*() { @@ -273,7 +277,7 @@ describe("Sharding claim release regressions", { concurrent: false }, () => { expect(values).toEqual([0]) assert.strictEqual(released.length, 0, "active stream claim must not be released after a chunk") assert.strictEqual(claimed.length, 1, "active stream must not be reclaimed on every poll") - }).pipe(Effect.provide(CappedSharding({}, (storage) => ({ + }).pipe(Effect.provide(CappedSharding({ refreshAssignmentsInterval: 60_000 }, (storage) => ({ ...storage, saveEnvelope: (message) => storage.saveEnvelope(message).pipe( @@ -362,9 +366,8 @@ describe("Sharding claim release regressions", { concurrent: false }, () => { }) })))) })) -}) -describe.concurrent("Sharding", () => { + // Keep the ten-minute clock advance out of the concurrent Sharding batch. it.effect("redelivers a request reset while an asynchronous storage read is pending", () => Effect.gen(function*() { const readStarted = yield* Deferred.make() @@ -408,7 +411,7 @@ describe.concurrent("Sharding", () => { 1, "reset request was claimed but not redelivered before claim expiry" ) - }).pipe(Effect.provide(CappedSharding({ refreshAssignmentsInterval: 1000 }, (storage) => ({ + }).pipe(Effect.provide(CappedSharding({ refreshAssignmentsInterval: 60_000 }, (storage) => ({ ...storage, unprocessedMessages: (shardIds, options) => Effect.gen(function*() { @@ -423,7 +426,9 @@ describe.concurrent("Sharding", () => { }) })))) })) +}) +describe.concurrent("Sharding", () => { for (const pauseReply of [false, true]) { it.effect( pauseReply @@ -1711,8 +1716,168 @@ describe.concurrent("Sharding", () => { Layer.provide(TestShardingConfig) ))) })) + + it.effect("WithTransaction persists a failure reply after rollback", () => + Effect.gen(function*() { + const state = yield* TestEntityState + const makeClient = yield* TestEntity.client + yield* TestClock.adjust(1) + const client = makeClient("1") + + const first = yield* Effect.flip(client.FailWithTransaction({ id: 1 })) + assert.strictEqual(first._tag, "BoomError") + + // The retry must be answered from storage without rerunning the handler. + const retry = yield* client.FailWithTransaction({ id: 1 }).pipe( + Effect.flip, + Effect.timeout(5000), + Effect.forkChild + ) + yield* TestClock.adjust(5000) + const second = yield* Fiber.join(retry) + assert.strictEqual(second._tag, "BoomError") + assert.strictEqual(Queue.sizeUnsafe(state.envelopes), 1) + }).pipe(Effect.provide(TestShardingWithoutStorage.pipe( + // Replies saved inside a failed transaction are discarded, like a SQL + // rollback. + Layer.updateService(MessageStorage.MessageStorage, (storage) => { + let saved: Array = [] + return { + ...storage, + withTransaction: (effect) => + storage.withTransaction(effect).pipe( + Effect.onExit((exit) => { + const rolledBack = saved + saved = [] + return Exit.isFailure(exit) + ? Effect.forEach(rolledBack, (id) => Effect.orDie(storage.clearReplies(id)), { discard: true }) + : Effect.void + }) + ), + saveReply: (reply) => + MessageStorage.MemoryTransaction.use((inTransaction) => { + if (inTransaction) saved.push(reply.reply.requestId) + return storage.saveReply(reply) + }) + } + }), + Layer.provide(MessageStorage.layerMemory), + Layer.provide(TestShardingConfig) + )))) + + it.effect("WithTransaction persists a client interrupt instead of replaying the request", () => + Effect.gen(function*() { + const driver = yield* MessageStorage.MemoryDriver + const state = yield* TestEntityState + const makeClient = yield* TestEntity.client + yield* TestClock.adjust(1) + + const fiber = yield* makeClient("1").NeverWithTransaction().pipe(Effect.forkChild({ startImmediately: true })) + yield* TestClock.adjust(1) + yield* Fiber.interrupt(fiber) + + yield* TestClock.adjust(1) + expect(driver.replyIds.size).toEqual(1) + expect(Queue.sizeUnsafe(state.envelopes)).toEqual(1) + }).pipe(Effect.provide(TestSharding))) + + it.effect("WithTransaction delivers a success only after commit", () => + Effect.gen(function*() { + const committing = Latch.makeUnsafe() + const commit = Latch.makeUnsafe() + yield* Effect.gen(function*() { + const makeClient = yield* TestEntity.client + yield* TestClock.adjust(1) + const result = yield* makeClient("1").WithTransaction({ id: 1 }).pipe(Effect.forkChild) + + yield* committing.await + yield* TestClock.adjust(1000) + assert.isUndefined(result.pollUnsafe()) + + yield* commit.open + assert.isTrue(yield* Fiber.join(result)) + }).pipe(Effect.provide(TestShardingWithoutStorage.pipe( + Layer.provide(RollbackMemoryStorage((transaction) => + Effect.tap(transaction, () => Effect.andThen(committing.open, commit.await)) + )), + Layer.provide(TestShardingConfig) + ))) + })) + + it.effect("WithTransaction replays the request without replying when COMMIT dies", () => + Effect.gen(function*() { + const state = yield* TestEntityState + const makeClient = yield* TestEntity.client + yield* TestClock.adjust(1) + + // Count handler runs when the reply arrives: only the replay may reply. + const result = yield* makeClient("1").WithTransaction({ id: 1 }).pipe( + Effect.map((value) => [value, Queue.sizeUnsafe(state.envelopes)] as const), + Effect.forkChild + ) + yield* TestClock.adjust(5000) + assert.deepStrictEqual(yield* Fiber.join(result), [true, 2]) + }).pipe(Effect.provide(TestShardingWithoutStorage.pipe( + Layer.provide(RollbackMemoryStorage((transaction, attempt) => + attempt === 1 ? Effect.andThen(transaction, Effect.die("COMMIT failed")) : transaction + )), + Layer.provide(TestShardingConfig) + )))) + + it.effect("WithTransaction replays the request without replying when ROLLBACK dies", () => + Effect.gen(function*() { + const state = yield* TestEntityState + const makeClient = yield* TestEntity.client + yield* TestClock.adjust(1) + + // Count handler runs when the reply arrives: only the replay may reply. + const result = yield* makeClient("1").FailWithTransaction({ id: 1 }).pipe( + Effect.flip, + Effect.map((error) => [error._tag, Queue.sizeUnsafe(state.envelopes)] as const), + Effect.forkChild + ) + yield* TestClock.adjust(5000) + assert.deepStrictEqual(yield* Fiber.join(result), ["BoomError", 2]) + }).pipe(Effect.provide(TestShardingWithoutStorage.pipe( + Layer.provide(RollbackMemoryStorage((transaction, attempt) => + attempt === 1 ? Effect.catchCause(transaction, () => Effect.die("ROLLBACK failed")) : transaction + )), + Layer.provide(TestShardingConfig) + )))) }) +// Memory storage that discards replies saved inside a failed transaction, like +// a SQL rollback. `wrap` runs around each transaction with its 1-based attempt. +const RollbackMemoryStorage = ( + wrap: (transaction: Effect.Effect, attempt: number) => Effect.Effect +) => + Layer.effect( + MessageStorage.MessageStorage, + Effect.gen(function*() { + const { encoded } = yield* MessageStorage.MemoryDriver + let attempts = 0 + let saved: Array = [] + return yield* MessageStorage.makeEncoded({ + ...encoded, + withTransaction: (effect) => + Effect.suspend(() => wrap(encoded.withTransaction(effect), ++attempts)).pipe( + Effect.onExit((exit) => { + const rolledBack = saved + saved = [] + return Exit.isFailure(exit) + ? Effect.forEach(rolledBack, (id) => Effect.orDie(encoded.clearReplies(id)), { discard: true }) + : Effect.void + }) + ), + saveReply: (reply) => + MessageStorage.MemoryTransaction.use((inTransaction) => { + if (inTransaction) saved.push(Snowflake.Snowflake(reply.requestId)) + return encoded.saveReply(reply) + }) + }) + }) + ).pipe(Layer.provide([MessageStorage.MemoryDriver.layer, Snowflake.layerGenerator])) + const DefectRecoveryRun = Rpc.make("run", { payload: { id: Schema.String }, success: Schema.String @@ -1921,6 +2086,61 @@ describe.concurrent("Sharding defect recovery", () => { assert.isTrue(Exit.hasInterrupts(exit), "queued arrival must be interrupted") yield* Deferred.succeed(acquired, undefined) }).pipe(Effect.provide(BlockedRebuildSharding))) + + it.effect("interrupts an entity whose id is also active on a shard that was interrupted first", () => + Effect.gen(function*() { + const run = Rpc.make("run") + const entity = Entity.make("DuplicateEntityId", [run]) + const sharding = yield* Sharding.Sharding + let stopped = 0 + const manager = yield* EntityManager.make( + entity, + Effect.as( + Effect.addFinalizer(() => Effect.sync(() => stopped++)), + entity.of({ run: () => Effect.void }) + ), + { + // both shards belong to this runner + sharding: { ...sharding, hasShardId: () => true }, + storage: MessageStorage.noop, + runnerAddress: RunnerAddress.make("localhost", 1234), + residency: { admitUnsafe: () => true, releaseUnsafe: () => {} }, + maxIdleTime: Infinity + } + ) + const entityId = EntityId.make("duplicate") + const activate = Effect.fnUntraced(function*(shardId: ShardId.ShardId) { + yield* manager.sendLocal( + new Message.IncomingRequestLocal({ + envelope: Envelope.makeRequest({ + requestId: yield* sharding.getSnowflake, + address: EntityAddress.make({ shardId, entityType: EntityType.make(entity.type), entityId }), + tag: "run", + payload: undefined, + headers: Headers.empty + }), + lastSentReply: Option.none(), + annotations: Context.empty(), + respond: () => Effect.void + }) + ) + }) + const defaultShard = ShardId.make("default", 1) + const workflowShard = ShardId.make("workflow", 1) + yield* TestClock.adjust(1) + yield* activate(defaultShard) + yield* activate(workflowShard) + yield* TestClock.adjust(1) + + const interruptWorkflow = yield* manager.interruptShard(workflowShard).pipe(Effect.forkChild) + yield* TestClock.adjust(1000) + assert.deepStrictEqual(interruptWorkflow.pollUnsafe(), Exit.void) + assert.strictEqual(stopped, 1) + const interruptDefault = yield* manager.interruptShard(defaultShard).pipe(Effect.forkChild) + yield* TestClock.adjust(1000) + assert.deepStrictEqual(interruptDefault.pollUnsafe(), Exit.void) + assert.strictEqual(stopped, 2) + }).pipe(Effect.provide(BlockedRebuildSharding))) }) const ActiveTeardownCaller = Entity.make("ActiveTeardownCaller", [ @@ -1988,8 +2208,8 @@ describe.concurrent("Sharding active teardowns", () => { runnerAddress: Option.some(RunnerAddress.make("localhost", 1234)), shardsPerGroup: 1, entityTerminationTimeout: 0, - entityMessagePollInterval: 10, - refreshAssignmentsInterval: 10, + entityMessagePollInterval: 1000, + refreshAssignmentsInterval: 1000, sendRetryInterval: 10 })) ) @@ -2008,8 +2228,10 @@ describe.concurrent("Sharding active teardowns", () => { assert.strictEqual(Queue.sizeUnsafe(state.envelopes), 1) storageState.assignSelf = false + // The acquisition loop waits one second between passes. Advance by + // that interval instead of repeatedly waking 10ms polling loops. while (sharding.hasShardId(shardId)) { - yield* TestClock.adjust(10) + yield* TestClock.adjust(1000) } while ((yield* sharding.activeEntityCount) > 0) { yield* TestClock.adjust(1) @@ -2146,6 +2368,7 @@ const ReapStormEntity = Entity.make("ReapStormEntity", [ const ReapStormEntityLayer = ReapStormEntity.toLayer({ Ping: () => Effect.void }) describe.concurrent("Sharding residency cap", () => { + // Repeated TestClock adjustments need wall-clock headroom under concurrent Deno CI load. it.effect("bounds resident entities to maxResidentEntities", () => Effect.gen(function*() { yield* TestClock.adjust(1) @@ -2170,7 +2393,7 @@ describe.concurrent("Sharding residency cap", () => { const users = yield* Fiber.joinAll(fibers) assert.deepStrictEqual(users.map((user) => user.id), [1, 2, 3, 4, 5, 6]) - }).pipe(Effect.provide(CappedSharding({ maxResidentEntities: 2, entityMaxIdleTime: 1000 })))) + }).pipe(Effect.provide(CappedSharding({ maxResidentEntities: 2, entityMaxIdleTime: 1000 }))), 20_000) it.effect("volatile sends to new entities fail with MailboxFull at the cap", () => Effect.gen(function*() { @@ -2698,6 +2921,97 @@ describe("Sharding shard lock failover", { concurrent: false }, () => { }).pipe(Effect.provide(layer), Effect.scoped) })) + it.effect("interrupts opted-in streams on reassignment but respects Uninterruptible", () => + Effect.gen(function*() { + const entity = Entity.make(TestEntity.type, [ + terminationRpc("StreamWithKey").annotate(ClusterSchema.Persisted, false), + terminationRpc("RequestWithKey") + .annotate(ClusterSchema.Persisted, false) + .annotate(ClusterSchema.Uninterruptible, "server") + ]).annotateRpcs(ClusterSchema.InterruptOnTermination, true) + const storageState = makeFailoverStorageState() + const started = yield* Queue.make() + const finish = yield* Deferred.make() + const entityLayer = entity.toLayer({ + StreamWithKey: () => Stream.fromEffect(Queue.offer(started, void 0).pipe(Effect.andThen(Effect.never))), + RequestWithKey: () => Queue.offer(started, void 0).pipe(Effect.andThen(Deferred.await(finish))) + }, { concurrency: "unbounded" }) + + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + yield* waitForTerminationOwnership(sharding, true) + // Let the acquisition backoff finish before testing reassignment. + yield* TestClock.adjust(1000) + const client = (yield* entity.client)("termination-stream") + const stream = yield* client.StreamWithKey({ key: "run" }).pipe( + Stream.runDrain, + Effect.forkChild({ startImmediately: true }) + ) + const protectedRequest = yield* client.RequestWithKey({ key: "run" }).pipe( + Effect.forkChild({ startImmediately: true }) + ) + yield* Queue.take(started) + yield* Queue.take(started) + storageState.assignSelf = false + yield* waitForTerminationOwnership(sharding, false) + yield* TestClock.adjust(100) + const earlyExit = stream.pollUnsafe() + const protectedExit = protectedRequest.pollUnsafe() + const held = storageState.releaseCalls.length === 0 + yield* Deferred.succeed(finish, void 0) + yield* TestClock.adjust(100) + const earlyReleases = storageState.releaseCalls.slice() + // Finish a broken implementation's grace period before asserting. + yield* TestClock.adjust(1000) + assert(earlyExit && Exit.hasInterrupts(earlyExit)) + assert.isUndefined(protectedExit) + assert.isTrue(held) + assert.deepStrictEqual(protectedRequest.pollUnsafe(), Exit.void) + assert.deepStrictEqual(earlyReleases, [ShardId.make("default", 1)]) + }).pipe(Effect.provide(TerminationSharding(entityLayer, storageState)), Effect.scoped) + })) + + it.effect("resumes an opted-in persisted request after early shard release", () => + Effect.gen(function*() { + const entity = Entity.make(TestEntity.type, [terminationRpc("RequestWithKey")]) + .annotateRpcs(ClusterSchema.InterruptOnTermination, true) + const storageState = makeFailoverStorageState() + const started = yield* Queue.make() + const finish = yield* Deferred.make() + const entityLayer = entity.toLayer({ + RequestWithKey: ({ requestId }) => Queue.offer(started, requestId).pipe(Effect.andThen(Deferred.await(finish))) + }) + + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + yield* waitForTerminationOwnership(sharding, true) + yield* TestClock.adjust(1000) + const client = (yield* entity.client)("termination-persisted") + const running = yield* client.RequestWithKey({ key: "run" }).pipe( + Effect.forkChild({ startImmediately: true }) + ) + const requestId = yield* Queue.take(started) + storageState.assignSelf = false + yield* waitForTerminationOwnership(sharding, false) + yield* TestClock.adjust(100) + const earlyReleases = storageState.releaseCalls.slice() + yield* TestClock.adjust(1000) + assert.deepStrictEqual(earlyReleases, [ShardId.make("default", 1)]) + assert.isUndefined(running.pollUnsafe()) + + // A replay with the same ID and successful completion proves termination + // neither saved a terminal reply nor marked the request processed. + storageState.assignSelf = true + yield* waitForTerminationOwnership(sharding, true) + yield* sharding.pollStorage + yield* TestClock.adjust(10) + assert.deepStrictEqual(Queue.takeUnsafe(started), Exit.succeed(requestId)) + yield* Deferred.succeed(finish, void 0) + yield* TestClock.adjust(100) + assert.deepStrictEqual(running.pollUnsafe(), Exit.void) + }).pipe(Effect.provide(TerminationSharding(entityLayer, storageState)), Effect.scoped) + })) + it.effect("keeps the graceful timeout for normal shard reassignment", () => Effect.gen(function*() { const storageState = makeFailoverStorageState() @@ -2773,6 +3087,95 @@ describe("Sharding shard lock failover", { concurrent: false }, () => { }).pipe(Effect.provide(layer), Effect.scoped) })) + it.effect("delivers a client interrupt to a request still draining on the lock holder", () => + Effect.gen(function*() { + const storageState = makeFailoverStorageState() + yield* Effect.gen(function*() { + const state = yield* TestEntityState + const sharding = yield* Sharding.Sharding + const client = (yield* TestEntity.client)("1") + const shardId = sharding.getShardId(EntityId.make("1"), "default") + while (!sharding.hasShardId(shardId)) { + yield* TestClock.adjust(10) + } + const request = yield* client.NeverVolatile().pipe(Effect.forkChild({ startImmediately: true })) + yield* Queue.take(state.envelopes) + storageState.assignSelf = false + while (sharding.hasShardId(shardId)) { + yield* TestClock.adjust(10) + } + assert.strictEqual(storageState.releaseCalls.length, 0, "the shard lock must still be held") + + yield* Fiber.interrupt(request).pipe(Effect.forkChild({ startImmediately: true })) + yield* TestClock.adjust(100) + assert.strictEqual(Queue.sizeUnsafe(state.interrupts), 1) + }).pipe(Effect.provide(GracefulHandoffSharding(storageState)), Effect.scoped) + })) + + it.effect("acknowledges stream chunks from a request still draining on the lock holder", () => + Effect.gen(function*() { + const storageState = makeFailoverStorageState() + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + const client = (yield* TestEntity.client)("1") + const shardId = sharding.getShardId(EntityId.make("1"), "default") + while (!sharding.hasShardId(shardId)) { + yield* TestClock.adjust(10) + } + // With one buffered chunk, the second is acknowledged only once the first is consumed. + const users = yield* client.GetAllUsersVolatile({ ids: [1, 2] }, { asQueue: true, streamBufferSize: 1 }) + yield* TestClock.adjust(1) + storageState.assignSelf = false + while (sharding.hasShardId(shardId)) { + yield* TestClock.adjust(10) + } + + const consumed = yield* Stream.fromQueue(users).pipe( + Stream.runCollect, + Effect.forkChild({ startImmediately: true }) + ) + yield* TestClock.adjust(100) + const exit = consumed.pollUnsafe() + assert.deepStrictEqual(exit && Exit.map(exit, (users) => users.map((user) => user.id)), Exit.succeed([1, 2])) + }).pipe(Effect.provide(GracefulHandoffSharding(storageState)), Effect.scoped) + })) + + it.effect("holds a remote request on the new owner until it acquires the shard lock", () => + Effect.gen(function*() { + const storageState = makeFailoverStorageState({ lockHeldElsewhere: true }) + yield* Effect.gen(function*() { + const sharding = yield* Sharding.Sharding + const entityId = EntityId.make("1") + const shardId = sharding.getShardId(entityId, "default") + while (storageState.acquireCalls.length === 0) { + yield* TestClock.adjust(10) + } + const delivery = yield* sharding.send( + new Message.IncomingRequest({ + envelope: { + _tag: "Request", + requestId: Snowflake.Snowflake(BigInt(1)), + address: EntityAddress.make({ shardId, entityType: EntityType.make(TestEntity.type), entityId }), + tag: "GetUserVolatile", + payload: { id: 1 }, + headers: Headers.empty + }, + lastSentReply: Option.none(), + respond: () => Effect.void, + codecFor: Schema.toCodecJson as RpcSerialization.CodecFor + }) + ).pipe(Effect.forkChild({ startImmediately: true })) + yield* TestClock.adjust(10) + assert.isUndefined(delivery.pollUnsafe(), "delivery must wait for the shard lock") + + storageState.lockHeldElsewhere = false + while (!sharding.hasShardId(shardId)) { + yield* TestClock.adjust(10) + } + assert.deepStrictEqual(yield* Fiber.await(delivery), Exit.void) + }).pipe(Effect.provide(GracefulHandoffSharding(storageState)), Effect.scoped) + })) + it.effect("does not wait for entity construction before a forced shard release", () => Effect.gen(function*() { const storageState = makeFailoverStorageState() @@ -2992,15 +3395,210 @@ describe("Sharding shard lock failover", { concurrent: false }, () => { })) }) +const terminationRpc = ["_tag"]>(tag: Tag) => + TestEntity.protocol.requests.get(tag)! as Extract, { readonly _tag: Tag }> + +const TerminationSharding = (entityLayer: Layer.Layer, state: FailoverStorageState) => + entityLayer.pipe( + Layer.provideMerge(Sharding.layer), + Layer.provide(Layer.effect( + RunnerStorage.RunnerStorage, + Effect.map(Clock.Clock, (clock) => makeFailoverStorage(state, clock)) + )), + Layer.provide(RunnerHealth.layerNoop), + Layer.provide(Runners.layerNoop), + Layer.provideMerge(MessageStorage.layerMemory), + Layer.provide(ShardingConfig.layer({ + runnerAddress: Option.some(RunnerAddress.make("localhost", 1234)), + shardsPerGroup: 1, + shardLockExpiration: 3000, + shardLockRefreshInterval: 100, + entityTerminationTimeout: 1000, + entityMessagePollInterval: 10, + entityReplyPollInterval: 10, + refreshAssignmentsInterval: 10, + sendRetryInterval: 10 + })) + ) + +const waitForTerminationOwnership = Effect.fnUntraced( + function*(sharding: Sharding.Sharding["Service"], owned: boolean) { + const shardId = ShardId.make("default", 1) + for (let i = 0; i < 100; i++) { + if (sharding.hasShardId(shardId) === owned) return + yield* TestClock.adjust(10) + } + assert.strictEqual(sharding.hasShardId(shardId), owned) + } +) +const GatedTeardownEntity = Entity.make("GatedTeardownEntity", [ + Rpc.make("Activate").annotate(ClusterSchema.Persisted, false) +]) + +describe("Sharding shard handoff", { concurrent: false }, () => { + const allShards = [ShardId.make("default", 1), ShardId.make("default", 2)] + const makeOwnedScope = Effect.acquireRelease(Scope.make(), (scope) => Scope.close(scope, Exit.void)) + // Open teardown gates before closing scopes, including on assertion failure. + const makeGate = Effect.acquireRelease(Effect.sync(() => Latch.makeUnsafe()), (gate) => gate.open) + const advanceUntil = (done: () => boolean) => + Effect.gen(function*() { + for (let i = 0; i < 100 && !done(); i++) { + yield* TestClock.adjust(10) + } + assert.isTrue(done()) + }) + const buildSharding = Effect.fnUntraced(function*( + storageState: FailoverStorageState, + config?: Partial, + existingScope?: Scope.Closeable + ) { + const scope = existingScope ?? (yield* makeOwnedScope) + const context = yield* Layer.buildWithScope( + Sharding.layer.pipe( + Layer.provide(Layer.effect( + RunnerStorage.RunnerStorage, + Effect.map(Clock.Clock, (clock) => makeFailoverStorage(storageState, clock)) + )), + Layer.provide(RunnerHealth.layerNoop), + Layer.provide(Runners.layerNoop), + Layer.provide([MessageStorage.layerMemory, Snowflake.layerGenerator]), + Layer.provide(ShardingConfig.layer({ + runnerAddress: Option.some(RunnerAddress.make("localhost", 1234)), + shardsPerGroup: 2, + entityTerminationTimeout: 0, + entityMessagePollInterval: 10, + refreshAssignmentsInterval: 10, + ...config + })) + ), + scope + ) + return { context, scope, sharding: Context.get(context, Sharding.Sharding) } + }) + + it.effect("waits for entity teardown before handing off without acquiring newly freed shards", () => + Effect.gen(function*() { + const [heldShard, freedShard] = allShards + const storageState = makeFailoverStorageState({ acquireDenied: [freedShard] }) + const registrationScope = yield* makeOwnedScope + // Keep assignments unchanged while a peer's lock becomes available. + const { context, scope, sharding } = yield* buildSharding(storageState, { refreshAssignmentsInterval: 60_000 }) + const gate = yield* makeGate + yield* advanceUntil(() => sharding.hasShardId(heldShard)) + yield* sharding.registerEntity( + GatedTeardownEntity, + Effect.as(Effect.addFinalizer(() => gate.await), GatedTeardownEntity.of({ Activate: () => Effect.void })) + ).pipe(Effect.provideService(Scope.Scope, registrationScope)) + let entityId = 0 + while (!Equal.equals(sharding.getShardId(EntityId.make(String(entityId)), "default"), heldShard)) { + entityId++ + } + const makeClient = yield* GatedTeardownEntity.client.pipe(Effect.provideContext(context)) + yield* makeClient(String(entityId)).Activate() + + const closing = yield* Effect.forkChild(Scope.close(scope, Exit.void)) + yield* advanceUntil(() => storageState.runner === undefined) + const acquires = storageState.acquireCalls.length + storageState.acquireDenied.length = 0 + yield* TestClock.adjust(3000) + assert.isUndefined(closing.pollUnsafe()) + assert.isUndefined(storageState.runner) + assert.deepStrictEqual(storageState.acquireCalls.slice(acquires), []) + assert.deepStrictEqual(storageState.releaseCalls, []) + assert.deepStrictEqual(storageState.releaseAllCalls, []) + + yield* gate.open + yield* advanceUntil(() => closing.pollUnsafe() !== undefined) + assert.deepStrictEqual(closing.pollUnsafe(), Exit.void) + assert.deepStrictEqual(storageState.releaseCalls, [heldShard]) + assert.deepStrictEqual(storageState.releaseAllCalls.map((call) => call.releases), [1]) + }).pipe(Effect.scoped)) + + it.effect("waits for singleton teardown before handing off their shards", () => + Effect.gen(function*() { + const storageState = makeFailoverStorageState() + const registrationScope = yield* makeOwnedScope + const runningRegistrationScope = yield* makeOwnedScope + const { scope, sharding } = yield* buildSharding(storageState) + const gate = yield* makeGate + yield* advanceUntil(() => allShards.every((shardId) => sharding.hasShardId(shardId))) + const started = Latch.makeUnsafe() + const stopping = Latch.makeUnsafe() + yield* sharding.registerSingleton( + "ClosingSingleton", + Effect.andThen(started.open, Effect.addFinalizer(() => Effect.andThen(stopping.open, gate.await))) + ).pipe(Effect.provideService(Scope.Scope, registrationScope)) + yield* started.await + // a singleton on the other shard that is still running at shutdown + const closingShard = sharding.getShardId(EntityId.make("ClosingSingleton"), "default") + let runningName = 0 + while (Equal.equals(sharding.getShardId(EntityId.make(`Running${runningName}`), "default"), closingShard)) { + runningName++ + } + const runningStarted = Latch.makeUnsafe() + yield* sharding.registerSingleton( + `Running${runningName}`, + Effect.andThen(runningStarted.open, Effect.addFinalizer(() => gate.await)) + ).pipe(Effect.provideService(Scope.Scope, runningRegistrationScope)) + yield* runningStarted.await + yield* Effect.forkChild(Scope.close(registrationScope, Exit.void)) + yield* stopping.await + + const closing = yield* Effect.forkChild(Scope.close(scope, Exit.void)) + yield* TestClock.adjust(100) + assert.isUndefined(closing.pollUnsafe()) + assert.deepStrictEqual(storageState.releaseCalls, []) + assert.deepStrictEqual(storageState.releaseAllCalls, []) + + yield* gate.open + yield* advanceUntil(() => closing.pollUnsafe() !== undefined) + assert.deepStrictEqual(closing.pollUnsafe(), Exit.void) + assert.deepStrictEqual(storageState.releaseAllCalls.map((call) => call.releases), [2]) + }).pipe(Effect.scoped)) + + it.effect("closes a singleton registration whose singleton registered another singleton", () => + Effect.gen(function*() { + // Await cleanup, but keep a regressed uninterruptible teardown bounded. + const makeDetachedScope = Effect.acquireRelease( + Scope.make(), + (scope) => + Effect.gen(function*() { + const closing = yield* Effect.forkDetach(Scope.close(scope, Exit.void)) + yield* advanceUntil(() => closing.pollUnsafe() !== undefined) + assert.deepStrictEqual(closing.pollUnsafe(), Exit.void) + }) + ) + const storageState = makeFailoverStorageState() + const shardingScope = yield* makeDetachedScope + const registrationScope = yield* makeDetachedScope + const { sharding } = yield* buildSharding(storageState, undefined, shardingScope) + yield* advanceUntil(() => allShards.every((shardId) => sharding.hasShardId(shardId))) + const childRegistered = Latch.makeUnsafe() + yield* sharding.registerSingleton( + "ParentSingleton", + Effect.andThen(sharding.registerSingleton("ChildSingleton", Effect.never), childRegistered.open) + ).pipe(Effect.provideService(Scope.Scope, registrationScope)) + yield* childRegistered.await + + const closing = yield* Effect.forkDetach(Scope.close(registrationScope, Exit.void)) + yield* advanceUntil(() => closing.pollUnsafe() !== undefined) + assert.deepStrictEqual(closing.pollUnsafe(), Exit.void) + }).pipe(Effect.scoped)) +}) + interface FailoverStorageState { blackholed: boolean /** Hang only non-empty refreshes, so the empty liveness probe can succeed. */ blackholeNonEmptyRefresh: boolean assignSelf: boolean + /** Fail lock acquisition, as when another runner still holds the locks. */ + lockHeldElsewhere: boolean otherRunnerHealthy: boolean /** Test clock duration `releaseAll` stays in flight for. */ releaseAllDuration: number runner: Runner.Runner | undefined + /** Shards `acquire` does not grant, as if another runner holds them. */ + readonly acquireDenied: Array readonly acquireCalls: Array<{ readonly shards: Array readonly completedReleaseAlls: number @@ -3010,7 +3608,7 @@ interface FailoverStorageState { readonly shards: Array }> readonly releaseCalls: Array - readonly releaseAllCalls: Array<{ completed: boolean }> + readonly releaseAllCalls: Array<{ completed: boolean; readonly releases: number }> } const makeFailoverStorageState = ( @@ -3019,9 +3617,11 @@ const makeFailoverStorageState = ( blackholed: false, blackholeNonEmptyRefresh: false, assignSelf: true, + lockHeldElsewhere: false, otherRunnerHealthy: false, releaseAllDuration: 0, runner: undefined, + acquireDenied: [], acquireCalls: [], refreshCalls: [], releaseCalls: [], @@ -3041,7 +3641,10 @@ const makeFailoverStorage = (state: FailoverStorageState, clock: Clock.Clock) => state.runner = runner return MachineId.make(1) }), - unregister: () => Effect.void, + unregister: () => + Effect.sync(() => { + state.runner = undefined + }), setRunnerHealth: () => Effect.void, acquire: (_address, shardIds) => Effect.sync(() => { @@ -3050,7 +3653,9 @@ const makeFailoverStorage = (state: FailoverStorageState, clock: Clock.Clock) => shards, completedReleaseAlls: state.releaseAllCalls.filter((call) => call.completed).length }) - return shards + return state.lockHeldElsewhere ? + [] : + shards.filter((shardId) => !state.acquireDenied.some((denied) => Equal.equals(denied, shardId))) }), refresh: (_address, shardIds) => Effect.suspend(() => { @@ -3069,7 +3674,7 @@ const makeFailoverStorage = (state: FailoverStorageState, clock: Clock.Clock) => }), releaseAll: () => Effect.suspend(() => { - const call = { completed: false } + const call = { completed: false, releases: state.releaseCalls.length } state.releaseAllCalls.push(call) return Effect.andThen( Effect.sleep(state.releaseAllDuration), @@ -3086,6 +3691,27 @@ const otherRunner = Runner.make({ weight: 1 }) +const GracefulHandoffSharding = (storageState: FailoverStorageState) => + TestEntityNoState.pipe( + Layer.provideMerge(Sharding.layer), + Layer.provide(Layer.effect( + RunnerStorage.RunnerStorage, + Effect.map(Clock.Clock, (clock) => makeFailoverStorage(storageState, clock)) + )), + Layer.provide(RunnerHealth.layerNoop), + Layer.provideMerge(TestEntityState.layer), + Layer.provide(Runners.layerNoop), + Layer.provide([MessageStorage.layerMemory, Snowflake.layerGenerator]), + Layer.provide(ShardingConfig.layer({ + runnerAddress: Option.some(RunnerAddress.make("localhost", 1234)), + shardsPerGroup: 1, + entityTerminationTimeout: 1000, + entityMessagePollInterval: 10, + refreshAssignmentsInterval: 10, + sendRetryInterval: 10 + })) + ) + class RegistrationContext extends Context.Service()( "effect/test/cluster/RegistrationContext" ) {} @@ -3434,3 +4060,53 @@ describe("Sharding singleton cancellation", { concurrent: false }, () => { }).pipe(Effect.provide(SingletonReassignmentSharding(storageState)), Effect.scoped) })) }) + +const PendingLockSharding = (storageState: FailoverStorageState) => + TestEntityNoState.pipe( + Layer.provideMerge(Sharding.layer), + Layer.provide(Layer.effect( + RunnerStorage.RunnerStorage, + Effect.map(Clock.Clock, (clock) => makeFailoverStorage(storageState, clock)) + )), + Layer.provide(RunnerHealth.layerNoop), + Layer.provideMerge(TestEntityState.layer), + Layer.provide(Runners.layerNoop), + Layer.provide(MessageStorage.layerMemory), + Layer.provide(ShardingConfig.layer({ + runnerAddress: Option.some(RunnerAddress.make("localhost", 1234)), + shardsPerGroup: 1, + entityMessagePollInterval: 10, + refreshAssignmentsInterval: 10, + sendRetryInterval: 10 + })) + ) + +describe("Sharding persisted sends to a shard awaiting its lock", { concurrent: false }, () => { + it.effect("persists a discard send and delivers it once the shard lock is acquired", () => + Effect.gen(function*() { + const storageState = makeFailoverStorageState({ lockHeldElsewhere: true }) + yield* Effect.gen(function*() { + const state = yield* TestEntityState + const sharding = yield* Sharding.Sharding + const shardId = sharding.getShardId(EntityId.make("1"), "default") + while (storageState.acquireCalls.length === 0) { + yield* TestClock.adjust(10) + } + const client = (yield* TestEntity.client)("1") + const send = yield* client.GetUser({ id: 1 }, { discard: true }).pipe( + Effect.forkChild({ startImmediately: true }) + ) + yield* TestClock.adjust(100) + assert.deepStrictEqual(send.pollUnsafe(), Exit.void, "a discard send returns once persisted") + assert.strictEqual(Queue.sizeUnsafe(state.envelopes), 0, "delivery must wait for the shard lock") + + storageState.lockHeldElsewhere = false + while (!sharding.hasShardId(shardId)) { + yield* TestClock.adjust(10) + } + yield* sharding.pollStorage + yield* TestClock.adjust(10) + assert.strictEqual(Queue.sizeUnsafe(state.envelopes), 1) + }).pipe(Effect.provide(PendingLockSharding(storageState)), Effect.scoped) + })) +}) diff --git a/repos/effect/packages/effect/test/cluster/ShardingConfig.test.ts b/packages/effect/test/cluster/ShardingConfig.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/ShardingConfig.test.ts rename to packages/effect/test/cluster/ShardingConfig.test.ts diff --git a/repos/effect/packages/effect/test/cluster/TestEntity.ts b/packages/effect/test/cluster/TestEntity.ts similarity index 82% rename from repos/effect/packages/effect/test/cluster/TestEntity.ts rename to packages/effect/test/cluster/TestEntity.ts index a7d66ac8a..aca255957 100644 --- a/repos/effect/packages/effect/test/cluster/TestEntity.ts +++ b/packages/effect/test/cluster/TestEntity.ts @@ -43,10 +43,21 @@ export const TestEntity = Entity.make("TestEntity", [ payload: { ids: Schema.Array(Schema.Number) }, stream: true }), + Rpc.make("GetAllUsersVolatile", { + success: User, + payload: { ids: Schema.Array(Schema.Number) }, + stream: true + }).annotate(ClusterSchema.Persisted, false), Rpc.make("WithTransaction", { success: Schema.Boolean, payload: { id: Schema.Number } - }).annotate(ClusterSchema.Dynamic, Context.add(ClusterSchema.WithTransaction, true)) + }).annotate(ClusterSchema.Dynamic, Context.add(ClusterSchema.WithTransaction, true)), + Rpc.make("FailWithTransaction", { + error: BoomError, + payload: { id: Schema.Number }, + primaryKey: ({ id }) => String(id) + }).annotate(ClusterSchema.Dynamic, Context.add(ClusterSchema.WithTransaction, true)), + Rpc.make("NeverWithTransaction").annotate(ClusterSchema.Dynamic, Context.add(ClusterSchema.WithTransaction, true)) ]).annotateRpcs(ClusterSchema.Persisted, true) export class TestEntityState extends Context.Service()("TestEntityState", { @@ -132,7 +143,21 @@ export const TestEntityNoState = TestEntity.toLayer( Stream.rechunk(1) ) }, - WithTransaction: () => MemoryTransaction + GetAllUsersVolatile: (envelope) => + Stream.fromIterable(envelope.payload.ids.map((id) => new User({ id, name: `User ${id}` }))).pipe( + Stream.rechunk(1) + ), + WithTransaction: (envelope) => + Effect.suspend(() => { + Queue.offerUnsafe(state.envelopes, envelope) + return MemoryTransaction + }), + FailWithTransaction: (envelope) => + Effect.suspend(() => { + Queue.offerUnsafe(state.envelopes, envelope) + return Effect.fail(new BoomError({ cause: "boom" })) + }), + NeverWithTransaction: never }) }), { defectRetryPolicy: Schedule.forever } diff --git a/repos/effect/packages/effect/test/cluster/interruptors.test.ts b/packages/effect/test/cluster/interruptors.test.ts similarity index 100% rename from repos/effect/packages/effect/test/cluster/interruptors.test.ts rename to packages/effect/test/cluster/interruptors.test.ts diff --git a/repos/effect/packages/effect/test/devtools/DevToolsClient.test.ts b/packages/effect/test/devtools/DevToolsClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/devtools/DevToolsClient.test.ts rename to packages/effect/test/devtools/DevToolsClient.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Base64.test.ts b/packages/effect/test/encoding/Base64.test.ts similarity index 69% rename from repos/effect/packages/effect/test/encoding/Base64.test.ts rename to packages/effect/test/encoding/Base64.test.ts index b2b68cf5b..4a4fb333b 100644 --- a/repos/effect/packages/effect/test/encoding/Base64.test.ts +++ b/packages/effect/test/encoding/Base64.test.ts @@ -3,6 +3,18 @@ import { Result } from "effect" import * as Base64 from "effect/encoding/Base64" describe("Base64", () => { + it("encodes and decodes binary subarrays with and without padding", () => { + const bytes = new Uint8Array([42, 251, 255, 128, 42]) + const vectors = ["", "+w==", "+/8=", "+/+A"] + + for (const [length, encoded] of vectors.entries()) { + const view = bytes.subarray(1, 1 + length) + assert.strictEqual(Base64.encode(view), encoded) + assert.deepStrictEqual(Result.getOrThrow(Base64.decode(encoded)), view) + } + assert.deepStrictEqual(bytes, new Uint8Array([42, 251, 255, 128, 42])) + }) + it("encodes strings and bytes", () => { assert.strictEqual(Base64.encode("hello"), "aGVsbG8=") assert.strictEqual(Base64.encode(new Uint8Array([72, 101, 108, 108, 111])), "SGVsbG8=") diff --git a/repos/effect/packages/effect/test/encoding/Base64Url.test.ts b/packages/effect/test/encoding/Base64Url.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Base64Url.test.ts rename to packages/effect/test/encoding/Base64Url.test.ts diff --git a/repos/effect/packages/effect/test/encoding/EncodingError.test.ts b/packages/effect/test/encoding/EncodingError.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/EncodingError.test.ts rename to packages/effect/test/encoding/EncodingError.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Hex.test.ts b/packages/effect/test/encoding/Hex.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Hex.test.ts rename to packages/effect/test/encoding/Hex.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Ini.test.ts b/packages/effect/test/encoding/Ini.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Ini.test.ts rename to packages/effect/test/encoding/Ini.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Ndjson.test.ts b/packages/effect/test/encoding/Ndjson.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Ndjson.test.ts rename to packages/effect/test/encoding/Ndjson.test.ts diff --git a/repos/effect/packages/effect/test/encoding/SchemaBinary.test.ts b/packages/effect/test/encoding/SchemaBinary.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/SchemaBinary.test.ts rename to packages/effect/test/encoding/SchemaBinary.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Sse.test.ts b/packages/effect/test/encoding/Sse.test.ts similarity index 69% rename from repos/effect/packages/effect/test/encoding/Sse.test.ts rename to packages/effect/test/encoding/Sse.test.ts index 2296f98c8..8407f5d6d 100644 --- a/repos/effect/packages/effect/test/encoding/Sse.test.ts +++ b/packages/effect/test/encoding/Sse.test.ts @@ -22,7 +22,8 @@ describe("Sse", () => { const events: Array = [] const parser = Sse.makeParser((event) => events.push(event)) - parser.feed("data: first\r") + parser.feed("data: fir") + parser.feed("st\r") parser.feed("\ndata: second\n\n") assert.deepStrictEqual(events, [{ @@ -65,6 +66,16 @@ describe("Sse", () => { assert.deepStrictEqual(events, [input]) }) + it("roundtrips CR, CRLF and LF data lines, preserving a trailing empty line", () => { + const input: Sse.Event = { _tag: "Event", event: "message", id: undefined, data: "10%\r50%\r\ndone\n" } + const events: Array = [] + const parser = Sse.makeParser((event) => events.push(event)) + + parser.feed(Sse.encoder.write(input)) + + assert.deepStrictEqual(events, [{ ...input, data: "10%\n50%\ndone\n" }]) + }) + it("strips a UTF-8 BOM from the start of an SSE stream", () => { const events: Array = [] const parser = Sse.makeParser((event) => events.push(event)) @@ -89,6 +100,53 @@ describe("Sse", () => { assert.strictEqual((events[0] as Sse.Event).event, "message") }) + it("parses a long line split across many chunks", () => { + const events: Array = [] + const parser = Sse.makeParser((event) => events.push(event)) + const value = "x".repeat(10_000) + const input = `event: big\ndata: ${value}\n\n` + + for (let i = 0; i < input.length; i += 7) { + parser.feed(input.slice(i, i + 7)) + } + + assert.deepStrictEqual(events, [{ + _tag: "Event", + event: "big", + id: undefined, + data: value + }]) + }) + + it("discards the rest of the chunk when reset from onParse", () => { + const events: Array = [] + const parser = Sse.makeParser((event) => { + if (event._tag === "Event") { + events.push(event.data) + if (event.data === "a") parser.reset() + } + }) + + parser.feed("data: a\n\ndata: b\n\n") + parser.feed("data: fresh\n\n") + + assert.deepStrictEqual(events, ["a", "fresh"]) + }) + + it("discards the current event and remaining chunk when onParse throws", () => { + const events: Array = [] + const error = new Error("onParse failed") + const parser = Sse.makeParser((event) => { + if (event._tag === "Event" && event.data === "a") throw error + events.push(event) + }) + + assert.throws(() => parser.feed("event: custom\ndata: a\n\ndata: b\n\n"), Error, "onParse failed") + parser.feed("data: c\n\n") + + assert.deepStrictEqual(events, [{ _tag: "Event", event: "message", id: undefined, data: "c" }]) + }) + it("ignores retry values that are not ASCII digits", () => { const events: Array = [] for (const value of ["123x", "+123", "-123", "1.5"]) { @@ -167,6 +225,22 @@ describe("Sse", () => { assert.strictEqual(error.reason.maxEventSize, 4) })) + it("counts buffered data and fragmented pending lines toward maxEventSize and recovers", () => { + const events: Array = [] + const parser = Sse.makeParser((event) => events.push(event), { maxEventSize: 10 }) + + assert.isUndefined(parser.feed("data: a\n")) + assert.isUndefined(parser.feed("data:")) + assert.isUndefined(parser.feed(" bc")) + const error = parser.feed("d") + assert.instanceOf(error, Sse.SseError) + assert.instanceOf(error.reason, Sse.EventTooLarge) + assert.strictEqual(error.reason.maxEventSize, 10) + + assert.isUndefined(parser.feed("\uFEFFdata: x\n\n")) + assert.deepStrictEqual(events, [{ _tag: "Event", event: "message", id: undefined, data: "x" }]) + }) + it.effect("fails when pending data exceeds maxEventSize", () => Effect.gen(function*() { const error = yield* Stream.make("data: a\n", "data: b\n").pipe( diff --git a/repos/effect/packages/effect/test/encoding/Toml.test.ts b/packages/effect/test/encoding/Toml.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Toml.test.ts rename to packages/effect/test/encoding/Toml.test.ts diff --git a/repos/effect/packages/effect/test/encoding/Yaml.test.ts b/packages/effect/test/encoding/Yaml.test.ts similarity index 100% rename from repos/effect/packages/effect/test/encoding/Yaml.test.ts rename to packages/effect/test/encoding/Yaml.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventJournal.test.ts b/packages/effect/test/eventlog/EventJournal.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventJournal.test.ts rename to packages/effect/test/eventlog/EventJournal.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLog.test.ts b/packages/effect/test/eventlog/EventLog.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLog.test.ts rename to packages/effect/test/eventlog/EventLog.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts b/packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts rename to packages/effect/test/eventlog/EventLogIdentityDerivation.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogMessage.test.ts b/packages/effect/test/eventlog/EventLogMessage.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogMessage.test.ts rename to packages/effect/test/eventlog/EventLogMessage.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogRemote.test.ts b/packages/effect/test/eventlog/EventLogRemote.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogRemote.test.ts rename to packages/effect/test/eventlog/EventLogRemote.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts b/packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts rename to packages/effect/test/eventlog/EventLogServerUnencrypted.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/EventLogSessionAuth.test.ts b/packages/effect/test/eventlog/EventLogSessionAuth.test.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/EventLogSessionAuth.test.ts rename to packages/effect/test/eventlog/EventLogSessionAuth.test.ts diff --git a/repos/effect/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts b/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts similarity index 100% rename from repos/effect/packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts rename to packages/effect/test/eventlog/SqlEventLogServerUnencryptedStorageTest.ts diff --git a/repos/effect/packages/effect/test/fixtures/migrator/0001_first.js b/packages/effect/test/fixtures/migrator/0001_first.js similarity index 100% rename from repos/effect/packages/effect/test/fixtures/migrator/0001_first.js rename to packages/effect/test/fixtures/migrator/0001_first.js diff --git a/repos/effect/packages/effect/test/fixtures/text.txt b/packages/effect/test/fixtures/text.txt similarity index 100% rename from repos/effect/packages/effect/test/fixtures/text.txt rename to packages/effect/test/fixtures/text.txt diff --git a/packages/effect/test/graphql/GraphQL.test.ts b/packages/effect/test/graphql/GraphQL.test.ts new file mode 100644 index 000000000..c06bbc6c1 --- /dev/null +++ b/packages/effect/test/graphql/GraphQL.test.ts @@ -0,0 +1,68 @@ +import { assert, describe, it } from "@effect/vitest" +import { Effect, Schema } from "effect" +import { GraphQL, GraphQLMiddleware } from "effect/graphql" +import { renderSlices } from "effect/internal/graphql" + +class Auth extends GraphQLMiddleware.Service()("Auth") {} + +// A pull request lookup. Odd entries in a `body` are names renamed per slice: +// `PR` references `$withBody`, so it is per-slice; `Author` is shared. The +// root `__typename` checks that joining keeps the printer's spacing. +const pullRequestTemplate: GraphQL.DocumentTemplate = { + variables: [ + { name: "owner", body: "String!" }, + { name: "number", body: "Int!" }, + { name: "withBody", body: "Boolean!=false" } + ], + selections: [ + { name: "__typename", body: [""] }, + { + alias: "repo", + name: "repository", + body: ["(owner:$", "owner", ",name:\"effect\"){pullRequest(number:$", "number", "){...", "PR", "}}"] + } + ], + fragments: [ + { name: "PR", body: ["on PullRequest{title body@include(if:$", "withBody", ")...Author}"] }, + { name: "Author", body: ["on PullRequest{author{login}}"] } + ] +} + +describe("GraphQL", () => { + it("middleware returns a new operation and leaves the original unchanged", () => { + const Viewer = GraphQL.query("Viewer", { document: "query Viewer{viewer{login}}", result: Schema.Struct({}) }) + const authed = Viewer.middleware(Auth) + assert.deepStrictEqual(authed.middlewares, [Auth]) + assert.deepStrictEqual(Viewer.middlewares, []) + }) + + it("an operation built from a template sends the joined document", () => { + const PullRequest = GraphQL.query("PullRequest", { document: pullRequestTemplate, result: Schema.Struct({}) }) + assert.strictEqual( + PullRequest.document, + "query PullRequest($owner:String!$number:Int!$withBody:Boolean!=false){__typename repo:repository(owner:$owner,name:\"effect\"){pullRequest(number:$number){...PR}}}fragment PR on PullRequest{title body@include(if:$withBody)...Author}fragment Author on PullRequest{author{login}}" + ) + assert.strictEqual(PullRequest.template, pullRequestTemplate) + }) + + it("renderSlices aliases root fields and renames variables and per-slice fragments", () => { + assert.strictEqual( + renderSlices("query", "PullRequest", pullRequestTemplate, ["_0_", "_1_"]), + "query PullRequest($_0_owner:String!$_0_number:Int!$_0_withBody:Boolean!=false$_1_owner:String!$_1_number:Int!$_1_withBody:Boolean!=false){_0___typename:__typename _0_repo:repository(owner:$_0_owner,name:\"effect\"){pullRequest(number:$_0_number){..._0_PR}}_1___typename:__typename _1_repo:repository(owner:$_1_owner,name:\"effect\"){pullRequest(number:$_1_number){..._1_PR}}}fragment _0_PR on PullRequest{title body@include(if:$_0_withBody)...Author}fragment _1_PR on PullRequest{title body@include(if:$_1_withBody)...Author}fragment Author on PullRequest{author{login}}" + ) + }) + + it.effect("otherTypename accepts any type name except the selected ones", () => + Effect.gen(function*() { + const decode = Schema.decodeUnknownEffect(GraphQL.otherTypename<"Issue" | "PullRequest">()(["Issue"])) + assert.strictEqual(yield* decode("PullRequest"), "PullRequest") + assert.strictEqual(yield* decode("AddedLater"), "AddedLater") + assert.instanceOf(yield* Effect.flip(decode("Issue")), Schema.SchemaError) + })) + + it.effect("enumLiterals accepts a value the server added later", () => + Effect.gen(function*() { + const decode = Schema.decodeUnknownEffect(GraphQL.enumLiterals(["OPEN", "CLOSED"])) + assert.strictEqual(yield* decode("MERGED"), "MERGED") + })) +}) diff --git a/packages/effect/test/graphql/GraphQLClient.makeTest.test.ts b/packages/effect/test/graphql/GraphQLClient.makeTest.test.ts new file mode 100644 index 000000000..afee4d223 --- /dev/null +++ b/packages/effect/test/graphql/GraphQLClient.makeTest.test.ts @@ -0,0 +1,118 @@ +import { assert, describe, it } from "@effect/vitest" +import { Context, DateTime, Effect, Schema, Stream } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup } from "effect/graphql" +import { IssuesGroup } from "./fixtures.ts" + +const viewerResult = Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + +// Variables that may be `null` as a whole. +const NullableVariables = GraphQL.query("NullableVariables", { + document: "query NullableVariables{viewer{login}}", + variables: Schema.NullOr(Schema.Struct({ login: Schema.String })), + result: viewerResult +}) + +// A valid operation name that is also an `Object.prototype` method. +const ToString = GraphQL.query("toString", { document: "query toString{viewer{login}}", result: viewerResult }) + +const issue = (number: number) => ({ + number, + title: `#${number}`, + createdAt: DateTime.makeUnsafe("2026-01-01T00:00:00Z") +}) + +const page = ( + nodes: ReadonlyArray>, + pageInfo: { hasNextPage: boolean; endCursor: string | null } +) => ({ repository: { issues: { pageInfo, nodes } } }) + +const variables = { owner: "Effect-TS", name: "effect" } + +describe("GraphQLClient.makeTest", () => { + it.effect("a handled query returns the handler's value and receives the caller's variables", () => + Effect.gen(function*() { + const result = page([issue(1)], { hasNextPage: false, endCursor: null }) + const received: Array = [] + const client = GraphQLClient.makeTest(IssuesGroup)({ + RepoIssues: (variables) => { + received.push(variables) + return Effect.succeed(result) + } + }) + const since = DateTime.makeUnsafe("2026-01-01T00:00:00Z") + const called = { ...variables, since } + + assert.strictEqual(yield* client.RepoIssues(called), result) + assert.strictEqual(received[0], called) + + const nullable = GraphQLClient.makeTest(GraphQLGroup.make(NullableVariables))({ + NullableVariables: (variables) => { + received.push(variables) + return Effect.succeed({ viewer: { login: "tim" } }) + } + }) + yield* nullable.NullableVariables(null) + assert.strictEqual(received[1], null) + })) + + it.effect("partial: true returns the handler's value as data with no errors", () => + Effect.gen(function*() { + const result = page([issue(1)], { hasNextPage: false, endCursor: null }) + const client = GraphQLClient.makeTest(IssuesGroup)({ + RepoIssues: () => Effect.succeed(result) + }) + + const partial = yield* client.RepoIssues(variables, { partial: true }) + assert.strictEqual(partial.data, result) + assert.deepStrictEqual(partial.errors, []) + })) + + it.effect("an operation without a handler dies with an UnimplementedError", () => + Effect.gen(function*() { + const client = GraphQLClient.makeTest(IssuesGroup)({}) + + const defect = yield* client.RepoIssues(variables).pipe(Effect.catchDefect(Effect.fail), Effect.flip) + assert.instanceOf(defect, Error) + assert.strictEqual(defect.name, "UnimplementedError") + assert.strictEqual(defect.message, `GraphQLClient: Unimplemented operation "RepoIssues"`) + + const inherited = GraphQLClient.makeTest(GraphQLGroup.make(ToString))({}) + const inheritedDefect = yield* inherited.toString().pipe(Effect.catchDefect(Effect.fail), Effect.flip) + assert.instanceOf(inheritedDefect, Error) + assert.strictEqual(inheritedDefect.name, "UnimplementedError") + })) + + it.effect("GraphQLClient.pages drives a mock method across two pages", () => + Effect.gen(function*() { + const cursors: Array = [] + const client = GraphQLClient.makeTest(IssuesGroup)({ + RepoIssues: ({ after }) => { + cursors.push(after) + return Effect.succeed( + after === "c1" + ? page([issue(2)], { hasNextPage: false, endCursor: null }) + : page([issue(1)], { hasNextPage: true, endCursor: "c1" }) + ) + } + }) + + const pages = yield* Stream.runCollect( + GraphQLClient.pages(client.RepoIssues, { variables, connection: (r) => r.repository?.issues }) + ) + assert.deepStrictEqual(pages.map((p) => p.nodes?.map((i) => i?.number)), [[1], [2]]) + assert.deepStrictEqual(cursors, [undefined, "c1"]) + })) + + it.effect("layerTest provides the mock client through the service tag", () => + Effect.gen(function*() { + class Issues extends Context.Service()("test/Issues", { make: GraphQLClient.make(IssuesGroup) }) { + static readonly layerTest = GraphQLClient.layerTest(Issues, IssuesGroup) + } + + const result = page([issue(1)], { hasNextPage: false, endCursor: null }) + const actual = yield* Issues.use((client) => client.RepoIssues(variables)).pipe( + Effect.provide(Issues.layerTest({ RepoIssues: () => Effect.succeed(result) })) + ) + assert.strictEqual(actual, result) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLClient.pages.test.ts b/packages/effect/test/graphql/GraphQLClient.pages.test.ts new file mode 100644 index 000000000..8c9696b96 --- /dev/null +++ b/packages/effect/test/graphql/GraphQLClient.pages.test.ts @@ -0,0 +1,111 @@ +import { assert, describe, it } from "@effect/vitest" +import { Effect, type Layer, Ref, Stream } from "effect" +import { GraphQLClient } from "effect/graphql" +import type * as GraphQLProtocol from "effect/graphql/GraphQLProtocol" +import * as UrlParams from "effect/http/UrlParams" +import { expectReason, IssuesGroup, protocolLayer } from "./fixtures.ts" + +const issue = (number: number) => ({ number, title: `#${number}`, createdAt: "2026-01-01T00:00:00Z" }) + +const page = (nodes: ReadonlyArray, pageInfo: { hasNextPage: boolean; endCursor: string | null }) => ({ + data: { repository: { issues: { pageInfo, nodes } } } +}) + +const noRepository = { data: { repository: null } } + +/** Scripts one response per `after` cursor; the first page is keyed `start`. */ +const pagedLayer = (pages: Record) => + Effect.gen(function*() { + const requests = yield* Ref.make>([]) + const layer = protocolLayer({ + execute: (request) => + Effect.as( + Ref.update(requests, (all) => [...all, request]), + { body: pages[(request.variables as { after?: string | null }).after ?? "start"] } + ) + }) + return { layer, requests } + }) + +const twoPages = { + start: page([issue(1), null], { hasNextPage: true, endCursor: "c1" }), + c1: page([issue(2)], { hasNextPage: false, endCursor: null }) +} + +const variables = { owner: "Effect-TS", name: "effect" } + +const makeClient = (layer: Layer.Layer) => GraphQLClient.make(IssuesGroup).pipe(Effect.provide(layer)) + +describe("GraphQLClient.pages / items", () => { + it.effect("pages passes endCursor as $after until hasNextPage is false", () => + Effect.gen(function*() { + const { layer, requests } = yield* pagedLayer(twoPages) + const client = yield* makeClient(layer) + const pages = yield* Stream.runCollect( + GraphQLClient.pages(client.RepoIssues, { variables, connection: (r) => r.repository?.issues }) + ) + assert.deepStrictEqual(pages.map((p) => p.pageInfo.endCursor), ["c1", null]) + assert.deepStrictEqual( + (yield* Ref.get(requests)).map((r) => (r.variables as { after?: string | null }).after), + [undefined, "c1"] + ) + })) + + it.effect("items flattens nodes across pages and keeps null entries", () => + Effect.gen(function*() { + const { layer } = yield* pagedLayer(twoPages) + const client = yield* makeClient(layer) + const items = yield* Stream.runCollect( + GraphQLClient.items(client.RepoIssues, { variables, connection: (r) => r.repository?.issues }) + ) + assert.deepStrictEqual(items.map((i) => i === null ? null : i.number), [1, null, 2]) + })) + + it.effect("options are forwarded to every page request", () => + Effect.gen(function*() { + const { layer, requests } = yield* pagedLayer(twoPages) + const client = yield* makeClient(layer) + yield* Stream.runDrain(GraphQLClient.pages(client.RepoIssues, { + variables, + connection: (r) => r.repository?.issues, + options: { headers: { "x-page-run": "1" }, urlParams: { preview: "t" } } + })) + const sent = yield* Ref.get(requests) + assert.deepStrictEqual(sent.map((r) => r.headers["x-page-run"]), ["1", "1"]) + assert.deepStrictEqual(sent.map((r) => UrlParams.getAll(r.urlParams, "preview")), [["t"], ["t"]]) + })) + + it.effect("a null connection on the first page is an empty stream", () => + Effect.gen(function*() { + const { layer } = yield* pagedLayer({ start: noRepository }) + const client = yield* makeClient(layer) + const pages = yield* Stream.runCollect( + GraphQLClient.pages(client.RepoIssues, { variables, connection: (r) => r.repository?.issues }) + ) + assert.deepStrictEqual(pages, []) + })) + + it.effect("a null connection on a later page is a PaginationError naming the cursor used", () => + Effect.gen(function*() { + const { layer } = yield* pagedLayer({ ...twoPages, c1: noRepository }) + const client = yield* makeClient(layer) + const reason = yield* Stream.runCollect( + GraphQLClient.pages(client.RepoIssues, { variables, connection: (r) => r.repository?.issues }) + ).pipe(expectReason("PaginationError")) + assert.strictEqual(reason.cursor, "c1") + })) + + it.effect("a cursor that does not advance is a PaginationError instead of a refetch", () => + Effect.gen(function*() { + const { layer, requests } = yield* pagedLayer({ + ...twoPages, + c1: page([issue(2)], { hasNextPage: true, endCursor: "c1" }) + }) + const client = yield* makeClient(layer) + const reason = yield* Stream.runCollect( + GraphQLClient.pages(client.RepoIssues, { variables, connection: (r) => r.repository?.issues }) + ).pipe(expectReason("PaginationError")) + assert.strictEqual(reason.cursor, "c1") + assert.strictEqual((yield* Ref.get(requests)).length, 2) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLClient.subscriptions.test.ts b/packages/effect/test/graphql/GraphQLClient.subscriptions.test.ts new file mode 100644 index 000000000..25de84b4b --- /dev/null +++ b/packages/effect/test/graphql/GraphQLClient.subscriptions.test.ts @@ -0,0 +1,138 @@ +import { assert, describe, it } from "@effect/vitest" +import { Cause, Duration, Effect, Fiber, Layer, Ref, Schedule, Stream } from "effect" +import { GraphQLClient, GraphQLMiddleware } from "effect/graphql" +import { TransportError } from "effect/graphql/GraphQLClientError" +import { TestClock } from "effect/testing" +import { expectReason, IssuesGroup, protocolLayer } from "./fixtures.ts" + +const event = (title: string) => ({ data: { issueUpdated: { title } } }) + +const lost = (fields?: Partial[0]>) => + Stream.fail(new TransportError({ description: "connection lost", ...fields })) + +/** + * A protocol whose `subscribe` hands out one scripted stream per attempt and + * counts the attempts. + */ +const scriptedLayer = (attempts: ReadonlyArray>) => + Effect.gen(function*() { + const count = yield* Ref.make(0) + const layer = protocolLayer({ + subscribe: () => + Stream.unwrap( + Effect.map( + Ref.updateAndGet(count, (n) => n + 1), + (n) => Stream.map(attempts[n - 1] ?? Stream.die(`attempt ${n} not scripted`), (body) => ({ body })) + ) + ) + }) + return { layer, attempts: Ref.get(count) } + }) + +const subscribe = ( + layer: Layer.Layer, + options?: Parameters[1] +) => + GraphQLClient.make(IssuesGroup, options).pipe( + Effect.map((client) => client.IssueUpdated({ id: "I_1" })), + Effect.provide(layer), + Stream.unwrap + ) + +describe("GraphQLClient subscriptions", () => { + it.effect("emits each decoded event and ends when the transport completes", () => + Effect.gen(function*() { + const { layer } = yield* scriptedLayer([Stream.make(event("a"), event("b"))]) + const events = yield* Stream.runCollect(subscribe(layer)) + assert.deepStrictEqual(events, [{ issueUpdated: { title: "a" } }, { issueUpdated: { title: "b" } }]) + })) + + it.effect("an event with errors fails the stream with ResponseError without retrying", () => + Effect.gen(function*() { + const received = yield* Ref.make>([]) + const { layer, attempts } = yield* scriptedLayer([ + Stream.make(event("a"), { data: null, errors: [{ message: "gone" }] }, event("c")) + ]) + const reason = yield* Stream.runForEach( + subscribe(layer), + (e) => Ref.update(received, (all) => [...all, e.issueUpdated.title]) + ).pipe(expectReason("ResponseError")) + assert.deepStrictEqual(reason.errors, [{ message: "gone" }]) + assert.isUndefined(reason.status) + assert.isFalse(reason.isRetryable) + assert.deepStrictEqual(yield* Ref.get(received), ["a"]) + assert.strictEqual(yield* attempts, 1) + })) + + it.effect("retries a retryable TransportError on the default schedule (500ms, then x1.5)", () => + Effect.gen(function*() { + const { layer, attempts } = yield* scriptedLayer([lost(), lost(), Stream.make(event("a"))]) + const fiber = yield* Stream.runCollect(subscribe(layer)).pipe(Effect.forkChild) + yield* TestClock.adjust("499 millis") + assert.strictEqual(yield* attempts, 1) + yield* TestClock.adjust("1 millis") + assert.strictEqual(yield* attempts, 2) + yield* TestClock.adjust("749 millis") + assert.strictEqual(yield* attempts, 2) + yield* TestClock.adjust("1 millis") + assert.strictEqual(yield* attempts, 3) + assert.deepStrictEqual(yield* Fiber.join(fiber), [{ issueUpdated: { title: "a" } }]) + })) + + it.effect("waits for retryAfter when the transport reports one", () => + Effect.gen(function*() { + const { layer, attempts } = yield* scriptedLayer([ + lost({ retryAfter: Duration.seconds(2) }), + Stream.make(event("a")) + ]) + const fiber = yield* Stream.runCollect(subscribe(layer)).pipe(Effect.forkChild) + yield* TestClock.adjust("1999 millis") + assert.strictEqual(yield* attempts, 1) + yield* TestClock.adjust("1 millis") + assert.strictEqual(yield* attempts, 2) + yield* Fiber.join(fiber) + })) + + it.effect("a fatal close code fails the stream without retrying", () => + Effect.gen(function*() { + const { layer, attempts } = yield* scriptedLayer([lost({ closeCode: 4401 }), Stream.make(event("a"))]) + const reason = yield* Stream.runCollect(subscribe(layer)).pipe(expectReason("TransportError")) + assert.strictEqual(reason.closeCode, 4401) + assert.strictEqual(yield* attempts, 1) + })) + + it.effect("fails with the last TransportError once subscriptionRetry is exhausted", () => + Effect.gen(function*() { + const { layer, attempts } = yield* scriptedLayer([lost(), lost({ description: "lost again" }), lost()]) + const fiber = yield* Stream.runCollect(subscribe(layer, { subscriptionRetry: Schedule.recurs(1) })) + .pipe(expectReason("TransportError"), Effect.forkChild) + yield* TestClock.adjust("1 minute") + assert.strictEqual((yield* Fiber.join(fiber)).description, "lost again") + assert.strictEqual(yield* attempts, 2) + })) + + it.effect("a middleware failure is not retried and does not step subscriptionRetry", () => + Effect.gen(function*() { + const failure = { message: "subscription denied" } + class Reject extends GraphQLMiddleware.Service()("test/Reject") {} + const subscribes = yield* Ref.make(0) + const steps = yield* Ref.make(0) + const RejectLive = Layer.succeed(Reject, { + execute: ({ next, request }) => next(request), + subscribe: () => Stream.unwrap(Effect.as(Ref.update(subscribes, (n) => n + 1), Stream.fail(failure))) + }) + const subscriptionRetry = Schedule.fromStep( + Effect.succeed(() => Effect.andThen(Ref.update(steps, (n) => n + 1), Cause.done(undefined))) + ) + const error = yield* GraphQLClient.make(IssuesGroup.middleware(Reject), { subscriptionRetry }).pipe( + Effect.map((client) => client.IssueUpdated({ id: "I_1" })), + Effect.provide([protocolLayer({}), RejectLive]), + Stream.unwrap, + Stream.runCollect, + Effect.flip + ) + assert.strictEqual(error, failure) + assert.strictEqual(yield* Ref.get(subscribes), 1) + assert.strictEqual(yield* Ref.get(steps), 0) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLClient.test.ts b/packages/effect/test/graphql/GraphQLClient.test.ts new file mode 100644 index 000000000..f78598ffa --- /dev/null +++ b/packages/effect/test/graphql/GraphQLClient.test.ts @@ -0,0 +1,195 @@ +import { assert, describe, it } from "@effect/vitest" +import { Context, DateTime, Effect, Layer, Ref, Schema } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLMiddleware } from "effect/graphql" +import * as Headers from "effect/http/Headers" +import { executeLayer, expectReason, IssuesGroup, RepoIssues, Viewer, ViewerGroup } from "./fixtures.ts" + +class CurrentUser extends Context.Service()("test/CurrentUser") {} +class TokenExpired extends Schema.TaggedError()("TokenExpired", { user: Schema.String }) {} + +class Auth extends GraphQLMiddleware.Service()("test/Auth") {} +class OpA extends GraphQLMiddleware.Service()("test/OpA") {} +class OpB extends GraphQLMiddleware.Service()("test/OpB") {} +class GroupA extends GraphQLMiddleware.Service()("test/GroupA") {} +class GroupB extends GraphQLMiddleware.Service()("test/GroupB") {} + +const AuthLive = Layer.succeed( + Auth, + GraphQLMiddleware.mapRequest(Effect.fnUntraced(function*(request) { + assert.isTrue(Headers.isHeaders(request.headers)) + assert.notProperty(request.headers, "X-Request-Id") + const user = yield* CurrentUser + if (user.id === "expired") return yield* new TokenExpired({ user: user.id }) + return { ...request, headers: Headers.set(request.headers, "authorization", `Bearer ${user.id}`) } + })) +) + +const viewerData = { data: { viewer: { login: "tim" } } } + +const variables = { owner: "Effect-TS", name: "effect" } + +const partialIssues = { + data: { + repository: { + issues: { + pageInfo: { hasNextPage: false, endCursor: null }, + nodes: [{ number: 1, title: "Hi", createdAt: "2026-01-01T00:00:00Z" }, null] + } + } + }, + errors: [{ type: "NOT_FOUND", message: "Not found", path: ["repository", "issues", "nodes", 1] }] +} + +const repoIssuesClient = (result: unknown) => + executeLayer(result).pipe( + Effect.flatMap(({ layer }) => GraphQLClient.make(IssuesGroup).pipe(Effect.provide(layer))) + ) + +describe("GraphQLClient", () => { + describe("middleware", () => { + it.effect("group middleware runs outside operation middleware, attach order within a level", () => + Effect.gen(function*() { + const log = yield* Ref.make>([]) + const record = (name: string) => + GraphQLMiddleware.mapRequest((request) => Effect.as(Ref.update(log, (all) => [...all, name]), request)) + + const group = GraphQLGroup.make(Viewer.middleware(OpA).middleware(OpB)) + .middleware(GroupA) + .middleware(GroupB) + const { layer } = yield* executeLayer(viewerData) + + yield* GraphQLClient.make(group).pipe( + Effect.flatMap((client) => client.Viewer()), + Effect.provide([ + layer, + Layer.succeed(OpA, record("OpA")), + Layer.succeed(OpB, record("OpB")), + Layer.succeed(GroupA, record("GroupA")), + Layer.succeed(GroupB, record("GroupB")) + ]) + ) + assert.deepStrictEqual(yield* Ref.get(log), ["GroupA", "GroupB", "OpA", "OpB"]) + })) + + it.effect("mapRequest transforms the request and its errors reach the caller", () => + Effect.gen(function*() { + const { layer, requests } = yield* executeLayer(viewerData) + const client = yield* GraphQLClient.make(ViewerGroup.middleware(Auth)).pipe(Effect.provide([layer, AuthLive])) + + yield* client.Viewer().pipe(Effect.provideService(CurrentUser, { id: "tim" })) + assert.strictEqual((yield* Ref.get(requests))[0].headers["authorization"], "Bearer tim") + + const error = yield* client.Viewer().pipe(Effect.provideService(CurrentUser, { id: "expired" }), Effect.flip) + assert.instanceOf(error, TokenExpired) + assert.strictEqual((yield* Ref.get(requests)).length, 1) + })) + + it.effect("per-call context provides services and headers are normalized before middleware", () => + Effect.gen(function*() { + const { layer, requests } = yield* executeLayer(viewerData) + const client = yield* GraphQLClient.make(ViewerGroup.middleware(Auth)).pipe(Effect.provide([layer, AuthLive])) + yield* client.Viewer(undefined, { + context: Context.make(CurrentUser, { id: "ctx" }), + headers: [["X-Request-Id", "abc"]] + }) + assert.deepStrictEqual( + (yield* Ref.get(requests))[0].headers, + Headers.fromInput({ + authorization: "Bearer ctx", + "x-request-id": "abc" + }) + ) + })) + + it.effect("middleware see the transport status from next and may rewrite it; ResponseError carries it", () => + Effect.gen(function*() { + class Status extends GraphQLMiddleware.Service()("test/Status") {} + const StatusLive = Layer.succeed(Status, { + execute: ({ next, request }) => + Effect.map(next(request), (result) => ({ ...result, status: result.status === 401 ? 403 : 500 })), + subscribe: ({ next, request }) => next(request) + }) + const { layer } = yield* executeLayer({ errors: [{ message: "denied" }] }, 401) + const reason = yield* GraphQLClient.make(ViewerGroup.middleware(Status)).pipe( + Effect.flatMap((client) => client.Viewer()), + Effect.provide([layer, StatusLive]), + expectReason("ResponseError") + ) + assert.strictEqual(reason.status, 403) + })) + }) + + it.effect("encodes variables through the custom scalar codec", () => + Effect.gen(function*() { + const { layer, requests } = yield* executeLayer({ data: { repository: null } }) + yield* GraphQLClient.make(IssuesGroup).pipe( + Effect.flatMap((client) => + client.RepoIssues({ ...variables, since: DateTime.makeUnsafe("2026-01-01T00:00:00Z") }) + ), + Effect.provide(layer) + ) + const [request] = yield* Ref.get(requests) + assert.strictEqual(request.operationName, "RepoIssues") + assert.strictEqual(request.query, RepoIssues.document) + assert.deepStrictEqual(request.variables, { ...variables, since: "2026-01-01T00:00:00.000Z" }) + })) + + it.effect("a rejected input is an EncodeError and nothing is sent", () => + Effect.gen(function*() { + const First = GraphQL.query("First", { + document: "query First($first:Int!){viewer{login}}", + variables: { first: Schema.Int.check(Schema.isGreaterThan(0)) }, + result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) + }) + const { layer, requests } = yield* executeLayer(viewerData) + yield* GraphQLClient.make(GraphQLGroup.make(First)).pipe( + Effect.flatMap((client) => client.First({ first: 0 })), + Effect.provide(layer), + expectReason("EncodeError") + ) + assert.deepStrictEqual(yield* Ref.get(requests), []) + })) + + it.effect("a response with errors fails with ResponseError carrying the raw data and errors", () => + Effect.gen(function*() { + const client = yield* repoIssuesClient(partialIssues) + const reason = yield* expectReason("ResponseError")(client.RepoIssues(variables)) + assert.deepStrictEqual(reason.data, partialIssues.data) + assert.deepStrictEqual(reason.errors, partialIssues.errors) + })) + + it.effect("data that does not match the result schema is a DecodeError", () => + Effect.gen(function*() { + const { layer } = yield* executeLayer({ data: { viewer: { login: 42 } } }) + yield* GraphQLClient.make(ViewerGroup).pipe( + Effect.flatMap((client) => client.Viewer()), + Effect.provide(layer), + expectReason("DecodeError") + ) + })) + + describe("partial: true", () => { + it.effect("returns decoded data alongside the errors", () => + Effect.gen(function*() { + const client = yield* repoIssuesClient(partialIssues) + const { data, errors } = yield* client.RepoIssues(variables, { partial: true }) + assert.strictEqual(data.repository!.issues!.nodes![0]!.title, "Hi") + assert.deepStrictEqual(errors, partialIssues.errors) + })) + + it.effect("data: null plus errors is a ResponseError", () => + Effect.gen(function*() { + const client = yield* repoIssuesClient({ data: null, errors: [{ message: "boom" }] }) + yield* expectReason("ResponseError")(client.RepoIssues(variables, { partial: true })) + })) + }) + + it.effect("data: null with no errors is a DecodeError even when the result codec accepts null", () => + Effect.gen(function*() { + const Raw = GraphQL.query("Raw", { document: "query Raw{__typename}", result: Schema.Json }) + const { layer } = yield* executeLayer({ data: null }) + const client = yield* GraphQLClient.make(GraphQLGroup.make(Raw)).pipe(Effect.provide(layer)) + yield* expectReason("DecodeError")(client.Raw()) + yield* expectReason("DecodeError")(client.Raw(undefined, { partial: true })) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLClientError.test.ts b/packages/effect/test/graphql/GraphQLClientError.test.ts new file mode 100644 index 000000000..a8a004227 --- /dev/null +++ b/packages/effect/test/graphql/GraphQLClientError.test.ts @@ -0,0 +1,85 @@ +import { assert, describe, it } from "@effect/vitest" +import { Duration, Effect, Schema } from "effect" +import { GraphQLClientError, GraphQLError, ResponseError, TransportError } from "effect/graphql/GraphQLClientError" +import * as Headers from "effect/http/Headers" + +describe("GraphQLClientError", () => { + it("decoding a GitHub error preserves its top-level type", () => { + const error = { + type: "NOT_FOUND", + path: ["repository"], + locations: [{ line: 1, column: 3 }], + message: "Could not resolve to a Repository with the name 'Effect-TS/missing'." + } + assert.deepStrictEqual(Schema.decodeUnknownSync(GraphQLError)(error), error) + }) + + it.effect("round trips both error headers as JSON records, encoding retryAfter as milliseconds", () => + Effect.gen(function*() { + const headers = Headers.fromInput({ "X-RateLimit-Remaining": "0" }) + for ( + const reason of [ + new TransportError({ description: "503", status: 503, retryAfter: Duration.seconds(30), headers }), + new ResponseError({ errors: [{ message: "Rate limited" }], status: 200, headers }) + ] + ) { + const error = new GraphQLClientError({ operation: "Viewer", reason }) + const encoded = yield* Schema.encodeEffect(GraphQLClientError)(error) + if (encoded.reason._tag !== "TransportError" && encoded.reason._tag !== "ResponseError") { + assert.fail("Expected an encoded transport or response error") + } + assert.deepStrictEqual(encoded.reason.headers, { "x-ratelimit-remaining": "0" }) + const decoded = yield* Schema.decodeUnknownEffect(GraphQLClientError)(JSON.parse(JSON.stringify(encoded))) + assert.strictEqual(decoded.reason._tag, reason._tag) + if (decoded.reason._tag !== "TransportError" && decoded.reason._tag !== "ResponseError") { + assert.fail("Expected a decoded transport or response error") + } + assert.isTrue(Headers.isHeaders(decoded.reason.headers)) + assert.deepStrictEqual(decoded.reason.headers, headers) + if (reason._tag === "TransportError") { + assert.strictEqual((encoded.reason as { retryAfter?: number }).retryAfter, 30_000) + assert.deepStrictEqual(decoded.retryAfter, Duration.seconds(30)) + } + } + })) + + it("TransportError is retryable for 429, 5xx and non-fatal close codes", () => { + const transport = (fields: { status?: number; closeCode?: number }) => + new TransportError({ description: "transport", ...fields }).isRetryable + assert.isTrue(transport({ status: 429 })) + assert.isTrue(transport({ status: 503 })) + assert.isFalse(transport({ status: 400 })) + assert.isFalse(transport({ status: 600 })) + assert.isTrue(transport({ closeCode: 1006 })) + assert.isFalse(transport({ closeCode: 4401 })) + assert.isFalse(transport({ closeCode: 4500 })) + assert.isFalse(transport({ closeCode: 1011 })) + }) + + it("a Retry-After that is neither whole seconds nor an HTTP date is ignored", () => { + for (const retryAfter of ["1.5", "-1"]) { + const reason = new ResponseError({ + errors: [{ message: "Bad request" }], + status: 400, + headers: Headers.fromInput({ "retry-after": retryAfter }) + }) + assert.isUndefined(reason.retryAfter, retryAfter) + assert.isFalse(reason.isRetryable, retryAfter) + } + }) + + it("a 403 with Retry-After is retryable, and its status and headers are on the error", () => { + const headers = Headers.fromInput({ "retry-after": "30" }) + for ( + const reason of [ + new ResponseError({ errors: [{ message: "Rate limited" }], status: 403, headers }), + new TransportError({ description: "403", status: 403, headers, retryAfter: Duration.seconds(30) }) + ] + ) { + const error = new GraphQLClientError({ operation: "Viewer", reason }) + assert.isTrue(error.isRetryable) + assert.strictEqual(error.status, 403) + assert.deepStrictEqual(error.headers, headers) + } + }) +}) diff --git a/packages/effect/test/graphql/GraphQLProtocol.sse.test.ts b/packages/effect/test/graphql/GraphQLProtocol.sse.test.ts new file mode 100644 index 000000000..1d600b601 --- /dev/null +++ b/packages/effect/test/graphql/GraphQLProtocol.sse.test.ts @@ -0,0 +1,167 @@ +import { assert, describe, it } from "@effect/vitest" +import { Deferred, Duration, Effect, Fiber, Layer, Stream } from "effect" +import { GraphQLClient, GraphQLProtocol } from "effect/graphql" +import { TransportError } from "effect/graphql/GraphQLClientError" +import * as Headers from "effect/http/Headers" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientResponse from "effect/http/HttpClientResponse" +import * as UrlParams from "effect/http/UrlParams" +import { TestClock } from "effect/testing" +import { expectReason, graphqlResponse, httpClientLayer, IssuesGroup, IssueUpdated } from "./fixtures.ts" + +const url = "http://localhost/graphql" + +const event = (title: string) => ({ data: { issueUpdated: { title } } }) + +const subscription: GraphQLProtocol.GraphQLRequest = { + kind: "subscription", + query: IssueUpdated.document, + operationName: "IssueUpdated", + variables: { id: "I_1" }, + headers: Headers.fromInput({ authorization: "Bearer t" }), + urlParams: UrlParams.fromInput({ preview: "t" }) +} + +/** + * A `text/event-stream` body that sends `chunks`, then fails with `error` or + * stays open. + */ +const eventStream = (chunks: ReadonlyArray, error?: Error) => + new Response( + new ReadableStream({ + start(controller) { + for (const chunk of chunks) controller.enqueue(new TextEncoder().encode(chunk)) + }, + pull(controller) { + // Error only after the queued chunks are read; erroring in start discards them. + if (error !== undefined) controller.error(error) + } + }), + { headers: { "content-type": "text/event-stream" } } + ) + +const next = (payload: unknown) => `event: next\ndata: ${JSON.stringify(payload)}\n\n` + +const subscribe = (layer: Layer.Layer) => + GraphQLProtocol.makeHttp({ url }).pipe( + Effect.map((protocol) => protocol.subscribe(subscription)), + Effect.provide(layer), + Stream.unwrap + ) + +describe("GraphQLProtocol.layerHttp subscriptions (graphql-sse distinct mode)", () => { + it.effect("POSTs with Accept: text/event-stream and emits next events, without a status, until complete", () => + Effect.gen(function*() { + let seen: { headers: Record; body: unknown; url: URL } | undefined + const events = yield* subscribe(httpClientLayer((request, bodyText, url) => { + seen = { headers: request.headers, body: JSON.parse(bodyText!), url } + return eventStream([next(event("a")), next(event("b")), "event: complete\ndata:\n\n"]) + })).pipe(Stream.runCollect) + assert.deepStrictEqual(events, [ + { body: event("a"), headers: Headers.fromInput({ "content-type": "text/event-stream" }) }, + { body: event("b"), headers: Headers.fromInput({ "content-type": "text/event-stream" }) } + ]) + assert.include(seen!.headers["accept"], "text/event-stream") + assert.strictEqual(seen!.headers["authorization"], "Bearer t") + assert.strictEqual(seen!.url.searchParams.get("preview"), "t") + assert.deepStrictEqual(seen!.body, { + query: IssueUpdated.document, + operationName: "IssueUpdated", + variables: { id: "I_1" } + }) + })) + + it.effect("a body that ends without a complete event is a retryable TransportError", () => + Effect.gen(function*() { + const error = yield* subscribe( + httpClientLayer(() => new Response(next(event("a")), { headers: { "content-type": "text/event-stream" } })) + ).pipe(Stream.runDrain, Effect.flip) + assert.instanceOf(error, TransportError) + assert.isTrue(error.isRetryable) + })) + + it.live("a complete event without a data: line ends the stream", () => + Effect.gen(function*() { + const events = yield* subscribe(httpClientLayer(() => eventStream([next(event("a")), "event: complete\n\n"]))) + .pipe( + Stream.runCollect, + Effect.timeout("2 seconds") + ) + assert.deepStrictEqual(events.map((answer) => answer.body), [event("a")]) + })) + + it.effect("a retry: line becomes retryAfter when the stream then fails", () => + Effect.gen(function*() { + const error = yield* subscribe( + httpClientLayer(() => eventStream([next(event("a")), "retry: 3000\n\n"], new Error("connection reset"))) + ).pipe(Stream.runDrain, Effect.flip) + assert.instanceOf(error, TransportError) + assert.isTrue(error.isRetryable) + assert.deepStrictEqual(error.retryAfter, Duration.seconds(3)) + })) + + it.effect("a GraphQL error response instead of a stream fails the client stream with ResponseError", () => + Effect.gen(function*() { + const layer = GraphQLProtocol.layerHttp({ url }).pipe( + Layer.provide(httpClientLayer(() => graphqlResponse({ errors: [{ message: "denied" }] }, { status: 400 }))) + ) + const reason = yield* GraphQLClient.make(IssuesGroup).pipe( + Effect.map((client) => client.IssueUpdated({ id: "I_1" })), + Effect.provide(layer), + Stream.unwrap, + Stream.runDrain, + expectReason("ResponseError") + ) + assert.deepStrictEqual(reason.errors, [{ message: "denied" }]) + assert.strictEqual(reason.status, 400) + })) + + it.effect("a client stream refused with 403 and Retry-After reconnects after the delay", () => + Effect.gen(function*() { + let attempts = 0 + const layer = GraphQLProtocol.layerHttp({ url }).pipe( + Layer.provide(httpClientLayer(() => + ++attempts === 1 + ? new Response(JSON.stringify({ message: "You have exceeded a secondary rate limit" }), { + status: 403, + headers: { "content-type": "application/json", "retry-after": "2" } + }) + : eventStream([next(event("a")), "event: complete\ndata:\n\n"]) + )) + ) + const fiber = yield* GraphQLClient.make(IssuesGroup).pipe( + Effect.map((client) => client.IssueUpdated({ id: "I_1" })), + Effect.provide(layer), + Stream.unwrap, + Stream.runCollect, + Effect.forkChild + ) + yield* TestClock.adjust("1999 millis") + assert.strictEqual(attempts, 1) + yield* TestClock.adjust("1 millis") + assert.deepStrictEqual(yield* Fiber.join(fiber), [{ issueUpdated: { title: "a" } }]) + assert.strictEqual(attempts, 2) + })) + + it.effect("interrupting the stream aborts the request", () => + Effect.gen(function*() { + let signal: AbortSignal | undefined + const layer = Layer.succeed( + HttpClient.HttpClient, + HttpClient.make((request, _url, abort) => { + signal = abort + return Effect.succeed(HttpClientResponse.fromWeb(request, eventStream([next(event("a"))]))) + }) + ) + const received = yield* Deferred.make() + const fiber = yield* subscribe(layer).pipe( + Stream.runForEach(() => Deferred.succeed(received, void 0)), + Effect.forkChild + ) + // Join too, so a stream that fails before its first event fails the test. + yield* Deferred.await(received).pipe(Effect.raceFirst(Fiber.join(fiber))) + assert.isFalse(signal!.aborted) + yield* Fiber.interrupt(fiber) + assert.isTrue(signal!.aborted) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLProtocol.test.ts b/packages/effect/test/graphql/GraphQLProtocol.test.ts new file mode 100644 index 000000000..ffcbb783c --- /dev/null +++ b/packages/effect/test/graphql/GraphQLProtocol.test.ts @@ -0,0 +1,286 @@ +import { assert, describe, it } from "@effect/vitest" +import { Context, Duration, Effect, Layer, Schema, Stream } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLMiddleware, GraphQLProtocol } from "effect/graphql" +import * as Headers from "effect/http/Headers" +import * as HttpClient from "effect/http/HttpClient" +import * as HttpClientError from "effect/http/HttpClientError" +import { + expectReason, + graphqlResponse, + httpClientLayer, + IssueUpdated, + layerHttp, + RepoIssues, + Viewer, + ViewerGroup +} from "./fixtures.ts" + +const viewerData = { data: { viewer: { login: "tim" } } } + +const AddStar = GraphQL.mutation("AddStar", { + document: "mutation AddStar{addStar{ok}}", + result: Schema.Struct({ addStar: Schema.Struct({ ok: Schema.Boolean }) }) +}) + +const GitHubHost = Context.Reference("test/GitHubHost", { defaultValue: () => "api.github.com" }) + +/** Every value of every URL param, so a duplicated or missing param shows. */ +const urlParams = (url: URL) => + Object.fromEntries([...new Set(url.searchParams.keys())].map((key) => [key, url.searchParams.getAll(key)])) + +const callViewer = (handler: Parameters[0]) => + GraphQLClient.make(ViewerGroup).pipe( + Effect.flatMap((client) => client.Viewer()), + Effect.provide(layerHttp(handler)) + ) + +const unavailable = (retryAfter: string) => + new Response("down", { status: 503, headers: { "retry-after": retryAfter, "content-type": "text/plain" } }) + +describe("GraphQLProtocol.layerHttp", () => { + it.effect("POSTs the document, operation name and variables as JSON with per-call headers and URL params", () => + Effect.gen(function*() { + let seen: { method: string; headers: Record; body: unknown; url: URL } | undefined + const result = yield* GraphQLClient.make(ViewerGroup).pipe( + Effect.flatMap((client) => + client.Viewer(undefined, { headers: { "x-request-id": "abc" }, urlParams: { preview: "t" } }) + ), + Effect.provide(layerHttp((request, bodyText, url) => { + seen = { method: request.method, headers: request.headers, body: JSON.parse(bodyText!), url } + return graphqlResponse(viewerData) + })) + ) + assert.deepStrictEqual(result, { viewer: { login: "tim" } }) + assert.strictEqual(seen!.method, "POST") + assert.deepStrictEqual(urlParams(seen!.url), { preview: ["t"] }) + assert.strictEqual(seen!.headers["x-request-id"], "abc") + assert.include(seen!.headers["accept"], "application/graphql-response+json") + assert.deepStrictEqual(seen!.body, { query: Viewer.document, operationName: "Viewer", variables: {} }) + })) + + it.effect("queryMethod GET sends a query as URL params over the URL's and caller's; a mutation is still POSTed", () => + Effect.gen(function*() { + const seen: Array<{ method: string; accept: string | undefined; bodyText: string | undefined; url: URL }> = [] + const layer = GraphQLProtocol.layerHttp({ + url: "http://localhost/graphql?tenant=a&query=stale&operationName=Stale", + queryMethod: "GET" + }).pipe( + Layer.provide(httpClientLayer((request, bodyText, url) => { + seen.push({ method: request.method, accept: request.headers["accept"], bodyText, url }) + return graphqlResponse( + request.method === "GET" ? { data: { repository: null } } : { data: { addStar: { ok: true } } } + ) + })) + ) + const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoIssues, AddStar)).pipe(Effect.provide(layer)) + const variables = { owner: "Effect-TS", name: "effect" } + yield* client.RepoIssues(variables, { urlParams: { preview: "t", query: "ignored" } }) + yield* client.AddStar() + + const [query, mutation] = seen + assert.strictEqual(query.method, "GET") + assert.isUndefined(query.bodyText) + assert.include(query.accept, "application/graphql-response+json") + assert.deepStrictEqual(urlParams(query.url), { + tenant: ["a"], + preview: ["t"], + query: [RepoIssues.document], + operationName: ["RepoIssues"], + variables: [JSON.stringify(variables)] + }) + assert.strictEqual(mutation.method, "POST") + assert.strictEqual(JSON.parse(mutation.bodyText!).operationName, "AddStar") + })) + + it.effect("a url function routes queries, mutations and graphql-sse subscriptions by context and operation", () => + Effect.gen(function*() { + const seen: Array = [] + const layer = GraphQLProtocol.layerHttp({ + url: ({ context, kind, operationName }) => + `http://${Context.get(context, GitHubHost)}/graphql?route=${kind}.${operationName}`, + queryMethod: "GET" + }).pipe( + Layer.provide(httpClientLayer((request, _, url) => { + seen.push(`${request.method} ${url.host} ${urlParams(url).route}`) + return request.headers["accept"] === "text/event-stream" + ? new Response("event: complete\ndata:\n\n", { headers: { "content-type": "text/event-stream" } }) + : graphqlResponse(request.method === "GET" ? viewerData : { data: { addStar: { ok: true } } }) + })) + ) + const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer, AddStar, IssueUpdated)).pipe( + Effect.provide(layer) + ) + + yield* client.Viewer() + yield* client.Viewer().pipe(Effect.provideService(GitHubHost, "api.acme.ghe.com")) + yield* client.AddStar(undefined, { context: Context.make(GitHubHost, "ghes.example.com") }) + yield* client.IssueUpdated({ id: "I_1" }).pipe( + Stream.runDrain, + Effect.provideService(GitHubHost, "api.acme.ghe.com") + ) + + assert.deepStrictEqual(seen, [ + "GET api.github.com query.Viewer", + "GET api.acme.ghe.com query.Viewer", + "POST ghes.example.com mutation.AddStar", + "POST api.acme.ghe.com subscription.IssueUpdated" + ]) + })) + + it.effect("an Effect url is resolved asynchronously in the context of each query, mutation and subscription", () => + Effect.gen(function*() { + const seen: Array = [] + const layer = GraphQLProtocol.layerHttp({ + url: Effect.gen(function*() { + yield* Effect.promise(() => Promise.resolve()) + return `http://${yield* GitHubHost}/graphql` + }) + }).pipe( + Layer.provide(httpClientLayer((request, bodyText, url) => { + seen.push(`${url.host} ${JSON.parse(bodyText!).operationName}`) + return request.headers["accept"] === "text/event-stream" + ? new Response("event: complete\ndata:\n\n", { headers: { "content-type": "text/event-stream" } }) + : graphqlResponse({ data: { viewer: { login: "tim" }, addStar: { ok: true } } }) + })) + ) + const client = yield* GraphQLClient.make(GraphQLGroup.make(Viewer, AddStar, IssueUpdated)).pipe( + Effect.provide(layer) + ) + + yield* client.Viewer() + yield* client.Viewer().pipe(Effect.provideService(GitHubHost, "api.acme.ghe.com")) + yield* client.AddStar(undefined, { context: Context.make(GitHubHost, "ghes.example.com") }) + yield* client.IssueUpdated({ id: "I_1" }).pipe( + Stream.runDrain, + Effect.provideService(GitHubHost, "api.acme.ghe.com") + ) + + assert.deepStrictEqual(seen, [ + "api.github.com Viewer", + "api.acme.ghe.com Viewer", + "ghes.example.com AddStar", + "api.acme.ghe.com IssueUpdated" + ]) + })) + + it.effect("accepts an application/json body with GraphQL keys, matching the media type case-insensitively", () => + Effect.gen(function*() { + for (const contentType of ["application/json", "Application/JSON; charset=utf-8"]) { + const result = yield* callViewer(() => graphqlResponse(viewerData, { contentType })) + assert.deepStrictEqual(result, { viewer: { login: "tim" } }) + } + })) + + it.effect("a 200 application/json body without data or errors is a DecodeError", () => + Effect.gen(function*() { + yield* callViewer(() => graphqlResponse({ ok: true }, { contentType: "application/json" })).pipe( + expectReason("DecodeError") + ) + })) + + for (const [status, isRetryable] of [[502, true], [400, false]] as const) { + it.effect(`a ${status} with a GraphQL body is a ResponseError with isRetryable: ${isRetryable}`, () => + Effect.gen(function*() { + const reason = yield* callViewer(() => + graphqlResponse({ data: null, errors: [{ message: "Query failed" }] }, { status }) + ).pipe(expectReason("ResponseError")) + assert.deepStrictEqual(reason.errors, [{ message: "Query failed" }]) + assert.strictEqual(reason.status, status) + assert.strictEqual(reason.isRetryable, isRetryable) + })) + } + + it.effect("a 429 with a GraphQL body is a ResponseError carrying a numeric Retry-After", () => + Effect.gen(function*() { + const reason = yield* callViewer(() => + new Response(JSON.stringify({ data: null, errors: [{ message: "Rate limited" }] }), { + status: 429, + headers: { "content-type": "application/graphql-response+json", "retry-after": "30" } + }) + ).pipe(expectReason("ResponseError")) + assert.strictEqual(reason.status, 429) + assert.deepStrictEqual(reason.retryAfter, Duration.seconds(30)) + assert.isTrue(reason.isRetryable) + })) + + it.effect("HTTP response headers reach middleware through next and ResponseError", () => + Effect.gen(function*() { + class RateLimit extends GraphQLMiddleware.Service()("test/RateLimit") {} + let seen: GraphQLProtocol.ExecutionResult | undefined + const headers = { "content-type": "application/graphql-response+json", "x-ratelimit-remaining": "0" } + const body = JSON.stringify({ errors: [{ message: "Rate limited", extensions: { code: "RATE_LIMITED" } }] }) + const reason = yield* GraphQLClient.make(ViewerGroup.middleware(RateLimit)).pipe( + Effect.flatMap((client) => client.Viewer()), + Effect.provide([ + layerHttp(() => new Response(body, { headers })), + Layer.succeed(RateLimit, { + execute: ({ next, request }) => Effect.tap(next(request), (result) => Effect.sync(() => seen = result)), + subscribe: ({ next, request }) => next(request) + }) + ]), + expectReason("ResponseError") + ) + assert.deepStrictEqual(seen?.headers, Headers.fromInput(headers)) + assert.deepStrictEqual(reason.headers, Headers.fromInput(headers)) + })) + + it.effect("an application/json 429 whose errors is not a list is a TransportError carrying its Retry-After", () => + Effect.gen(function*() { + const reason = yield* callViewer(() => + new Response(JSON.stringify({ errors: "Throttled" }), { + status: 429, + headers: { "content-type": "application/json", "retry-after": "5" } + }) + ).pipe(expectReason("TransportError")) + assert.strictEqual(reason.status, 429) + assert.deepStrictEqual(reason.retryAfter, Duration.seconds(5)) + })) + + it.effect("a non-GraphQL 403 is a TransportError carrying response headers without Retry-After", () => + Effect.gen(function*() { + const headers = { "content-type": "text/plain", "x-ratelimit-remaining": "0", "x-ratelimit-reset": "1791768600" } + const reason = yield* callViewer(() => new Response("Secondary rate limit", { status: 403, headers })).pipe( + expectReason("TransportError") + ) + assert.strictEqual(reason.status, 403) + assert.deepStrictEqual(reason.headers, Headers.fromInput(headers)) + })) + + it.effect("a non-GraphQL 503 is a TransportError carrying a numeric Retry-After", () => + Effect.gen(function*() { + const reason = yield* callViewer(() => unavailable("30")).pipe(expectReason("TransportError")) + assert.strictEqual(reason.status, 503) + assert.deepStrictEqual(reason.retryAfter, Duration.seconds(30)) + assert.isTrue(HttpClientError.isHttpClientError(reason.cause)) + })) + + it.effect("an HTTP-date Retry-After is measured from the Effect clock", () => + Effect.gen(function*() { + // it.effect runs under TestClock, whose current time is the Unix epoch. + const reason = yield* callViewer(() => unavailable(new Date(45_000).toUTCString())).pipe( + expectReason("TransportError") + ) + assert.deepStrictEqual(reason.retryAfter, Duration.seconds(45)) + })) + + it.effect("a network failure is a retryable TransportError without status", () => + Effect.gen(function*() { + const failing = Layer.succeed( + HttpClient.HttpClient, + HttpClient.make((request) => + Effect.fail( + new HttpClientError.HttpClientError({ + reason: new HttpClientError.TransportError({ request, description: "ECONNREFUSED" }) + }) + ) + ) + ) + const reason = yield* GraphQLClient.make(ViewerGroup).pipe( + Effect.flatMap((client) => client.Viewer()), + Effect.provide(GraphQLProtocol.layerHttp({ url: "http://localhost/graphql" }).pipe(Layer.provide(failing))), + expectReason("TransportError") + ) + assert.isUndefined(reason.status) + assert.isTrue(reason.isRetryable) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLProtocol.webSocket.test.ts b/packages/effect/test/graphql/GraphQLProtocol.webSocket.test.ts new file mode 100644 index 000000000..35648d71a --- /dev/null +++ b/packages/effect/test/graphql/GraphQLProtocol.webSocket.test.ts @@ -0,0 +1,250 @@ +import { assert, describe, it } from "@effect/vitest" +import { Deferred, type Duration, Effect, Exit, Fiber, Layer, Queue, Ref, Scope, Stream } from "effect" + +import { GraphQLClient, GraphQLMiddleware, GraphQLProtocol } from "effect/graphql" +import { TransportError } from "effect/graphql/GraphQLClientError" +import * as Headers from "effect/http/Headers" +import * as UrlParams from "effect/http/UrlParams" +import { TestClock } from "effect/testing" +import { expectReason, IssuesGroup, IssueUpdated, Viewer, type WsMessage, wsServer } from "./fixtures.ts" + +const url = "ws://localhost/graphql" + +const event = (title: string) => ({ data: { issueUpdated: { title } } }) + +const subscription = (id: string): GraphQLProtocol.GraphQLRequest => ({ + kind: "subscription", + query: IssueUpdated.document, + operationName: "IssueUpdated", + variables: { id }, + headers: Headers.empty, + urlParams: UrlParams.empty +}) + +interface Options { + readonly connectionParams?: Effect.Effect | undefined, unknown> + readonly connectionAckTimeout?: Duration.Input + readonly keepAlive?: Duration.Input | false + readonly idleTimeout?: Duration.Input +} + +const makeProtocol = (server: Effect.Success, options?: Options) => + GraphQLProtocol.makeWebSocket({ url, ...options }).pipe(Effect.provide(server.layer)) + +/** Starts a subscription and accepts its connection, returning the failure fiber. */ +const acceptedSubscription = (server: Effect.Success, options?: Options) => + Effect.gen(function*() { + const protocol = yield* makeProtocol(server, options) + const fiber = yield* protocol.subscribe(subscription("A")).pipe(Stream.runDrain, Effect.flip, Effect.forkChild) + const { connection } = yield* server.accept + const subscribe = yield* Queue.take(connection.received) + return { fiber, connection, subscribe } + }) + +describe("GraphQLProtocol.makeWebSocket", () => { + it.effect("subscribes only after connection_ack and routes events by operation id", () => + Effect.gen(function*() { + const server = yield* wsServer + const protocol = yield* makeProtocol(server, { connectionParams: Effect.succeed({ token: "t1" }) }) + const a = yield* protocol.subscribe(subscription("A")).pipe(Stream.runCollect, Effect.forkChild) + const b = yield* protocol.subscribe(subscription("B")).pipe(Stream.runCollect, Effect.forkChild) + + const connection = yield* Queue.take(server.connections) + assert.deepStrictEqual(connection.options, "graphql-transport-ws") + assert.deepStrictEqual(yield* Queue.take(connection.received), { + type: "connection_init", + payload: { token: "t1" } + }) + yield* TestClock.adjust("1 second") + assert.strictEqual(yield* Queue.size(connection.received), 0) + + yield* connection.send({ type: "connection_ack" }) + const ids: Record = {} + for (const message of [yield* Queue.take(connection.received), yield* Queue.take(connection.received)]) { + assert.strictEqual(message.type, "subscribe") + ids[(message.payload as { variables: { id: string } }).variables.id] = message.id! + } + + yield* connection.send({ id: ids.B, type: "next", payload: event("b") }) + yield* connection.send({ id: ids.A, type: "next", payload: event("a") }) + yield* connection.send({ id: ids.A, type: "complete" }) + yield* connection.send({ id: ids.B, type: "complete" }) + assert.deepStrictEqual(yield* Fiber.join(a), [{ body: event("a") }]) + assert.deepStrictEqual(yield* Fiber.join(b), [{ body: event("b") }]) + })) + + it.effect("a missing connection_ack is a TransportError", () => + Effect.gen(function*() { + const server = yield* wsServer + const protocol = yield* makeProtocol(server, { connectionAckTimeout: "5 seconds" }) + const fiber = yield* protocol.subscribe(subscription("A")).pipe(Stream.runDrain, Effect.flip, Effect.forkChild) + yield* Queue.take(server.connections) + yield* TestClock.adjust("5 seconds") + assert.instanceOf(yield* Fiber.join(fiber), TransportError) + })) + + it.effect("a close while connectionParams is pending fails the waiting operation", () => + Effect.gen(function*() { + const server = yield* wsServer + const protocol = yield* makeProtocol(server, { connectionParams: Effect.never }) + const fiber = yield* protocol.subscribe(subscription("A")).pipe(Stream.runDrain, Effect.flip, Effect.forkChild) + const connection = yield* Queue.take(server.connections) + yield* connection.close(1006) + yield* TestClock.adjust("1 minute") + assert.isDefined(fiber.pollUnsafe()) + assert.strictEqual((yield* Fiber.join(fiber)).closeCode, 1006) + })) + + // Shape is checked before the id lookup, so an unknown id does not excuse it. + const invalidMessages: ReadonlyArray = [ + { type: "not-a-graphql-message" }, + { type: "next", id: "unknown", payload: [] } + ] + for (const invalid of invalidMessages) { + it.effect(`an invalid message ${JSON.stringify(invalid)} closes the connection with 4400`, () => + Effect.gen(function*() { + const server = yield* wsServer + const { connection, fiber } = yield* acceptedSubscription(server) + yield* connection.send(invalid) + const error = yield* Fiber.join(fiber) + assert.strictEqual(error.closeCode, 4400) + assert.isFalse(error.isRetryable) + assert.strictEqual(yield* Deferred.await(connection.closed), 4400) + })) + } + + it.effect("1002 is a fatal close", () => + Effect.gen(function*() { + const server = yield* wsServer + const { connection, fiber } = yield* acceptedSubscription(server) + yield* connection.close(1002) + const error = yield* Fiber.join(fiber) + assert.strictEqual(error.closeCode, 1002) + assert.isFalse(error.isRetryable) + })) + + it.effect("answers ping with pong, pings every keepAlive and treats a missed frame as a retryable close", () => + Effect.gen(function*() { + const server = yield* wsServer + const { connection, fiber } = yield* acceptedSubscription(server, { keepAlive: "10 seconds" }) + yield* connection.send({ type: "ping" }) + assert.deepStrictEqual(yield* Queue.take(connection.received), { type: "pong" }) + + yield* TestClock.adjust("10 seconds") + assert.deepStrictEqual(yield* Queue.take(connection.received), { type: "ping" }) + yield* TestClock.adjust("10 seconds") + assert.isTrue((yield* Fiber.join(fiber)).isRetryable) + })) + + it.effect("interrupting a subscription sends a client complete", () => + Effect.gen(function*() { + const server = yield* wsServer + const { connection, fiber, subscribe } = yield* acceptedSubscription(server) + yield* Fiber.interrupt(fiber) + assert.deepStrictEqual(yield* Queue.take(connection.received), { id: subscribe.id, type: "complete" }) + })) + + it.effect("a lost connection fails in-flight queries, and the next operation reconnects with fresh connectionParams", () => + Effect.gen(function*() { + const server = yield* wsServer + const tokens = yield* Ref.make(0) + const protocol = yield* makeProtocol(server, { + connectionParams: Effect.map(Ref.updateAndGet(tokens, (n) => n + 1), (n) => ({ token: n })) + }) + const query: GraphQLProtocol.GraphQLRequest = { + kind: "query", + query: Viewer.document, + operationName: "Viewer", + variables: {}, + headers: Headers.empty, + urlParams: UrlParams.empty + } + + const inFlight = yield* protocol.execute(query).pipe(Effect.flip, Effect.forkChild) + const first = yield* server.accept + assert.deepStrictEqual(first.init.payload, { token: 1 }) + yield* Queue.take(first.connection.received) + yield* first.connection.close(1006) + assert.isTrue((yield* Fiber.join(inFlight)).isRetryable) + + const retried = yield* protocol.execute(query).pipe(Effect.forkChild) + const second = yield* server.accept + assert.deepStrictEqual(second.init.payload, { token: 2 }) + const subscribe = yield* Queue.take(second.connection.received) + yield* second.connection.send({ id: subscribe.id, type: "next", payload: { data: { viewer: { login: "tim" } } } }) + yield* second.connection.send({ id: subscribe.id, type: "complete" }) + assert.deepStrictEqual(yield* Fiber.join(retried), { body: { data: { viewer: { login: "tim" } } } }) + })) + + it.effect("an operation started after the scope closed fails with a non-retryable TransportError", () => + Effect.gen(function*() { + const server = yield* wsServer + const scope = yield* Scope.make() + const protocol = yield* makeProtocol(server).pipe(Scope.provide(scope)) + yield* Scope.close(scope, Exit.void) + const fiber = yield* protocol.subscribe(subscription("A")).pipe( + Stream.runDrain, + Effect.flip, + Effect.timeout("5 seconds"), + Effect.forkChild + ) + yield* TestClock.adjust("5 seconds") + const error = yield* Fiber.join(fiber) + assert.instanceOf(error, TransportError) + assert.isFalse(error.isRetryable) + })) + + it.effect("connects on the first operation and closes idleTimeout after the last one", () => + Effect.gen(function*() { + const server = yield* wsServer + const protocol = yield* makeProtocol(server, { idleTimeout: "30 seconds", keepAlive: false }) + yield* TestClock.adjust("1 minute") + assert.strictEqual(yield* Queue.size(server.connections), 0) + + const fiber = yield* protocol.subscribe(subscription("A")).pipe(Stream.runCollect, Effect.forkChild) + const { connection } = yield* server.accept + const subscribe = yield* Queue.take(connection.received) + yield* connection.send({ id: subscribe.id, type: "complete" }) + yield* Fiber.join(fiber) + + yield* TestClock.adjust("29 seconds") + assert.isFalse(yield* Deferred.isDone(connection.closed)) + yield* TestClock.adjust("1 second") + assert.strictEqual(yield* Deferred.await(connection.closed), 1000) + })) +}) + +describe("GraphQLProtocol.layerWebSocket with GraphQLClient", () => { + it.effect("a retryable close resubscribes through the middleware chain; a server error is a ResponseError", () => + Effect.gen(function*() { + class Count extends GraphQLMiddleware.Service()("test/Count") {} + const subscribes = yield* Ref.make(0) + const CountLive = Layer.succeed(Count, { + execute: ({ next, request }) => next(request), + subscribe: ({ next, request }) => Stream.unwrap(Effect.as(Ref.update(subscribes, (n) => n + 1), next(request))) + }) + const server = yield* wsServer + const context = yield* Layer.build( + Layer.merge(GraphQLProtocol.layerWebSocket({ url }).pipe(Layer.provide(server.layer)), CountLive) + ) + const client = yield* GraphQLClient.make(IssuesGroup.middleware(Count)).pipe(Effect.provide(context)) + const fiber = yield* client.IssueUpdated({ id: "I_1" }).pipe( + Stream.runCollect, + expectReason("ResponseError"), + Effect.forkChild + ) + + const first = yield* server.accept + yield* Queue.take(first.connection.received) + yield* first.connection.close(1006) + // The client's default schedule waits 500ms before resubscribing. + yield* TestClock.adjust("500 millis") + + const second = yield* server.accept + const subscribe = yield* Queue.take(second.connection.received) + yield* second.connection.send({ id: subscribe.id, type: "error", payload: [{ message: "denied" }] }) + const reason = yield* Fiber.join(fiber) + assert.deepStrictEqual(reason.errors, [{ message: "denied" }]) + assert.strictEqual(yield* Ref.get(subscribes), 2) + })) +}) diff --git a/packages/effect/test/graphql/GraphQLResolver.test.ts b/packages/effect/test/graphql/GraphQLResolver.test.ts new file mode 100644 index 000000000..41f93109b --- /dev/null +++ b/packages/effect/test/graphql/GraphQLResolver.test.ts @@ -0,0 +1,532 @@ +import { assert, describe, it } from "@effect/vitest" +import { Cause, Context, Effect, Layer, Ref, Schema } from "effect" +import { GraphQL, GraphQLClient, GraphQLGroup, GraphQLMiddleware, GraphQLResolver } from "effect/graphql" +import { GraphQLClientError, type ResponseError } from "effect/graphql/GraphQLClientError" +import * as GraphQLProtocol from "effect/graphql/GraphQLProtocol" +import * as Headers from "effect/http/Headers" +import { graphqlResponse, httpClientLayer, protocolLayer } from "./fixtures.ts" + +const Repository = Schema.Struct({ __typename: Schema.Literal("Repository"), nameWithOwner: Schema.String }) +const User = Schema.Struct({ __typename: Schema.Literal("User"), login: Schema.String }) +const Node = Schema.Union([Repository, User]) + +const RepoNodes = GraphQL.query("RepoNodes", { + document: + "query RepoNodes($ids:[ID!]!){nodes(ids:$ids){__typename ...on Repository{nameWithOwner} ...on User{login}}}", + variables: { ids: Schema.Array(Schema.String) }, + result: Schema.Struct({ nodes: Schema.Array(Schema.NullOr(Node)) }) +}) + +const SearchRepos = GraphQL.query("SearchRepos", { + document: + "query SearchRepos($query:String!){search(query:$query,type:REPOSITORY,first:100){pageInfo{hasNextPage endCursor} nodes{__typename ...on Repository{nameWithOwner} ...on User{login}}}}", + variables: { query: Schema.String }, + result: Schema.Struct({ + search: Schema.Struct({ + pageInfo: Schema.Struct({ hasNextPage: Schema.Boolean, endCursor: Schema.NullOr(Schema.String) }), + nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Node))) + }) + }) +}) + +// Generated operations carry a document template, which `aliased` repeats +// once per slice. +const PullRequest = GraphQL.query("PullRequest", { + document: { + variables: [{ name: "name", body: "String!" }, { name: "number", body: "Int!" }], + selections: [{ + name: "repository", + body: ["(owner:\"Effect-TS\",name:$", "name", "){pullRequest(number:$", "number", "){title}}"] + }], + fragments: [] + }, + variables: { name: Schema.String, number: Schema.Int }, + result: Schema.Struct({ + repository: Schema.NullOr(Schema.Struct({ pullRequest: Schema.NullOr(Schema.Struct({ title: Schema.String })) })) + }) +}) + +// `$after` is optional, so `{ name }` and `{ name, after: undefined }` encode alike. +const RepoIssues = GraphQL.query("RepoIssues", { + document: { + variables: [{ name: "name", body: "String!" }, { name: "after", body: "String" }], + selections: [{ + name: "repository", + body: ["(owner:\"Effect-TS\",name:$", "name", "){issues(after:$", "after", "){totalCount}}"] + }], + fragments: [] + }, + variables: { name: Schema.String, after: Schema.optional(Schema.NullOr(Schema.String)) }, + result: Schema.Struct({ + repository: Schema.NullOr(Schema.Struct({ issues: Schema.Struct({ totalCount: Schema.Int }) })) + }) +}) + +class CurrentUser extends Context.Service()("test/CurrentUser") {} +class Auth extends GraphQLMiddleware.Service()("test/Auth") {} + +const AuthLive = Layer.succeed( + Auth, + GraphQLMiddleware.mapRequest(Effect.fnUntraced(function*(request) { + const user = yield* CurrentUser + return { ...request, headers: Headers.set(request.headers, "authorization", `Bearer ${user.tenant}`) } + })) +) + +const repo = (nameWithOwner: string) => ({ __typename: "Repository" as const, nameWithOwner }) + +/** Answers each request with `respond(variables)` and records the requests. */ +const makeClient = (respond: (variables: any) => unknown, headers?: Headers.Headers) => + Effect.gen(function*() { + const requests = yield* Ref.make>([]) + const layer = protocolLayer({ + execute: (request) => + Effect.as(Ref.update(requests, (all) => [...all, request]), { + body: respond(request.variables), + status: 200, + headers + }) + }) + const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes, SearchRepos, PullRequest, RepoIssues)).pipe( + Effect.provide(layer) + ) + return { client, requests } + }) + +/** Requests every key concurrently and returns each entry's success or failure. */ +const resolveAll = (keys: ReadonlyArray, get: (key: K) => Effect.Effect) => + Effect.forEach( + keys, + (key) => Effect.map(Effect.result(get(key)), (r) => r._tag === "Success" ? r.success : r.failure), + { concurrency: "unbounded" } + ) + +const byId = (client: Effect.Success>["client"]) => + GraphQLResolver.request(GraphQLResolver.ordered(client.RepoNodes, { + variables: (ids: ReadonlyArray) => ({ ids }), + path: ["nodes"], + typename: "Repository" + })) + +const byName = (client: Effect.Success>["client"]) => + GraphQLResolver.request(GraphQLResolver.findById(client.SearchRepos, { + key: (repo) => repo.nameWithOwner, + variables: (names) => ({ query: names.map((name) => `repo:${name}`).join(" ") }), + path: ["search", "nodes"], + typename: "Repository" + })) + +const searchNames = (variables: { query: string }) => variables.query.split(" ").map((term) => term.slice(5)) + +const searchResult = (nodes: ReadonlyArray, hasNextPage = false) => ({ + data: { search: { pageInfo: { hasNextPage, endCursor: null }, nodes } } +}) + +/** Splits batched variables (`_0_name`, `_1_name`, …) into each slice's variables, keyed by prefix. */ +const slices = (variables: unknown): Record> => { + const out: Record> = {} + for (const [key, value] of Object.entries(variables as Record)) { + const [, prefix, name] = /^(_\d+_)(.+)$/.exec(key)! + out[prefix] ??= {} + out[prefix][name] = value + } + return out +} + +/** Answers every slice with `respond(variables)` under its aliased `repository` field. */ +const answerSlices = (variables: unknown, respond: (variables: any) => unknown) => + Object.fromEntries( + Object.entries(slices(variables)).map(([prefix, slice]) => [`${prefix}repository`, respond(slice)]) + ) + +const pullRequest = (variables: { readonly name: string; readonly number: number }) => ({ + pullRequest: { title: `${variables.name}#${variables.number}` } +}) + +const getPullRequest = (client: Effect.Success>["client"]) => + GraphQLResolver.request(GraphQLResolver.aliased(client.PullRequest)) + +const lookups = [{ name: "effect", number: 1 }, { name: "effect", number: 2 }] + +describe("GraphQLResolver", () => { + describe("ordered", () => { + it.effect("batches concurrent keys into one call; a null item or a typename mismatch is a miss", () => + Effect.gen(function*() { + const store: Record = { + R_1: repo("Effect-TS/effect"), + U_1: { __typename: "User", login: "tim" } + } + const { client, requests } = yield* makeClient((variables) => ({ + data: { nodes: variables.ids.map((id: string) => store[id] ?? null) } + })) + + const results = yield* resolveAll(["R_1", "R_2", "U_1", "R_1"], byId(client)) + + const sent = yield* Ref.get(requests) + assert.strictEqual(sent.length, 1) + assert.deepStrictEqual([...(sent[0].variables as { ids: Array }).ids].sort(), ["R_1", "R_2", "U_1"]) + assert.deepStrictEqual(results[0], repo("Effect-TS/effect")) + assert.deepStrictEqual(results[3], repo("Effect-TS/effect")) + assert.instanceOf(results[1], Cause.NoSuchElementError) + assert.instanceOf(results[2], Cause.NoSuchElementError) + })) + + it.effect("an error at [...path, i] fails only entry i, keeping the response status and headers", () => + Effect.gen(function*() { + const { client } = yield* makeClient((variables) => { + const failed = variables.ids.indexOf("R_2") + return { + data: { nodes: variables.ids.map((id: string) => id === "R_2" ? null : repo(id)) }, + errors: [{ message: "Could not resolve", path: ["nodes", failed] }] + } + }, Headers.fromInput({ "retry-after": "60" })) + + const [ok, failed] = yield* resolveAll(["R_1", "R_2"], byId(client)) + + assert.deepStrictEqual(ok, repo("R_1")) + assert.instanceOf(failed, GraphQLClientError) + assert.strictEqual(failed.reason._tag, "ResponseError") + const reason = failed.reason as ResponseError + assert.deepStrictEqual(reason.errors.map((error) => error.message), ["Could not resolve"]) + assert.strictEqual(reason.status, 200) + assert.strictEqual(reason.headers?.["retry-after"], "60") + assert.isTrue(failed.isRetryable) + })) + + it.effect("an error at any other path fails the batch", () => + Effect.gen(function*() { + const { client } = yield* makeClient((variables) => ({ + data: { nodes: variables.ids.map(repo) }, + errors: [{ message: "Rate limited", path: ["rateLimit"] }] + })) + + const results = yield* resolveAll(["R_1", "R_2"], byId(client)) + + for (const result of results) { + assert.instanceOf(result, GraphQLClientError) + assert.strictEqual(result.reason._tag, "ResponseError") + } + })) + }) + + describe("ordered, when an error nulls the whole list", () => { + const NullableNodes = GraphQL.query("NullableNodes", { + document: "query NullableNodes($ids:[ID!]!){nodes(ids:$ids){__typename ...on Repository{nameWithOwner}}}", + variables: { ids: Schema.Array(Schema.String) }, + result: Schema.Struct({ nodes: Schema.NullOr(Schema.Array(Repository)) }) + }) + + it.effect("every entry fails with the ResponseError instead of NoSuchElementError", () => + Effect.gen(function*() { + const client = yield* GraphQLClient.make(GraphQLGroup.make(NullableNodes)).pipe( + Effect.provide(protocolLayer({ + execute: () => + Effect.succeed({ + body: { data: { nodes: null }, errors: [{ message: "boom", path: ["nodes", 1, "nameWithOwner"] }] }, + status: 200 + }) + })) + ) + const get = GraphQLResolver.request(GraphQLResolver.ordered(client.NullableNodes, { + variables: (ids: ReadonlyArray) => ({ ids }), + path: ["nodes"] + })) + for (const failed of yield* resolveAll(["R_1", "R_2"], get)) { + assert.instanceOf(failed, GraphQLClientError) + assert.strictEqual(failed.reason._tag, "ResponseError") + } + })) + }) + + describe("findById", () => { + it.effect("matches out-of-order results on a nested path by key; a missing key is a miss", () => + Effect.gen(function*() { + const { client } = yield* makeClient((variables) => + searchResult(searchNames(variables).filter((name) => name !== "a/missing").map(repo).reverse()) + ) + + const results = yield* resolveAll(["Effect-TS/effect", "a/missing", "Effect-TS/website"], byName(client)) + + assert.deepStrictEqual(results[0], repo("Effect-TS/effect")) + assert.instanceOf(results[1], Cause.NoSuchElementError) + assert.deepStrictEqual(results[2], repo("Effect-TS/website")) + })) + + it.effect("hasNextPage: true on the list's parent fails the batch", () => + Effect.gen(function*() { + const { client } = yield* makeClient((variables) => searchResult(searchNames(variables).map(repo), true)) + + const results = yield* resolveAll(["Effect-TS/effect", "Effect-TS/website"], byName(client)) + + for (const result of results) { + assert.instanceOf(result, GraphQLClientError) + assert.strictEqual(result.reason._tag, "DecodeError") + } + })) + }) + + describe("batchKey", () => { + it.effect("splits a batch by key and calls each group with its own services", () => + Effect.gen(function*() { + const requests = yield* Ref.make>([]) + const layer = protocolLayer({ + execute: (request) => + Effect.as(Ref.update(requests, (all) => [...all, request]), { + body: { data: { nodes: (request.variables as { ids: Array }).ids.map(repo) } }, + status: 200 + }) + }) + const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes).middleware(Auth)).pipe( + Effect.provide([layer, AuthLive]) + ) + const get = GraphQLResolver.request(GraphQLResolver.ordered(client.RepoNodes, { + variables: (ids: ReadonlyArray) => ({ ids }), + path: ["nodes"], + batchKey: (context) => Context.get(context, CurrentUser).tenant + })) + const getAs = (tenant: string, id: string) => Effect.provideService(get(id), CurrentUser, { tenant }) + + const results = yield* Effect.all([getAs("a", "R_1"), getAs("b", "R_2"), getAs("a", "R_3")], { + concurrency: "unbounded" + }) + + assert.deepStrictEqual(results, [repo("R_1"), repo("R_2"), repo("R_3")]) + const sent = yield* Ref.get(requests) + assert.deepStrictEqual( + sent.map((request) => + `${request.headers["authorization"]}: ${[...(request.variables as { ids: Array }).ids].sort()}` + ).sort(), + ["Bearer a: R_1,R_3", "Bearer b: R_2"] + ) + })) + }) + + describe("url", () => { + const GitHubHost = Context.Reference("test/GitHubHost", { defaultValue: () => "api.github.com" }) + + /** Looks up R_1 and R_3 on the default host and R_2 on another, returning what each request sent. */ + const lookUpOnTwoHosts = (url: Parameters[0]["url"]) => + Effect.gen(function*() { + const sent: Array = [] + const layer = GraphQLProtocol.layerHttp({ url }).pipe( + Layer.provide(httpClientLayer((_, bodyText, url) => { + const { ids } = JSON.parse(bodyText!).variables as { ids: Array } + sent.push(`${url.host}: ${[...ids].sort()}`) + return graphqlResponse({ data: { nodes: ids.map(repo) } }) + })) + ) + const client = yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes)).pipe(Effect.provide(layer)) + const get = GraphQLResolver.request(GraphQLResolver.ordered(client.RepoNodes, { + variables: (ids: ReadonlyArray) => ({ ids }), + path: ["nodes"] + })) + + const results = yield* Effect.all([ + get("R_1"), + Effect.provideService(get("R_2"), GitHubHost, "api.acme.ghe.com"), + get("R_3") + ], { concurrency: "unbounded" }) + + assert.deepStrictEqual(results, [repo("R_1"), repo("R_2"), repo("R_3")]) + return sent.sort() + }) + + const urls = [ + [ + "function", + ({ context }: { readonly context: Context.Context }) => + `http://${Context.get(context, GitHubHost)}/graphql` + ], + [ + "Effect resolved asynchronously", + Effect.gen(function*() { + yield* Effect.promise(() => Promise.resolve()) + return `http://${yield* GitHubHost}/graphql` + }) + ] + ] as const + + for (const [form, url] of urls) { + it.effect(`${form}: lookups whose URLs differ go in separate batches; lookups with the same URL share one`, () => + Effect.gen(function*() { + const sent = yield* lookUpOnTwoHosts(url) + assert.deepStrictEqual(sent, ["api.acme.ghe.com: R_2", "api.github.com: R_1,R_3"]) + })) + } + + it.effect("a batch is sent to the URL its lookups resolved to, without evaluating the URL again", () => + Effect.gen(function*() { + let evaluations = 0 + const sent = yield* lookUpOnTwoHosts(Effect.gen(function*() { + evaluations++ + return `http://${yield* GitHubHost}/graphql` + })) + assert.deepStrictEqual(sent, ["api.acme.ghe.com: R_2", "api.github.com: R_1,R_3"]) + // Once per lookup, and not again when each batch is sent. + assert.strictEqual(evaluations, 3) + })) + + it.effect("a direct call made while a batch is sent resolves its own URL", () => + Effect.gen(function*() { + class Nested extends GraphQLMiddleware.Service()("test/Nested") {} + const sent: Array = [] + const layer = GraphQLProtocol.layerHttp({ + url: Effect.map(Effect.service(GitHubHost), (host) => `http://${host}/graphql`) + }).pipe( + Layer.provide(httpClientLayer((_, bodyText, url) => { + const { ids } = JSON.parse(bodyText!).variables as { ids: Array } + sent.push(`${url.host}: ${ids}`) + return graphqlResponse({ data: { nodes: ids.map(repo) } }) + })) + ) + // Both clients share one transport. The batched client's middleware + // calls the same operation directly on another host. + const batched = yield* Effect.gen(function*() { + const direct = yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes)) + return yield* GraphQLClient.make(GraphQLGroup.make(RepoNodes).middleware(Nested)).pipe( + Effect.provideService(Nested, { + execute: ({ next, request }) => + Effect.andThen( + Effect.orDie( + direct.RepoNodes({ ids: ["R_9"] }, { context: Context.make(GitHubHost, "api.acme.ghe.com") }) + ), + next(request) + ), + subscribe: ({ next, request }) => next(request) + }) + ) + }).pipe(Effect.provide(layer)) + const get = GraphQLResolver.request(GraphQLResolver.ordered(batched.RepoNodes, { + variables: (ids: ReadonlyArray) => ({ ids }), + path: ["nodes"] + })) + + assert.deepStrictEqual(yield* get("R_1"), repo("R_1")) + assert.deepStrictEqual(sent, ["api.acme.ghe.com: R_9", "api.github.com: R_1"]) + })) + }) + + describe("aliased", () => { + it.effect("concurrent lookups make one POST whose document repeats the operation under aliases", () => + Effect.gen(function*() { + const sent: Array<{ readonly method: string; readonly body: any }> = [] + // Queries go over GET, but a batch must not. + const layer = GraphQLProtocol.layerHttp({ url: "http://localhost/graphql", queryMethod: "GET" }).pipe( + Layer.provide(httpClientLayer((request, bodyText) => { + const body = bodyText === undefined ? undefined : JSON.parse(bodyText) + sent.push({ method: request.method, body }) + return graphqlResponse({ data: answerSlices(body?.variables ?? {}, pullRequest) }) + })) + ) + const client = yield* GraphQLClient.make(GraphQLGroup.make(PullRequest)).pipe(Effect.provide(layer)) + const keys = [1, 2, 3].map((number) => ({ name: "effect", number })) + + const results = yield* Effect.forEach( + keys, + GraphQLResolver.request(GraphQLResolver.aliased(client.PullRequest)), + { concurrency: "unbounded" } + ) + + assert.deepStrictEqual(results, keys.map((key) => ({ repository: pullRequest(key) }))) + assert.strictEqual(sent.length, 1) + assert.strictEqual(sent[0].method, "POST") + assert.strictEqual( + sent[0].body.query, + "query PullRequest($_0_name:String!$_0_number:Int!$_1_name:String!$_1_number:Int!$_2_name:String!$_2_number:Int!){_0_repository:repository(owner:\"Effect-TS\",name:$_0_name){pullRequest(number:$_0_number){title}}_1_repository:repository(owner:\"Effect-TS\",name:$_1_name){pullRequest(number:$_1_number){title}}_2_repository:repository(owner:\"Effect-TS\",name:$_2_name){pullRequest(number:$_2_number){title}}}" + ) + assert.deepStrictEqual(Object.values(slices(sent[0].body.variables)).map((slice) => slice.number).sort(), [ + 1, + 2, + 3 + ]) + })) + + it.effect("an error at path[0] fails only its slice, with the path un-prefixed", () => + Effect.gen(function*() { + const { client } = yield* makeClient((variables) => { + const prefix = Object.entries(slices(variables)).find(([, slice]) => slice.number === 2)![0] + return { + data: answerSlices(variables, (slice) => slice.number === 2 ? { pullRequest: null } : pullRequest(slice)), + errors: [{ message: "Could not resolve", path: [`${prefix}repository`, "pullRequest"] }] + } + }) + + const [ok, failed] = yield* resolveAll(lookups, getPullRequest(client)) + + assert.deepStrictEqual(ok, { repository: pullRequest(lookups[0]) }) + assert.instanceOf(failed, GraphQLClientError) + assert.strictEqual(failed.reason._tag, "ResponseError") + const reason = failed.reason as ResponseError + assert.deepStrictEqual(reason.errors.map((error) => error.path), [["repository", "pullRequest"]]) + })) + + it.effect("rejects a template it can't slice when the resolver is built, not on the first lookup", () => + Effect.gen(function*() { + const RootSpread = GraphQL.query("RootSpread", { + document: { variables: [], selections: [{ body: ["...on Query{__typename}"] }], fragments: [] }, + result: Schema.Struct({ __typename: Schema.String }) + }) + const client = yield* GraphQLClient.make(GraphQLGroup.make(RootSpread)).pipe( + Effect.provide(protocolLayer({ execute: () => Effect.die("not sent") })) + ) + assert.throws( + () => GraphQLResolver.aliased(client.RootSpread), + /fragment spread or inline fragment at the root/ + ) + })) + + it.effect("an error without a path fails the batch", () => + Effect.gen(function*() { + const { client } = yield* makeClient((variables) => ({ + data: answerSlices(variables, pullRequest), + errors: [{ message: "Rate limited" }] + })) + + const results = yield* resolveAll(lookups, getPullRequest(client)) + + for (const result of results) { + assert.instanceOf(result, GraphQLClientError) + assert.strictEqual(result.reason._tag, "ResponseError") + } + })) + + it.effect("lookups whose encoded variables are equal share one slice", () => + Effect.gen(function*() { + const issues = { totalCount: 1 } + const { client, requests } = yield* makeClient((variables) => ({ + data: answerSlices(variables, () => ({ issues })) + })) + + // Not `Equal`, but both encode to `{"name":"effect"}`. + const results = yield* resolveAll( + [{ name: "effect" }, { name: "effect", after: undefined }], + GraphQLResolver.request(GraphQLResolver.aliased(client.RepoIssues)) + ) + + assert.deepStrictEqual(results, [{ repository: { issues } }, { repository: { issues } }]) + const sent = yield* Ref.get(requests) + assert.strictEqual(sent.length, 1) + assert.deepStrictEqual(Object.keys(slices(sent[0].variables)), ["_0_"]) + })) + + it.effect("over a makeTest client, the handler is called once per slice", () => + Effect.gen(function*() { + const received: Array<{ readonly name: string; readonly number: number }> = [] + const client = GraphQLClient.makeTest(GraphQLGroup.make(PullRequest))({ + PullRequest: (variables) => { + received.push(variables) + return Effect.succeed({ repository: pullRequest(variables) }) + } + }) + + const results = yield* Effect.forEach( + lookups, + GraphQLResolver.request(GraphQLResolver.aliased(client.PullRequest)), + { concurrency: "unbounded" } + ) + + assert.deepStrictEqual(results, lookups.map((key) => ({ repository: pullRequest(key) }))) + assert.deepStrictEqual(received.map((variables) => variables.number).sort(), [1, 2]) + })) + }) +}) diff --git a/packages/effect/test/graphql/fixtures.ts b/packages/effect/test/graphql/fixtures.ts new file mode 100644 index 000000000..5abd8f1c3 --- /dev/null +++ b/packages/effect/test/graphql/fixtures.ts @@ -0,0 +1,233 @@ +/** + * Shared fixtures for the `effect/graphql` runtime tests. + * + * The operations below stand in for what `@effect/graphql-generator` emits for a + * GitHub-style schema, including a custom scalar codec (`DateTime`). + */ +import { assert } from "@effect/vitest" +import { Deferred, Effect, Layer, Queue, Ref, Schema, Stream } from "effect" +import { GraphQL, GraphQLGroup } from "effect/graphql" +import { GraphQLClientError } from "effect/graphql/GraphQLClientError" +import * as GraphQLProtocol from "effect/graphql/GraphQLProtocol" +import * as HttpBody from "effect/http/HttpBody" +import * as HttpClient from "effect/http/HttpClient" +import type * as HttpClientRequest from "effect/http/HttpClientRequest" +import * as HttpClientResponse from "effect/http/HttpClientResponse" +import * as Socket from "effect/socket/Socket" + +// ----------------------------------------------------------------------------- +// Stand-in for generated output +// ----------------------------------------------------------------------------- + +const Issue = Schema.Struct({ + number: Schema.Int, + title: Schema.String, + createdAt: Schema.DateTimeUtcFromString +}) + +export const RepoIssues = GraphQL.query("RepoIssues", { + document: + "query RepoIssues($owner:String!,$name:String!,$after:String,$since:DateTime){repository(owner:$owner,name:$name){issues(first:2,after:$after,filterBy:{since:$since}){pageInfo{hasNextPage endCursor}nodes{number title createdAt}}}}", + variables: { + owner: Schema.String, + name: Schema.String, + after: Schema.optional(Schema.NullOr(Schema.String)), + since: Schema.optional(Schema.NullOr(Schema.DateTimeUtcFromString)) + }, + result: Schema.Struct({ + repository: Schema.NullOr(Schema.Struct({ + issues: Schema.NullOr(Schema.Struct({ + pageInfo: Schema.Struct({ hasNextPage: Schema.Boolean, endCursor: Schema.NullOr(Schema.String) }), + nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Issue))) + })) + })) + }) +}) + +export const Viewer = GraphQL.query("Viewer", { + document: "query Viewer{viewer{login}}", + result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) }) +}) + +export const IssueUpdated = GraphQL.subscription("IssueUpdated", { + document: "subscription IssueUpdated($id:ID!){issueUpdated(id:$id){title}}", + variables: { id: Schema.String }, + result: Schema.Struct({ issueUpdated: Schema.Struct({ title: Schema.String }) }) +}) + +export const IssuesGroup = GraphQLGroup.make(RepoIssues, IssueUpdated) +export const ViewerGroup = GraphQLGroup.make(Viewer) + +// ----------------------------------------------------------------------------- +// Transport doubles +// ----------------------------------------------------------------------------- + +/** + * A `GraphQLProtocol` layer with scripted behaviour. Tests that exercise the + * client (middleware, partial results, paging, subscriptions) use this instead + * of HTTP so they stay about the client. + */ +export const protocolLayer = (service: { + readonly execute?: GraphQLProtocol.GraphQLProtocol["Service"]["execute"] + readonly subscribe?: GraphQLProtocol.GraphQLProtocol["Service"]["subscribe"] +}) => + Layer.succeed(GraphQLProtocol.GraphQLProtocol, { + execute: service.execute ?? (() => Effect.die("execute not scripted")), + subscribe: service.subscribe ?? (() => Stream.die("subscribe not scripted")) + }) + +/** + * A protocol whose `execute` always answers with the same raw body, and HTTP + * `status` when given, and records every request it received. + */ +export const executeLayer = (body: unknown, status?: number) => + Effect.gen(function*() { + const requests = yield* Ref.make>([]) + const layer = protocolLayer({ + execute: (request) => + Effect.as( + Ref.update(requests, (all) => [...all, request]), + status === undefined ? { body } : { body, status } + ) + }) + return { layer, requests } + }) + +/** + * An `HttpClient` that answers every request from `handler`, with the request + * body text and the final URL exposed so tests can assert on what went over + * the wire. + */ +export const httpClientLayer = ( + handler: (request: HttpClientRequest.HttpClientRequest, bodyText: string | undefined, url: URL) => Response +) => + Layer.succeed( + HttpClient.HttpClient, + HttpClient.make((request, url) => + Effect.sync(() => + HttpClientResponse.fromWeb( + request, + handler(request, request.body instanceof HttpBody.Uint8Array ? request.body.text : undefined, url) + ) + ) + ) + ) + +export const graphqlResponse = (body: unknown, init?: { status?: number; contentType?: string }) => + new Response(JSON.stringify(body), { + status: init?.status ?? 200, + headers: { "content-type": init?.contentType ?? "application/graphql-response+json" } + }) + +export const layerHttp = ( + handler: (request: HttpClientRequest.HttpClientRequest, bodyText: string | undefined, url: URL) => Response +) => GraphQLProtocol.layerHttp({ url: "http://localhost/graphql" }).pipe(Layer.provide(httpClientLayer(handler))) + +/** + * A graphql-ws message as it goes over the wire. + */ +export interface WsMessage { + readonly type: string + readonly id?: string | undefined + readonly payload?: unknown +} + +/** + * The server end of one in-memory WebSocket opened by the transport. + */ +interface WsConnection { + readonly url: string + readonly options: Socket.WebSocketConstructorOptions | undefined + /** Every frame the client sent, parsed. */ + readonly received: Queue.Queue + /** The code the client closed with, once it does. */ + readonly closed: Deferred.Deferred + readonly send: (message: WsMessage) => Effect.Effect + readonly close: (code: number, reason?: string) => Effect.Effect +} + +/** + * A `Socket.WebSocketConstructor` whose sockets are in-memory queue pairs. + * Each socket the transport opens shows up on `connections`, already open, + * for the test to script the server side. + */ +export const wsServer = Effect.gen(function*() { + const connections = yield* Queue.unbounded() + const layer = Layer.succeed( + Socket.WebSocketConstructor, + (url, options) => { + const inbox = Effect.runSync(Queue.unbounded()) + const closedWith = Effect.runSync(Deferred.make()) + let listeners: Array<{ + readonly type: string + readonly listener: (event: Socket.WebSocketEvent) => void + readonly once: boolean + }> = [] + const dispatch = (type: string, event: Socket.WebSocketEvent) => { + for (const entry of listeners.filter((entry) => entry.type === type)) { + if (entry.once) listeners = listeners.filter((other) => other !== entry) + entry.listener(event) + } + } + const ws = { + readyState: 1, + addEventListener(type, listener, options) { + listeners.push({ type, listener, once: options?.once === true }) + }, + removeEventListener(type, listener) { + listeners = listeners.filter((entry) => entry.type !== type || entry.listener !== listener) + }, + send(data) { + Queue.offerUnsafe(inbox, JSON.parse(typeof data === "string" ? data : new TextDecoder().decode(data))) + }, + close(code = 1005, reason) { + if (ws.readyState >= 2) return + ws.readyState = 3 + Deferred.doneUnsafe(closedWith, Effect.succeed(code)) + dispatch("close", { code, reason: reason ?? "" }) + } + } satisfies Socket.WebSocketLike & { readyState: number } + Queue.offerUnsafe(connections, { + url, + options, + received: inbox, + closed: closedWith, + send: (message) => Effect.sync(() => dispatch("message", { data: JSON.stringify(message) })), + close: (code, reason) => + Effect.sync(() => { + if (ws.readyState >= 2) return + ws.readyState = 3 + dispatch("close", { code, reason: reason ?? "" }) + }) + }) + return ws + } + ) + /** + * Takes the next connection, reads its `connection_init` and acknowledges it. + */ + const accept = Effect.gen(function*() { + const connection = yield* Queue.take(connections) + const init = yield* Queue.take(connection.received) + assert.strictEqual(init.type, "connection_init") + yield* connection.send({ type: "connection_ack" }) + return { connection, init } + }) + return { layer, connections, accept } +}) + +// ----------------------------------------------------------------------------- +// Assertions +// ----------------------------------------------------------------------------- + +/** + * Runs `effect`, asserts it fails with a `GraphQLClientError` whose reason has + * `tag`, and returns that reason narrowed. + */ +export const expectReason = + (tag: Tag) => (effect: Effect.Effect) => + Effect.map(Effect.flip(effect), (error) => { + assert.instanceOf(error, GraphQLClientError) + assert.strictEqual(error.reason._tag, tag) + return error.reason as Extract + }) diff --git a/repos/effect/packages/effect/test/http/Cookies.test.ts b/packages/effect/test/http/Cookies.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Cookies.test.ts rename to packages/effect/test/http/Cookies.test.ts diff --git a/repos/effect/packages/effect/test/http/FetchHttpClient.test.ts b/packages/effect/test/http/FetchHttpClient.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FetchHttpClient.test.ts rename to packages/effect/test/http/FetchHttpClient.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/case-insensitive.test.ts b/packages/effect/test/http/FindMyWay/case-insensitive.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/case-insensitive.test.ts rename to packages/effect/test/http/FindMyWay/case-insensitive.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/matching-order.test.ts b/packages/effect/test/http/FindMyWay/matching-order.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/matching-order.test.ts rename to packages/effect/test/http/FindMyWay/matching-order.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/methods.test.ts b/packages/effect/test/http/FindMyWay/methods.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/methods.test.ts rename to packages/effect/test/http/FindMyWay/methods.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/optional-params.test.ts b/packages/effect/test/http/FindMyWay/optional-params.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/optional-params.test.ts rename to packages/effect/test/http/FindMyWay/optional-params.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/params-collisions.test.ts b/packages/effect/test/http/FindMyWay/params-collisions.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/params-collisions.test.ts rename to packages/effect/test/http/FindMyWay/params-collisions.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/path-params-match.test.ts b/packages/effect/test/http/FindMyWay/path-params-match.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/path-params-match.test.ts rename to packages/effect/test/http/FindMyWay/path-params-match.test.ts diff --git a/repos/effect/packages/effect/test/http/FindMyWay/querystring.test.ts b/packages/effect/test/http/FindMyWay/querystring.test.ts similarity index 73% rename from repos/effect/packages/effect/test/http/FindMyWay/querystring.test.ts rename to packages/effect/test/http/FindMyWay/querystring.test.ts index 74180d0cb..5fa32e67d 100644 --- a/repos/effect/packages/effect/test/http/FindMyWay/querystring.test.ts +++ b/packages/effect/test/http/FindMyWay/querystring.test.ts @@ -36,3 +36,11 @@ it("handles %", () => { "%": "" }) }) + +it("normalizes duplicate path slashes without changing query slashes", () => { + const router = Router.make() + router.on("GET", "/user/:id", true) + const result = router.find("GET", "//user//1?u=https://x.com/y") + assert.deepStrictEqual(result?.params, { id: "1" }) + assert.deepStrictEqual(result?.searchParams, { u: "https://x.com/y" }) +}) diff --git a/repos/effect/packages/effect/test/http/FindMyWay/regex.test.ts b/packages/effect/test/http/FindMyWay/regex.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/FindMyWay/regex.test.ts rename to packages/effect/test/http/FindMyWay/regex.test.ts diff --git a/repos/effect/packages/effect/test/http/Headers.test.ts b/packages/effect/test/http/Headers.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/Headers.test.ts rename to packages/effect/test/http/Headers.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpBody.test.ts b/packages/effect/test/http/HttpBody.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpBody.test.ts rename to packages/effect/test/http/HttpBody.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpClient.test.ts b/packages/effect/test/http/HttpClient.test.ts similarity index 73% rename from repos/effect/packages/effect/test/http/HttpClient.test.ts rename to packages/effect/test/http/HttpClient.test.ts index 74662c69b..3058dd8c4 100644 --- a/repos/effect/packages/effect/test/http/HttpClient.test.ts +++ b/packages/effect/test/http/HttpClient.test.ts @@ -1,10 +1,11 @@ import { assert, describe, it, vi } from "@effect/vitest" import { strictEqual } from "@effect/vitest/utils" -import { Clock, Duration, Effect, Fiber, Layer, Ref, Schema, Stream } from "effect" -import { Cookies, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/http" +import { Clock, Deferred, Duration, Effect, Fiber, Layer, Redacted, Ref, Schedule, Schema, Stream } from "effect" +import { Cookies, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse, UrlParams } from "effect/http" import { RateLimiter } from "effect/persistence" import { TestClock } from "effect/testing" import * as Tracer from "effect/Tracer" +import { inspect } from "node:util" import { collectGarbage } from "../utils/gc.ts" const makeStatusClient = Effect.fnUntraced(function*(status: number) { @@ -18,6 +19,19 @@ const makeStatusClient = Effect.fnUntraced(function*(status: number) { return { attempts, client } as const }) +const makeSequenceClient = Effect.fnUntraced(function*( + responses: ReadonlyArray<{ readonly status: number; readonly headers?: Record }> +) { + const attempts = yield* Ref.make(0) + const client = HttpClient.make((request) => + Effect.map( + Ref.getAndUpdate(attempts, (n) => n + 1), + (n) => HttpClientResponse.fromWeb(request, new Response(null, responses[Math.min(n, responses.length - 1)])) + ) + ) + return { attempts, client } as const +}) + const makeRedirectClient = Effect.fnUntraced(function*(status: number, location: string | ReadonlyArray) { const locations = typeof location === "string" ? [location] : location const requests = yield* Ref.make>([]) @@ -272,6 +286,166 @@ Missing key assert.strictEqual(clientSpan.attributes.get("http.request.header.x-phase-filter"), undefined) assert.strictEqual(clientSpan.attributes.get("http.response.header.x-phase-filter"), "response") })) + + it.effect("redacts sensitive query params in url attributes", () => + Effect.gen(function*() { + let clientSpan: Tracer.NativeSpan | undefined + let sentUrl: string | undefined + const tracer = Tracer.make({ + span(options) { + clientSpan = new Tracer.NativeSpan(options) + return clientSpan + } + }) + const client = HttpClient.make((request, url) => { + sentUrl = url.toString() + return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null))) + }) + + yield* client.get("http://test/path?Signature=sig-secret&page=1", { + urlParams: { "X-Goog-Signature": "goog-secret" } + }).pipe(Effect.provideService(Tracer.Tracer, tracer)) + + assert(clientSpan !== undefined) + assert.strictEqual( + clientSpan.attributes.get("url.full"), + "http://test/path?Signature=REDACTED&page=1&X-Goog-Signature=REDACTED" + ) + assert.strictEqual( + clientSpan.attributes.get("url.query"), + "Signature=REDACTED&page=1&X-Goog-Signature=REDACTED" + ) + assert.strictEqual(sentUrl, "http://test/path?Signature=sig-secret&page=1&X-Goog-Signature=goog-secret") + })) + + it.effect("redacts userinfo in url.full", () => + Effect.gen(function*() { + let clientSpan: Tracer.NativeSpan | undefined + const tracer = Tracer.make({ + span(options) { + clientSpan = new Tracer.NativeSpan(options) + return clientSpan + } + }) + const client = HttpClient.make((request) => + Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null))) + ) + + yield* client.get("http://user:password@test/path").pipe(Effect.provideService(Tracer.Tracer, tracer)) + + assert(clientSpan !== undefined) + assert.strictEqual(clientSpan.attributes.get("url.full"), "http://REDACTED:REDACTED@test/path") + })) + + it.effect("redacts Redacted query params in url attributes", () => + Effect.gen(function*() { + let clientSpan: Tracer.NativeSpan | undefined + let sentUrl: string | undefined + const tracer = Tracer.make({ + span(options) { + clientSpan = new Tracer.NativeSpan(options) + return clientSpan + } + }) + const client = HttpClient.make((request, url) => { + sentUrl = url.toString() + return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null))) + }) + + yield* HttpClientRequest.get("http://test/path?page=1#frag").pipe( + HttpClientRequest.appendUrlParam("remote", Redacted.make("param-secret")), + HttpClientRequest.appendUrlParam("\uD800", Redacted.make("surrogate-secret")), + HttpClientRequest.appendUrlParam("q", "visible"), + client.execute, + Effect.provideService(Tracer.Tracer, tracer) + ) + + assert(clientSpan !== undefined) + assert.strictEqual( + clientSpan.attributes.get("url.full"), + "http://test/path?page=1&remote=REDACTED&%EF%BF%BD=REDACTED&q=visible#frag" + ) + assert.strictEqual( + clientSpan.attributes.get("url.query"), + "page=1&remote=REDACTED&%EF%BF%BD=REDACTED&q=visible" + ) + assert.strictEqual( + sentUrl, + "http://test/path?page=1&remote=param-secret&%EF%BF%BD=surrogate-secret&q=visible#frag" + ) + })) + + const makeCapturingClient = () => { + let sent: HttpClientRequest.HttpClientRequest | undefined + const client = HttpClient.make((request) => { + sent = request + return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null))) + }) + return { client, sent: () => sent } + } + + it.effect("sends trace headers by default", () => + Effect.gen(function*() { + const { client, sent } = makeCapturingClient() + + yield* client.get("http://test/") + + const request = sent() + assert(request !== undefined) + assert.strictEqual(typeof request.headers.b3, "string") + assert.strictEqual(typeof request.headers.traceparent, "string") + })) + + it.effect("omits trace headers when propagation is disabled and keeps the client span", () => + Effect.gen(function*() { + const { client, sent } = makeCapturingClient() + let clientSpan: Tracer.NativeSpan | undefined + const tracer = Tracer.make({ + span(options) { + clientSpan = new Tracer.NativeSpan(options) + return clientSpan + } + }) + + yield* client.get("http://test/").pipe( + Effect.provideService(HttpClient.TracerPropagationEnabled, false), + Effect.provideService(Tracer.Tracer, tracer) + ) + + const request = sent() + assert(request !== undefined) + assert.isUndefined(request.headers.b3) + assert.isUndefined(request.headers.traceparent) + assert(clientSpan !== undefined) + })) + }) + + describe("HttpClientError", () => { + it.effect("does not expose Redacted query params", () => + Effect.gen(function*() { + const client = HttpClient.make((request) => + Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 500 }))) + ).pipe(HttpClient.filterStatusOk) + + const error = yield* client.get("http://test/path", { + urlParams: { remote: Redacted.make("param-secret"), q: "visible" } + }).pipe(Effect.flip) + + for (const output of [JSON.stringify(error), String(error), inspect(error, { depth: 10 })]) { + assert.notInclude(output, "param-secret") + } + assert.include(JSON.stringify(error), "visible") + })) + + it.effect("redacts query params in messages using the current redacted names", () => + Effect.gen(function*() { + const request = HttpClientRequest.get("http://test/path?token=token-secret&sig=sig-visible") + const error = new HttpClientError.HttpClientError({ + reason: new HttpClientError.TransportError({ request }) + }) + + strictEqual(error.message, "Transport error (GET http://test/path?token=REDACTED&sig=sig-visible)") + }).pipe(Effect.provideService(UrlParams.CurrentRedactedNames, ["token"]))) }) describe("followRedirects", () => { @@ -413,6 +587,43 @@ Missing key yield* retryClient.get("http://test/").pipe(Effect.ignore) strictEqual(yield* Ref.get(attempts), 1) })) + + it.effect.each([ + { name: "seconds", header: "1", status: 503, delay: 1_000 }, + { name: "a five-minute Retry-After", header: "300", status: 429, delay: 300_000 }, + { name: "HTTP date on an errors-only retry", header: "date", status: 429, delay: 30_000 }, + { name: "a longer schedule", header: "5", status: 503, delay: 60_000, scheduled: true }, + { name: "no header", status: 503, delay: 0 } + ])( + "honors the retry delay for $name", + ({ delay, header, scheduled, status }) => + Effect.gen(function*() { + const retryAfter = header === "date" + ? new Date((yield* Clock.currentTimeMillis) + 30_000).toUTCString() + : header + const { attempts, client } = yield* makeSequenceClient([ + { status, headers: retryAfter === undefined ? {} : { "retry-after": retryAfter } }, + { status: 200 } + ]) + const retryClient = (header === "date" ? HttpClient.filterStatusOk(client) : client).pipe( + HttpClient.retryTransient({ + retryOn: header === "date" ? "errors-only" : "errors-and-responses", + times: 1, + ...(scheduled ? { schedule: Schedule.spaced("1 minute") } : {}) + }) + ) + const fiber = yield* retryClient.get("http://test/").pipe( + Effect.forkChild({ startImmediately: true }) + ) + + if (delay > 0) { + yield* TestClock.adjust(Duration.millis(delay - 1)) + strictEqual(yield* Ref.get(attempts), 1) + yield* TestClock.adjust("1 millis") + } + strictEqual((yield* Fiber.join(fiber)).status, 200) + }) + ) }) describe("stream", () => { @@ -606,9 +817,234 @@ Missing key strictEqual(yield* Ref.get(attempts), 1) - yield* TestClock.adjust("10 seconds") + yield* TestClock.adjust("59 seconds") + strictEqual(yield* Ref.get(attempts), 1) + + yield* TestClock.adjust("1 second") + yield* Fiber.join(fiber) + strictEqual(yield* Ref.get(attempts), 2) + }).pipe(Effect.provide(RateLimiterTestLayer))) + + it.effect("tracks remaining headers when the limit header is also present", () => + Effect.gen(function*() { + const attempts = yield* Ref.make(0) + const client = HttpClient.make((request) => + Effect.map( + Ref.updateAndGet(attempts, (n) => n + 1), + (attempt) => + HttpClientResponse.fromWeb( + request, + attempt === 1 + ? new Response(null, { + status: 200, + headers: { + "ratelimit-limit": "800", + "ratelimit-remaining": "1", + "ratelimit-reset-after": "60" + } + }) + : new Response(null, { status: 200 }) + ) + ) + ).pipe( + HttpClient.withRateLimiter({ + limiter: yield* RateLimiter.RateLimiter, + key: "reported-remaining", + limit: 800, + window: "1 minute" + }) + ) + + yield* client.get("http://test/") + yield* client.get("http://test/") + const fiber = yield* client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + + yield* TestClock.adjust("59 seconds") + strictEqual(yield* Ref.get(attempts), 2) + + yield* TestClock.adjust("1 second") + yield* Fiber.join(fiber) + strictEqual(yield* Ref.get(attempts), 3) + }).pipe(Effect.provide(RateLimiterTestLayer))) + + it.effect("retries a 429 at the reported reset when remaining is exhausted", () => + Effect.gen(function*() { + const attempts = yield* Ref.make(0) + const client = HttpClient.make((request) => + Effect.map( + Ref.updateAndGet(attempts, (n) => n + 1), + (attempt) => + HttpClientResponse.fromWeb( + request, + attempt === 1 + ? new Response(null, { + status: 429, + headers: { + "ratelimit-limit": "800", + "ratelimit-remaining": "0", + "ratelimit-reset-after": "60" + } + }) + : new Response(null, { status: 200 }) + ) + ) + ).pipe( + HttpClient.withRateLimiter({ + limiter: yield* RateLimiter.RateLimiter, + key: "reported-429", + limit: 800, + window: "1 minute", + times: 1, + disableAdaptiveLearning: true + }) + ) + + const fiber = yield* client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + + yield* TestClock.adjust("59 seconds") + strictEqual(yield* Ref.get(attempts), 1) + + yield* TestClock.adjust("1 second") + const response = yield* Fiber.join(fiber) + strictEqual(response.status, 200) + strictEqual(yield* Ref.get(attempts), 2) + }).pipe(Effect.provide(RateLimiterTestLayer))) + + it.effect("ignores stale remaining headers from out-of-order responses", () => + Effect.gen(function*() { + const attempts = yield* Ref.make(0) + const releaseFirst = yield* Deferred.make() + const client = HttpClient.make((request) => + Effect.flatMap( + Ref.updateAndGet(attempts, (n) => n + 1), + (attempt) => { + const response = (headers: Record = {}) => + HttpClientResponse.fromWeb(request, new Response(null, { status: 200, headers })) + if (attempt === 1) { + return Effect.as( + Deferred.await(releaseFirst), + response({ "ratelimit-remaining": "5", "ratelimit-reset-after": "60" }) + ) + } + return Effect.succeed( + attempt === 2 + ? response({ "ratelimit-remaining": "0", "ratelimit-reset-after": "60" }) + : response() + ) + } + ) + ).pipe( + HttpClient.withRateLimiter({ + limiter: yield* RateLimiter.RateLimiter, + key: "stale-remaining", + limit: 800, + window: "1 minute" + }) + ) + + const first = yield* client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + yield* client.get("http://test/") + yield* Deferred.succeed(releaseFirst, undefined) + yield* Fiber.join(first) + + const fiber = yield* client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + + yield* TestClock.adjust("59 seconds") + strictEqual(yield* Ref.get(attempts), 2) + + yield* TestClock.adjust("1 second") yield* Fiber.join(fiber) + strictEqual(yield* Ref.get(attempts), 3) + }).pipe(Effect.provide(RateLimiterTestLayer))) + + it.effect("counts requests sent after the reporting request against its remaining", () => + Effect.gen(function*() { + const attempts = yield* Ref.make(0) + const releaseFirst = yield* Deferred.make() + const client = HttpClient.make((request) => + Effect.flatMap( + Ref.updateAndGet(attempts, (n) => n + 1), + (attempt) => + attempt === 1 + ? Effect.as( + Deferred.await(releaseFirst), + HttpClientResponse.fromWeb( + request, + new Response(null, { + status: 200, + headers: { "ratelimit-remaining": "1", "ratelimit-reset-after": "60" } + }) + ) + ) + : Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 200 }))) + ) + ).pipe( + HttpClient.withRateLimiter({ + limiter: yield* RateLimiter.RateLimiter, + key: "in-flight-remaining", + limit: 800, + window: "1 minute" + }) + ) + + const first = yield* client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + yield* client.get("http://test/") + yield* Deferred.succeed(releaseFirst, undefined) + yield* Fiber.join(first) + + const fiber = yield* client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + + yield* TestClock.adjust("59 seconds") strictEqual(yield* Ref.get(attempts), 2) + + yield* TestClock.adjust("1 second") + yield* Fiber.join(fiber) + strictEqual(yield* Ref.get(attempts), 3) + }).pipe(Effect.provide(RateLimiterTestLayer))) + + it.effect("paces requests started while remaining is exhausted with the limiter after reset", () => + Effect.gen(function*() { + const attempts = yield* Ref.make(0) + const client = HttpClient.make((request) => + Effect.map( + Ref.updateAndGet(attempts, (n) => n + 1), + (attempt) => + HttpClientResponse.fromWeb( + request, + attempt === 1 + ? new Response(null, { + status: 200, + headers: { + "ratelimit-limit": "2", + "ratelimit-remaining": "0", + "ratelimit-reset-after": "60" + } + }) + : new Response(null, { status: 200 }) + ) + ) + ).pipe( + HttpClient.withRateLimiter({ + limiter: yield* RateLimiter.RateLimiter, + key: "exhausted-pacing", + limit: 2, + window: "1 minute", + disableAdaptiveLearning: true + }) + ) + + yield* client.get("http://test/") + const fibers = yield* Effect.forEach( + [1, 2, 3], + () => client.get("http://test/").pipe(Effect.forkChild({ startImmediately: true })) + ) + + yield* TestClock.adjust("60 seconds") + strictEqual(yield* Ref.get(attempts), 3) + + yield* TestClock.adjust("60 seconds") + yield* Fiber.joinAll(fibers) + strictEqual(yield* Ref.get(attempts), 4) }).pipe(Effect.provide(RateLimiterTestLayer))) it.effect("inspects custom remaining and reset-after headers", () => @@ -652,7 +1088,7 @@ Missing key strictEqual(yield* Ref.get(attempts), 1) - yield* TestClock.adjust("10 seconds") + yield* TestClock.adjust("1 minute") yield* Fiber.join(fiber) strictEqual(yield* Ref.get(attempts), 2) }).pipe(Effect.provide(RateLimiterTestLayer))) @@ -856,6 +1292,45 @@ Missing key strictEqual(yield* Ref.get(attempts), 3) }).pipe(Effect.provide(RateLimiterTestLayer))) + it.effect("waits until a Retry-After HTTP date", () => + Effect.gen(function*() { + const retryAt = new Date((yield* Clock.currentTimeMillis) + 30_000).toUTCString() + const attempts = yield* Ref.make(0) + const client = HttpClient.make((request) => + Effect.map( + Ref.updateAndGet(attempts, (n) => n + 1), + (attempt) => + HttpClientResponse.fromWeb( + request, + new Response(null, { + status: attempt === 1 ? 429 : 200, + headers: { "retry-after": retryAt } + }) + ) + ) + ).pipe( + HttpClient.withRateLimiter({ + limiter: yield* RateLimiter.RateLimiter, + key: "http-date-retry-after", + limit: 100, + window: "1 minute", + times: 1, + disableAdaptiveLearning: true + }) + ) + + const fiber = yield* client.get("http://test/").pipe(Effect.forkChild) + + yield* TestClock.adjust("29 seconds") + strictEqual(yield* Ref.get(attempts), 1) + + yield* TestClock.adjust("1 second") + const response = yield* Fiber.join(fiber) + + strictEqual(response.status, 200) + strictEqual(yield* Ref.get(attempts), 2) + }).pipe(Effect.provide(RateLimiterTestLayer))) + it.effect("uses a custom Retry-After header", () => Effect.gen(function*() { const attempts = yield* Ref.make(0) diff --git a/repos/effect/packages/effect/test/http/HttpClientRequest.test.ts b/packages/effect/test/http/HttpClientRequest.test.ts similarity index 61% rename from repos/effect/packages/effect/test/http/HttpClientRequest.test.ts rename to packages/effect/test/http/HttpClientRequest.test.ts index 5d3dd08fb..eaae8da82 100644 --- a/repos/effect/packages/effect/test/http/HttpClientRequest.test.ts +++ b/packages/effect/test/http/HttpClientRequest.test.ts @@ -1,10 +1,109 @@ import { describe, it } from "@effect/vitest" -import { assertNone, assertSome, assertTrue, deepStrictEqual, strictEqual } from "@effect/vitest/utils" -import { ByteSize, Effect, FileSystem, Stream } from "effect" +import { assertFalse, assertNone, assertSome, assertTrue, deepStrictEqual, strictEqual } from "@effect/vitest/utils" +import { ByteSize, Effect, FileSystem, Formatter, Redacted, Stream } from "effect" import { Headers, HttpBody, HttpClientRequest } from "effect/http" import * as Option from "effect/Option" +import { inspect } from "node:util" +import { gunzipSync, inflateSync } from "node:zlib" describe("HttpClientRequest", () => { + describe("compressBody", () => { + const url = "https://example.com" + const text = "hello compression ".repeat(100) + const bytes = new TextEncoder().encode(text) + + it.each( + [ + { name: "bytes", body: HttpBody.uint8Array(bytes, "application/custom"), expected: text, algorithm: "gzip" }, + { name: "lazy text", body: HttpBody.text(text), expected: text, algorithm: "gzip" }, + { name: "JSON", body: HttpBody.jsonUnsafe({ text }), expected: JSON.stringify({ text }), algorithm: "gzip" }, + { name: "deflate", body: HttpBody.uint8Array(bytes), expected: text, algorithm: "deflate" } + ] as const + )("round-trips $name as compressed bytes", async ({ algorithm, body, expected }) => { + const original = HttpClientRequest.post(url).pipe(HttpClientRequest.setBody(body)) + const effect: Effect.Effect = HttpClientRequest.compressBody( + original, + algorithm + ) + const request = await Effect.runPromise(effect) + strictEqual(request.body._tag, "Uint8Array") + if (request.body._tag !== "Uint8Array") { + throw new Error("Expected a compressed Uint8Array body") + } + const decompress = algorithm === "gzip" ? gunzipSync : inflateSync + strictEqual(decompress(request.body.body).toString(), expected) + strictEqual(request.body.text, undefined) + strictEqual(request.body.contentType, body.contentType) + strictEqual(request.body.contentLength, request.body.body.byteLength) + strictEqual(request.headers["content-type"], body.contentType) + strictEqual(request.headers["content-encoding"], algorithm) + strictEqual(request.headers["content-length"], String(request.body.body.byteLength)) + const web = await Effect.runPromise(HttpClientRequest.toWeb(request)) + strictEqual(decompress(new Uint8Array(await web.arrayBuffer())).toString(), expected) + strictEqual(await Effect.runPromise(HttpClientRequest.compressBody(request, algorithm)), request) + }) + + it.each([ + { text: "a".repeat(1023), options: undefined, compressed: false }, + { text: "a".repeat(1024), options: undefined, compressed: true }, + { text: "éé", options: { minSize: 5 }, compressed: false }, + { text: "éé", options: { minSize: 4 }, compressed: true } + ])("applies the byte threshold at $options with $text.length characters", async ({ compressed, options, text }) => { + const original = HttpClientRequest.post(url).pipe(HttpClientRequest.bodyText(text)) + const request = await Effect.runPromise(original.pipe(HttpClientRequest.compressBody("gzip", options))) + if (compressed) { + strictEqual(request.headers["content-encoding"], "gzip") + strictEqual(request.body._tag, "Uint8Array") + if (request.body._tag === "Uint8Array") { + strictEqual(gunzipSync(request.body.body).toString(), text) + } + } else { + strictEqual(request, original) + strictEqual(request.headers["content-encoding"], undefined) + } + }) + + it.effect("leaves unsupported bodies and existing content encodings unchanged", () => + Effect.gen(function*() { + for (const body of [HttpBody.empty, HttpBody.formData(new FormData()), HttpBody.raw(bytes)]) { + const original = HttpClientRequest.post(url).pipe(HttpClientRequest.setBody(body)) + const request = yield* HttpClientRequest.compressBody(original, "gzip", { minSize: 0 }) + strictEqual(request, original) + strictEqual(request.headers["content-encoding"], undefined) + } + for (const encoding of ["identity", "deflate", "gzip"]) { + const original = HttpClientRequest.post(url).pipe( + HttpClientRequest.bodyText(text), + HttpClientRequest.setHeader("Content-Encoding", encoding) + ) + strictEqual(yield* HttpClientRequest.compressBody(original, "gzip"), original) + } + })) + + it.each([ + { name: "known length", text, contentLength: bytes.byteLength }, + { name: "unknown length below minSize", text: "tiny", contentLength: undefined } + ])("compresses a stream with $name without a content length", async ({ contentLength, text }) => { + const source = new TextEncoder().encode(text) + const original = HttpClientRequest.post(url).pipe( + HttpClientRequest.bodyStream(Stream.make(source.subarray(0, 2), source.subarray(2)), { + contentType: "text/plain", + contentLength + }) + ) + const request = await Effect.runPromise(original.pipe(HttpClientRequest.compressBody("gzip"))) + strictEqual(request.body._tag, "Stream") + strictEqual(request.headers["content-length"], undefined) + strictEqual(request.headers["content-encoding"], "gzip") + strictEqual(request.headers["content-type"], "text/plain") + if (request.body._tag === "Stream") { + strictEqual(request.body.contentLength, undefined) + const compressed = await Effect.runPromise(Stream.mkUint8Array(request.body.stream)) + strictEqual(gunzipSync(compressed).toString(), text) + } + }) + }) + describe("bodyFile", () => { const oversized = 9007199254740993n const fileSystem = (size: bigint) => @@ -217,6 +316,31 @@ describe("HttpClientRequest", () => { }) }) + describe("toJSON", () => { + it("redacts sensitive query params in url and urlParams", () => { + const request = HttpClientRequest.get("https://api.test/path?Signature=url-secret&page=1", { + urlParams: { sig: "param-secret", q: "visible" } + }) + + strictEqual((request.toJSON() as { readonly url: string }).url, "https://api.test/path?Signature=REDACTED&page=1") + const output = Formatter.format(request) + assertFalse(output.includes("url-secret")) + assertFalse(output.includes("param-secret")) + assertTrue(output.includes("visible")) + }) + + it("hides Redacted url params from inspection", () => { + const request = HttpClientRequest.get("https://api.test/path").pipe( + HttpClientRequest.setUrlParam("remote", Redacted.make("param-secret")), + HttpClientRequest.appendUrlParam("q", "visible") + ) + for (const output of [JSON.stringify(request), String(request), inspect(request)]) { + assertFalse(output.includes("param-secret")) + assertTrue(output.includes("visible")) + } + }) + }) + describe("web conversions", () => { it.effect("fromWeb", () => Effect.gen(function*() { diff --git a/repos/effect/packages/effect/test/http/HttpCompression.test.ts b/packages/effect/test/http/HttpCompression.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpCompression.test.ts rename to packages/effect/test/http/HttpCompression.test.ts diff --git a/packages/effect/test/http/HttpDecompression.test.ts b/packages/effect/test/http/HttpDecompression.test.ts new file mode 100644 index 000000000..811b0f253 --- /dev/null +++ b/packages/effect/test/http/HttpDecompression.test.ts @@ -0,0 +1,228 @@ +import { afterAll, assert, describe, it } from "@effect/vitest" +import * as ByteSize from "effect/ByteSize" +import * as Effect from "effect/Effect" +import * as HttpEffect from "effect/http/HttpEffect" +import * as HttpIncomingMessage from "effect/http/HttpIncomingMessage" +import * as HttpMiddleware from "effect/http/HttpMiddleware" +import * as HttpRouter from "effect/http/HttpRouter" +import * as HttpServer from "effect/http/HttpServer" +import * as HttpServerRequest from "effect/http/HttpServerRequest" +import * as HttpServerResponse from "effect/http/HttpServerResponse" +import * as Layer from "effect/Layer" +import * as Stream from "effect/Stream" + +const bigJson = JSON.stringify({ text: "All work and no play makes Jack a dull boy. ".repeat(100) }) + +type Format = "gzip" | "deflate" +type DecompressionOptions = Parameters[0] + +const compress = async (data: string | Uint8Array, formats: ReadonlyArray): Promise => { + let body = new Blob([data as BlobPart]).stream() + for (const format of formats) { + body = body.pipeThrough(new CompressionStream(format)) + } + return new Uint8Array(await new Response(body).arrayBuffer()) +} + +const post = ( + handler: (request: Request) => Promise, + body: Uint8Array | string, + headers?: Record, + path = "/" +) => + handler( + new Request(`http://localhost${path}`, { + method: "POST", + body: body as BodyInit, + ...(headers === undefined ? {} : { headers }) + }) + ) + +const readers = { + json: (request: HttpServerRequest.HttpServerRequest) => Effect.map(request.json, (json) => JSON.stringify(json)), + text: (request: HttpServerRequest.HttpServerRequest) => request.text, + arrayBuffer: (request: HttpServerRequest.HttpServerRequest) => + Effect.map(request.arrayBuffer, (buffer) => new TextDecoder().decode(buffer)), + stream: (request: HttpServerRequest.HttpServerRequest) => + Effect.map(Stream.mkUint8Array(request.stream), (bytes) => new TextDecoder().decode(bytes)) +} as const + +type Accessor = keyof typeof readers + +// Echoes the body read through `accessor`, along with the request's +// Content-Encoding and Content-Length as the app sees them. +const echo = (accessor: Accessor) => (request: HttpServerRequest.HttpServerRequest) => + Effect.map(readers[accessor](request), (body) => + HttpServerResponse.text(body, { + headers: { + "x-content-encoding": request.headers["content-encoding"] ?? "", + "x-content-length": request.headers["content-length"] ?? "" + } + })) + +const echoApp = (accessor: Accessor) => HttpServerRequest.HttpServerRequest.use(echo(accessor)) + +const disposers: Array<() => Promise> = [] + +afterAll(() => Promise.all(disposers.map((dispose) => dispose()))) + +const makeHandler = ( + app: Effect.Effect, + options?: DecompressionOptions, + maxBodySize?: ByteSize.ByteSize +) => { + const { dispose, handler } = HttpEffect.toWebHandlerLayer( + app, + Layer.merge(HttpServer.layerServices, Layer.succeed(HttpIncomingMessage.MaxBodySize, maxBodySize)), + { middleware: HttpMiddleware.decompression(options) } + ) + disposers.push(dispose) + return handler +} + +describe("HttpDecompression", () => { + for (const accessor of ["json", "text", "arrayBuffer", "stream"] as const) { + it(`decompresses a gzip body read via ${accessor} and hides the encoding headers`, async () => { + const response = await post( + makeHandler(echoApp(accessor)), + await compress(bigJson, ["gzip"]), + { "content-type": "application/json", "content-encoding": "gzip" } + ) + assert.strictEqual(response.status, 200) + assert.strictEqual(await response.text(), bigJson) + assert.strictEqual(response.headers.get("x-content-encoding"), "") + assert.strictEqual(response.headers.get("x-content-length"), "") + }) + } + + for (const formats of [["deflate"], ["deflate", "gzip"]] as const) { + it(`decodes Content-Encoding: ${formats.join(", ")}`, async () => { + const response = await post( + makeHandler(echoApp("text")), + await compress(bigJson, formats), + { "content-encoding": formats.join(", ") } + ) + assert.strictEqual(response.status, 200) + assert.strictEqual(await response.text(), bigJson) + }) + } + + it("passes through bodies without a coding or with identity", async () => { + const handler = makeHandler(echoApp("text")) + + const plain = await post(handler, bigJson) + assert.strictEqual(plain.status, 200) + assert.strictEqual(await plain.text(), bigJson) + + const identity = await post(handler, bigJson, { "content-encoding": "identity" }) + assert.strictEqual(identity.status, 200) + assert.strictEqual(await identity.text(), bigJson) + }) + + it("rejects unsupported codings with 415 before running the app", async () => { + let ran = false + const app = Effect.sync(() => { + ran = true + return HttpServerResponse.empty() + }) + const response = await post(makeHandler(app), bigJson, { "content-encoding": "br" }) + assert.strictEqual(response.status, 415) + assert.strictEqual(response.headers.get("accept-encoding"), "gzip, deflate") + assert.isFalse(ran) + }) + + it("fails corrupt gzip bodies with 400", async () => { + const response = await post(makeHandler(echoApp("text")), "not gzip", { "content-encoding": "gzip" }) + assert.strictEqual(response.status, 400) + }) + + for (const accessor of ["text", "stream"] as const) { + it(`enforces maxDecompressedSize when reading via ${accessor}`, async () => { + const body = await compress(bigJson, ["gzip"]) + // the limit must apply to the inflated size, not the size on the wire + assert.isBelow(body.length, 1024) + const response = await post( + makeHandler(echoApp(accessor), { maxDecompressedSize: 1024 }), + body, + { "content-encoding": "gzip" } + ) + assert.strictEqual(response.status, 400) + }) + } + + it("falls back to the MaxBodySize reference", async () => { + const response = await post( + makeHandler( + echoApp("text"), + undefined, + ByteSize.bytes(1024) + ), + await compress(bigJson, ["gzip"]), + { "content-encoding": "gzip" } + ) + assert.strictEqual(response.status, 400) + }) + + it("maps multipart decompression failures to client errors", async () => { + const multipart = new FormData() + multipart.append("text", bigJson) + const encoded = new Response(multipart) + const headers = { "content-type": encoded.headers.get("content-type")!, "content-encoding": "gzip" } + const body = new Uint8Array(await encoded.arrayBuffer()) + const handler = makeHandler( + HttpServerRequest.HttpServerRequest.use((request) => + Effect.as(Stream.runDrain(request.multipartStream), HttpServerResponse.empty()) + ), + { maxDecompressedSize: 1024 } + ) + + const corrupt = await post(handler, "not gzip", headers) + assert.strictEqual(corrupt.status, 400) + + const tooLarge = await post(handler, await compress(body, ["gzip"]), headers) + assert.strictEqual(tooLarge.status, 413) + }) + + it("cancels the request body when a stream read stops early", async () => { + const compressed = await compress(bigJson, ["gzip"]) + let onCancel!: () => void + const cancelled = new Promise((resolve) => { + onCancel = resolve + }) + // never closes, so the upstream only finishes if cancellation propagates + const body = new ReadableStream({ + start(controller) { + controller.enqueue(compressed) + }, + cancel() { + onCancel() + } + }) + const handler = makeHandler( + HttpServerRequest.HttpServerRequest.use((request) => + Effect.as(Stream.runHead(request.stream), HttpServerResponse.empty()) + ) + ) + const response = await handler( + new Request("http://localhost/", { + method: "POST", + body, + headers: { "content-encoding": "gzip" }, + duplex: "half" + } as RequestInit) + ) + assert.strictEqual(response.status, 204) + await cancelled + }) + + it("decompresses bodies for routes under a prefixed router", async () => { + const { dispose, handler } = HttpRouter.toWebHandler( + HttpRouter.use((router) => router.prefixed("/api").add("POST", "/echo", echo("text"))), + { middleware: HttpMiddleware.decompression(), disableLogger: true } + ) + disposers.push(dispose) + const response = await post(handler, await compress(bigJson, ["gzip"]), { "content-encoding": "gzip" }, "/api/echo") + assert.strictEqual(response.status, 200) + assert.strictEqual(await response.text(), bigJson) + }) +}) diff --git a/repos/effect/packages/effect/test/http/HttpEffect.test.ts b/packages/effect/test/http/HttpEffect.test.ts similarity index 86% rename from repos/effect/packages/effect/test/http/HttpEffect.test.ts rename to packages/effect/test/http/HttpEffect.test.ts index c1aa0bfe0..96f6ea613 100644 --- a/repos/effect/packages/effect/test/http/HttpEffect.test.ts +++ b/packages/effect/test/http/HttpEffect.test.ts @@ -160,6 +160,75 @@ describe("HttpEffect", () => { strictEqual(response.headers.get("content-length"), "9007199254740993") }) + test.each(["before the request", "while the handler starts"] as const)( + "an abort %s interrupts the handler as a client abort", + async (when) => { + const controller = new AbortController() + if (when === "before the request") controller.abort() + let interrupted = false + const handler = HttpEffect.toWebHandler( + Effect.sync(() => { + if (when === "while the handler starts") controller.abort() + }).pipe( + Effect.andThen(Effect.never), + Effect.onInterrupt(() => + Effect.sync(() => { + interrupted = true + }) + ), + // Handlers are uninterruptible unless they opt in, as router routes do. + Effect.interruptible + ) + ) + const response = await handler(new Request("http://localhost:3000/", { signal: controller.signal })) + strictEqual(response.status, 499) + strictEqual(interrupted, true) + } + ) + + test.each(["after reading a chunk", "while the unread body is full"] as const)( + "an abort %s releases the request scope owned by a streamed body", + async (when) => { + const controller = new AbortController() + let finalized = false + let pulledSecond = false + const handler = HttpEffect.toWebHandler(Effect.gen(function*() { + yield* Effect.addFinalizer(() => + Effect.sync(() => { + finalized = true + }) + ) + return HttpServerResponse.stream( + Stream.make("first").pipe( + Stream.concat(Stream.fromEffect(Effect.sync(() => { + pulledSecond = true + return "second" + }))), + Stream.concat(Stream.never), + Stream.encodeText + ) + ) + })) + const waitFor = (predicate: () => boolean) => + new Promise((resolve) => { + const deadline = Date.now() + 1000 + const poll = () => predicate() || Date.now() > deadline ? resolve() : setImmediate(poll) + poll() + }) + const response = await handler(new Request("http://localhost:3000/", { signal: controller.signal })) + if (when === "after reading a chunk") { + await response.body!.getReader().read() + } else { + // The body has taken its second chunk and now waits for a read that never comes. + await waitFor(() => pulledSecond) + } + + controller.abort() + await waitFor(() => finalized) + strictEqual(finalized, true) + } + ) + test("stream scope", async () => { let order = 0 let streamFinalized = 0 diff --git a/repos/effect/packages/effect/test/http/HttpMiddleware.test.ts b/packages/effect/test/http/HttpMiddleware.test.ts similarity index 94% rename from repos/effect/packages/effect/test/http/HttpMiddleware.test.ts rename to packages/effect/test/http/HttpMiddleware.test.ts index 9d042244b..3355593ff 100644 --- a/repos/effect/packages/effect/test/http/HttpMiddleware.test.ts +++ b/packages/effect/test/http/HttpMiddleware.test.ts @@ -357,6 +357,35 @@ describe("HttpMiddleware", () => { assert.strictEqual(serverSpan.attributes.get("http.response.header.x-response-secret"), undefined) })) + it.effect("redacts sensitive query params in url attributes", () => + Effect.gen(function*() { + let serverSpan: Tracer.NativeSpan | undefined + let handlerUrl: string | undefined + const tracer = Tracer.make({ + span(options) { + serverSpan = new Tracer.NativeSpan(options) + return serverSpan + } + }) + const request = HttpServerRequest.fromWeb( + new Request("http://localhost/todos?sig=sig-secret&foo=bar") + ) + + yield* HttpMiddleware.tracer(Effect.gen(function*() { + handlerUrl = (yield* HttpServerRequest.HttpServerRequest).url + return HttpServerResponse.empty() + })).pipe( + Effect.provideService(HttpServerRequest.HttpServerRequest, request), + Effect.provideService(Tracer.Tracer, tracer) + ) + yield* Effect.yieldNow + + assert(serverSpan !== undefined) + assert.strictEqual(serverSpan.attributes.get("url.full"), "http://localhost/todos?sig=REDACTED&foo=bar") + assert.strictEqual(serverSpan.attributes.get("url.query"), "sig=REDACTED&foo=bar") + assert.strictEqual(handlerUrl, "/todos?sig=sig-secret&foo=bar") + })) + it.effect("skips attributes for unsampled spans", () => Effect.gen(function*() { let serverSpan: Tracer.NativeSpan | undefined diff --git a/repos/effect/packages/effect/test/http/HttpPlatform.test.ts b/packages/effect/test/http/HttpPlatform.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpPlatform.test.ts rename to packages/effect/test/http/HttpPlatform.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpRouter.test.ts b/packages/effect/test/http/HttpRouter.test.ts similarity index 70% rename from repos/effect/packages/effect/test/http/HttpRouter.test.ts rename to packages/effect/test/http/HttpRouter.test.ts index 39ffdd002..816c35738 100644 --- a/repos/effect/packages/effect/test/http/HttpRouter.test.ts +++ b/packages/effect/test/http/HttpRouter.test.ts @@ -64,6 +64,38 @@ describe("HttpRouter", () => { })) } + for ( + const { prefix, requestUrl, expected } of [ + { prefix: "/tenants/:tenant", requestUrl: "/tenants/a/users?x=1", expected: "/users?x=1" }, + { prefix: "/tenants/:tenant([^/]+)", requestUrl: "/tenants/acme/users?x=1", expected: "/users?x=1" }, + { prefix: "/café", requestUrl: "/caf%C3%A9/users", expected: "/users" } + ] + ) { + it.effect(`removes the matched ${prefix} prefix from ${requestUrl}`, () => + Effect.gen(function*() { + const routes = HttpRouter.use((router) => router.prefixed(prefix).add("GET", "/users", echoUrl)) + + const body = yield* fetchText(routes, requestUrl) + + assert.strictEqual(body, expected) + })) + } + + for (const ignoreDuplicateSlashes of [true, false]) { + it.effect(`preserves the local URL with ignoreDuplicateSlashes=${ignoreDuplicateSlashes}`, () => + Effect.gen(function*() { + const routes = HttpRouter.use((router) => router.prefixed("/api//v1").add("GET", "/users", echoUrl)).pipe( + Layer.provide( + Layer.succeed(HttpRouter.RouterConfig)(ignoreDuplicateSlashes ? {} : { ignoreDuplicateSlashes }) + ) + ) + + const body = yield* fetchText(routes, ignoreDuplicateSlashes ? "/api/v1/users?x=1" : "/api//v1/users?x=1") + + assert.strictEqual(body, "/users?x=1") + })) + } + it.effect("nests a prefixed sub-router beneath a prefixed parent router", () => Effect.gen(function*() { const routes = HttpRouter.use((router) => router.prefixed("/app").add("GET", "/users", echoUrl)).pipe( diff --git a/repos/effect/packages/effect/test/http/HttpServer.test.ts b/packages/effect/test/http/HttpServer.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServer.test.ts rename to packages/effect/test/http/HttpServer.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerError.test.ts b/packages/effect/test/http/HttpServerError.test.ts similarity index 89% rename from repos/effect/packages/effect/test/http/HttpServerError.test.ts rename to packages/effect/test/http/HttpServerError.test.ts index 16cdb6c10..3264e9631 100644 --- a/repos/effect/packages/effect/test/http/HttpServerError.test.ts +++ b/packages/effect/test/http/HttpServerError.test.ts @@ -14,6 +14,7 @@ import { import * as HttpServerRequest from "effect/http/HttpServerRequest" import * as Respondable from "effect/http/HttpServerRespondable" import * as HttpServerResponse from "effect/http/HttpServerResponse" +import * as UrlParams from "effect/http/UrlParams" const makeRequest = (method: string, url: string) => HttpServerRequest.fromWeb(new Request(url, { method })) const makeResponse = (status: number) => HttpServerResponse.empty({ status }) @@ -32,6 +33,16 @@ describe("HttpServerError", () => { assert.strictEqual(routeNotFound.message, "RouteNotFound (POST /widgets)") }) + it.effect("redacts query params using the current redacted names", () => + Effect.gen(function*() { + const request = makeRequest("GET", "http://example.com/widgets?token=token-secret&sig=sig-visible") + + assert.strictEqual( + new RouteNotFound({ request }).message, + "RouteNotFound (GET /widgets?token=REDACTED&sig=sig-visible)" + ) + }).pipe(Effect.provideService(UrlParams.CurrentRedactedNames, ["token"]))) + it("formats response reason messages", () => { const request = makeRequest("GET", "http://example.com/tea") const response = makeResponse(418) diff --git a/repos/effect/packages/effect/test/http/HttpServerRequest.test.ts b/packages/effect/test/http/HttpServerRequest.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServerRequest.test.ts rename to packages/effect/test/http/HttpServerRequest.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerRespondable.test.ts b/packages/effect/test/http/HttpServerRespondable.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpServerRespondable.test.ts rename to packages/effect/test/http/HttpServerRespondable.test.ts diff --git a/repos/effect/packages/effect/test/http/HttpServerResponse.test.ts b/packages/effect/test/http/HttpServerResponse.test.ts similarity index 85% rename from repos/effect/packages/effect/test/http/HttpServerResponse.test.ts rename to packages/effect/test/http/HttpServerResponse.test.ts index c86e90466..84ff55f17 100644 --- a/repos/effect/packages/effect/test/http/HttpServerResponse.test.ts +++ b/packages/effect/test/http/HttpServerResponse.test.ts @@ -4,6 +4,18 @@ import { Cookies, HttpBody, HttpClientRequest, HttpClientResponse, HttpServerRes const TestValue = Context.Reference("test/TestValue", { defaultValue: () => 0 }) +// Bun leaves `Response.redirect` headers mutable, unlike Node, Deno and workerd +const immutableHeaders = (response: Response): Response => { + for (const method of ["append", "set"]) { + Object.defineProperty(response.headers, method, { + value: () => { + throw new TypeError("immutable") + } + }) + } + return response +} + describe("HttpServerResponse", () => { describe("toWeb", () => { it.each([ @@ -44,13 +56,22 @@ describe("HttpServerResponse", () => { ) it("preserves raw Response metadata for HEAD", () => { - const native = new Response("body", { - status: 202, - statusText: "Accepted by upstream", - headers: { "x-probe": "native", "set-cookie": "sid=abc" } - }) + const native = immutableHeaders( + new Response("body", { + status: 202, + statusText: "Accepted by upstream", + headers: { "x-probe": "native", "x-shared": "native", "set-cookie": "sid=abc" } + }) + ) + // A locked body fails the test if HEAD copies the response + native.body!.getReader() const web = HttpServerResponse.toWeb( - HttpServerResponse.raw(native, { status: 200, statusText: "Outer status" }), + HttpServerResponse.raw(native, { + status: 200, + statusText: "Outer status", + headers: { "x-shared": "outer" }, + cookies: Cookies.setAllUnsafe(Cookies.empty, [["a", "1", {}]]) + }), { withoutBody: true } ) @@ -58,10 +79,11 @@ describe("HttpServerResponse", () => { assert.strictEqual(web.status, 202) assert.strictEqual(web.statusText, "Accepted by upstream") assert.strictEqual(web.headers.get("x-probe"), "native") - assert.deepStrictEqual(web.headers.getSetCookie(), ["sid=abc"]) + assert.strictEqual(web.headers.get("x-shared"), "outer") + assert.deepStrictEqual(web.headers.getSetCookie(), ["sid=abc", "a=1"]) }) - it("returns the raw Response with merged headers for GET", async () => { + it("merges headers for GET without modifying the raw Response", async () => { const native = new Response("body", { status: 202, statusText: "Accepted by upstream", @@ -74,16 +96,34 @@ describe("HttpServerResponse", () => { }) ) - assert.strictEqual(web, native) assert.strictEqual(web.status, 202) assert.strictEqual(web.statusText, "Accepted by upstream") assert.strictEqual(web.headers.get("x-probe"), "native") assert.strictEqual(web.headers.get("x-outer"), "outer") assert.strictEqual(web.headers.get("x-shared"), "outer") assert.deepStrictEqual(web.headers.getSetCookie(), ["sid=abc; Path=/; HttpOnly", "a=1", "b=2"]) + assert.strictEqual(native.headers.get("x-probe"), "native") + assert.strictEqual(native.headers.get("x-outer"), null) + assert.strictEqual(native.headers.get("x-shared"), "native") + assert.deepStrictEqual(native.headers.getSetCookie(), ["sid=abc; Path=/; HttpOnly"]) assert.strictEqual(await web.text(), "body") }) + it("merges outer headers into a raw Response with immutable headers", () => { + const native = immutableHeaders(Response.redirect("http://localhost/target", 302)) + const web = HttpServerResponse.toWeb( + HttpServerResponse.raw(native, { + headers: { "x-outer": "outer" }, + cookies: Cookies.setAllUnsafe(Cookies.empty, [["a", "1", {}]]) + }) + ) + + assert.strictEqual(web.status, 302) + assert.strictEqual(web.headers.get("location"), "http://localhost/target") + assert.strictEqual(web.headers.get("x-outer"), "outer") + assert.deepStrictEqual(web.headers.getSetCookie(), ["a=1"]) + }) + it.each([ { status: 200, withoutBody: true }, { status: 304, withoutBody: false } diff --git a/repos/effect/packages/effect/test/http/HttpStaticServer.test.ts b/packages/effect/test/http/HttpStaticServer.test.ts similarity index 88% rename from repos/effect/packages/effect/test/http/HttpStaticServer.test.ts rename to packages/effect/test/http/HttpStaticServer.test.ts index 4ab34911b..af96f607c 100644 --- a/repos/effect/packages/effect/test/http/HttpStaticServer.test.ts +++ b/packages/effect/test/http/HttpStaticServer.test.ts @@ -136,3 +136,18 @@ describe("HttpStaticServer precision", () => { }).pipe(Effect.provide(precisionServices(size, calls))) }) }) + +describe("HttpStaticServer paths", () => { + it.effect("serves a path with repeated leading slashes from the root", () => { + const calls: Array<{ path: string; options: FileOptions }> = [] + return Effect.gen(function*() { + const app = yield* HttpStaticServer.make({ root: "/root" }) + const response = yield* app.pipe(Effect.provideService( + HttpServerRequest.HttpServerRequest, + HttpServerRequest.fromWeb(new Request("http://localhost//nested/file.txt")) + )) + assert.strictEqual(response.status, 200) + assert.deepStrictEqual(calls.map((call) => call.path), ["/root/nested/file.txt"]) + }).pipe(Effect.provide(precisionServices(10n, calls))) + }) +}) diff --git a/repos/effect/packages/effect/test/http/HttpStatus.test.ts b/packages/effect/test/http/HttpStatus.test.ts similarity index 100% rename from repos/effect/packages/effect/test/http/HttpStatus.test.ts rename to packages/effect/test/http/HttpStatus.test.ts diff --git a/packages/effect/test/http/HttpTraceContext.test.ts b/packages/effect/test/http/HttpTraceContext.test.ts new file mode 100644 index 000000000..f0376cde0 --- /dev/null +++ b/packages/effect/test/http/HttpTraceContext.test.ts @@ -0,0 +1,88 @@ +import { describe, it } from "@effect/vitest" +import { assertNone, strictEqual } from "@effect/vitest/utils" +import { Effect, Option, Tracer } from "effect" +import { Headers, HttpTraceContext } from "effect/http" + +const traceId = "80f198ee56343ba864fe8b2a57d3eff7" +const spanId = "e457b5a2e4d86bd1" + +const assertSampled = (headers: Record, sampled: boolean) => { + const span = Option.getOrThrow(HttpTraceContext.fromHeaders(Headers.fromInput(headers))) + strictEqual(span.traceId, traceId) + strictEqual(span.spanId, spanId) + strictEqual(span.sampled, sampled) +} + +describe("HttpTraceContext", () => { + describe("fromHeaders", () => { + it("accepts single-header B3 debug sampling", () => { + assertSampled({ b3: `${traceId}-${spanId}-d` }, true) + }) + + it("accepts legacy X-B3-Sampled: true", () => { + assertSampled({ "X-B3-TraceId": traceId, "X-B3-SpanId": spanId, "X-B3-Sampled": "true" }, true) + }) + + it("lets X-B3-Flags debug override X-B3-Sampled: 0", () => { + assertSampled({ + "X-B3-TraceId": traceId, + "X-B3-SpanId": spanId, + "X-B3-Sampled": "0", + "X-B3-Flags": "1" + }, true) + }) + + it.each([ + { b3: `${traceId.slice(16)}-${spanId}-1` }, + { "X-B3-TraceId": traceId.slice(16), "X-B3-SpanId": spanId } + ])("accepts a 64-bit B3 trace-id: %j", (headers) => { + const span = Option.getOrThrow(HttpTraceContext.fromHeaders(Headers.fromInput(headers))) + strictEqual(span.traceId, traceId.slice(16)) + strictEqual(span.spanId, spanId) + }) + + it.each([ + { b3: ` - ``` - -## 0.3.3 - -### Patch Changes - -- df2e948: Fixes meta.required wrong inheritance and adds at least storybook story, waiting for unit tests - -## 0.3.2 - -### Patch Changes - -- e7c3aff: deep merge default values which comes from persistency wrt tanstack def values - -## 0.3.1 - -### Patch Changes - -- c8e5220: update OmegaForm persistency configuration to use policies array and add query params option - -## 0.3.0 - -### Minor Changes - -- 6f33b83: Enhance OmegaForm functionality with persistency support and improved input handling. Added clear and reset methods to the form API, updated internal input state management, and refined form stories for better demonstration of features. - -## 0.2.8 - -### Patch Changes - -- 7fcd006: feat: add showErrorOn props in OmegaForm Wrapper components, to manage when to show the errors - -## 0.2.7 - -### Patch Changes - -- f7909fc: Explicitly pass down model-value - -## 0.2.6 - -### Patch Changes - -- 34abab7: Changes spread order in OmegaFormVuetify - -## 0.2.5 - -### Patch Changes - -- f6043f4: manually pass clear function - -## 0.2.4 - -### Patch Changes - -- b9efc9f: Moves from custom ui to standard vuetify ui for clearable selects -- 587a06a: Fixes model value reference inside vuetify components - -## 0.2.3 - -### Patch Changes - -- Updated dependencies [d3b05cb] -- Updated dependencies [4627c7b] -- Updated dependencies [1e1f32b] - - effect-app@2.39.1 - - @effect-app/vue@2.40.1 - -## 0.2.2 - -### Patch Changes - -- Updated dependencies [bdc9d5c] - - effect-app@2.39.0 - - @effect-app/vue@2.40.0 - -## 0.2.1 - -### Patch Changes - -- d01c3e9: Manages focus in OmegaInput - -## 0.2.0 - -### Minor Changes - -- 7ac3d9d: Adds checkbox default to OmegaForm - -## 0.1.3 - -### Patch Changes - -- 21b0696: Fix: OmegaInput Attributes spreading - -## 0.1.2 - -### Patch Changes - -- Updated dependencies [8b7fdbe] - - effect-app@2.38.5 - - @effect-app/vue@2.39.9 - -## 0.1.1 - -### Patch Changes - -- b7cfa49: Fixes default styling for reset in select and autocomplete - -## 0.1.0 - -### Minor Changes - -- 98d5344: Enhance OmegaForm components with new input types and improved structure - - Added support to CSS inside JS bundle both for modules than for custom elements - - OmegaFormInput now fully support children as input, so vuetify it's only a default for environments that globally import that library - - Updated OmegaInput.vue to support autocomplete and autocompletemultiple types. - - Refactored OmegaInternalInput.vue to streamline input handling and improve slot usage. - - Modified OmegaErrors.vue to utilize slots for better flexibility in error display. - - Added new types to OmegaFormStuff.ts for enhanced type safety. - - Adjusted Vite configuration to support custom elements in Vue components. - -## 0.0.9 - -### Patch Changes - -- Updated dependencies - - effect-app@2.38.4 - - @effect-app/vue@2.39.8 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies - - effect-app@2.38.3 - - @effect-app/vue@2.39.7 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [c0ed28a] -- Updated dependencies [0fcab38] - - effect-app@2.38.2 - - @effect-app/vue@2.39.6 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [cd15a8e] - - effect-app@2.38.1 - - @effect-app/vue@2.39.5 - -## 0.0.5 - -### Patch Changes - -- Updated dependencies [81479bf] - - effect-app@2.38.0 - - @effect-app/vue@2.39.4 - -## 0.0.4 - -### Patch Changes - -- 6e7dc86: add support for textarea and pass attrs to input. - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [7bedb13] - - effect-app@2.37.1 - - @effect-app/vue@2.39.3 - -## 0.0.2 - -### Patch Changes - -- ce0bfca: fix style - -## 0.0.1 - -### Patch Changes - -- c3a730c: initial vue-components diff --git a/packages/vue-components/README.md b/packages/vue-components/README.md deleted file mode 100644 index b7cea449f..000000000 --- a/packages/vue-components/README.md +++ /dev/null @@ -1,233 +0,0 @@ -[![Netlify Status](https://api.netlify.com/api/v1/badges/b1b84831-789e-4629-a9e3-55a36e136653/deploy-status)](https://app.netlify.com/sites/sharp-babbage-154f0a/deploys) - -# Vue Component Library Starter - -> Create your own [Vue 3](https://v3.vuejs.org/) component library with TypeScript, [Vite](https://vitejs.dev) and [VitePress](https://vitepress.vuejs.org/). - -Sooner or later, you will find that creating a component library is much better than having all components inside your app project. A component library force to you remove app specific logic from your components, making it easier to test and reuse them in other apps. - -Once the components are in a library, documentation becomes critical. This starter project includes a documentation app powered by VitePress. It not only documents the usage of the component, but also provides a testing bed during the development of components. See the generated documentation app [here](https://sharp-babbage-154f0a.netlify.com/). - -## Setup - -> When running `docs:dev` for the first time, you may encounter error like `vitepress data not properly injected in app` in your browser. Restart the server and reload the browser. Please refer to [issue #30](https://github.com/wuruoyun/vue-component-lib-starter/issues/30) for more details. - -```bash -# install dependencies -npm install - -# start the doc app with hot reload, great for testing components -npm run docs:dev - -# build the library, available under dist -npm run build - -# build the doc app, available under docs/.vitepress/dist -npm run docs:build - -# preview the doc app locally from docs/.vitepress/dist -npm run docs:serve -``` - -You may use [Netlify](https://www.netlify.com/) to auto build and deploy the doc app like this project does. - -## Develop and test locally - -The best way to develop and test your component is by creating demos in `docs/components/demo` folder, as shown by the example components. - -If you want to test the library in your Vue3 app locally: - -- In the root folder of this library, run `npm link`. This will create a symbolic link to the library. -- In the root folder of your client app, run `npm link my-lib`. This will add the symbolic link to the `node_modules` folder in your client app. -- You can now import `my-lib` in your client app. - -There is no need to add `my-lib` to your client app's dependency in this case. - -If you made changes to the library, you will need to rebuild the library. Your Vue3 app shall hot reload when the building of library is completed. - -## How it works - -### Components - -The library is a [Vue plugin](https://v3.vuejs.org/guide/plugins.html). The `install` function in [index.ts](src/index.ts) registers all components under [components](src/components) to Vue globably. - -The components are also exported by [index.ts](src/index.ts) so that the client app can import them individually and register them locally, instead of using the library as a plugin. This may be a better option if the client app only use a small set of components in your library. - -As there are already many UI component libraries for Vue 3, you may just want to build on top of one of them and create components for your specific needs. The Component B in this starter shows the example of using [PrimeVue](https://www.primefaces.org/primevue/) as the fundation library. However, this means the client app shall also use the same fundation component library as your library does. - -The doc app itself is a client app of the libary, therefore PrimeVue is imported in [docs/.vitepress/theme/index.js](docs/.vitepress/theme/index.js). The configuration in [docs/.vitepress/config.js](docs/.vitepress/config.js) below forces VitePress to resolve these modules with no duplication, avoiding error at runtime, as PrimeVue also has Vue in its dependency. - -```js -module.exports = { - vite: { - resolve: { - dedupe: ["vue", /primevue\/.+/] - } - } -} -``` - -> In [vite.config.ts](vite.config.ts), format 'umd' is not present in `build.lib.formats` option. This is because the PrimeVue components used by this library are externalized, and therefore requiring corresponding options in `rollupOptions.output.globals`. To avoid adding global varaibles for PrimeVue components, 'umd' is removed for simplicity. - -### Utilities and constants - -The library includes example utilities and constants. They are also exported in [index.ts](src/index.ts). The client app may use them as below: - -```js - -``` - -### Styling - -Individual components may have styles defined in its `.vue` file. They will be processed, combined and minified into `dist/style.css`, which is included in the `exports` list in [package.json](package.json). - -If you have library level styles shared by all components in the library, you may add them to [src/assets/main.scss](src/assets/main.scss). This file is imported in [index.ts](src/index.ts), therefore the processed styles are also included into `dist/style.css`. To avoid conflicting with other global styles, consider pre-fixing the class names or wrapping them into a namespace class. - -If you have your own special set of SVG icons, you may create a font file (`.woff` format) using tools like [Icomoon](https://icomoon.io/) or [Fontello](https://fontello.com/). This starter includes an example font file [src/assets/fonts/myfont.woff](src/assets/fonts/myfont.woff) and references it in [src/assets/main.scss](src/assets/main.scss), with utility icon CSS classes. An icon from the font file is used in Component A. Vite will include the font file into the build, see [https://vitejs.dev/guide/assets.html](https://vitejs.dev/guide/assets.html). - -The client app shall import `style.css`, usually in the entry file: - -```js -import "my-lib/dist/style.css" -``` - -### Third-party dependencies - -Third-party libraries used by you library may bloat up the size of your library, if you simply add them to the `dependencies` in [package.json](package.json). - -The following are some strategies to reduce the size of your library: - -#### Externalization - -If you expect the client app of your library may also need the same dependency, you may externalize the dependency. For example, to exclude PrimeVue from your library build artifact, in [vite.config.ts](vite.config.ts), you may have - -```js -module.exports = defineConfig({ - rollupOptions: { - external: ['vue', /primevue\/.+/] - } - } -}) -``` - -The dependency to be externalized may be declared as peer dependency in your library. - -#### Cherry picking - -If you don't expect the client app of your library also needing the same dependency, you may embed cherry-picked functions. For example, to embed the `fill` function of popular library [lodash](https://lodash.com), import the `fill` function like the following: - -```js -import fill from "lodash/fill" -``` - -Even with tree-shaking, the codes being brought into your library may still be large, as the function may have its own dependencies. - -Note that `import { fill } from 'lodash'` or `import _ from 'lodash'` will not work and will embed the whole `lodash` library. - -Finally, if your client app also use `lodash` and you don't want `lodash` to be in both the client app and your libraries, even after cherry-picking, you may consider cherry-picking in component library and re-export them as utils for client to consume, so that the client does not need to depend on `lodash`, therefore avoiding duplication. - -### Type generation - -In [tsconfig.json](tsconfig.json), the following options instructs `tsc` to emit declaration (`.d.ts` files) only, as `vite build` handles the `.js` file generation. The generated `.d.ts` files are sent to `dist/types` folder. - -```json -"compilerOptions": { - "declaration": true, - "emitDeclarationOnly": true, - "declarationDir": "./dist/types" -} -``` - -In [package.json](package.json), the line below locates the generated types for library client. - -```json -"types": "./dist/types/index.d.ts", -``` - -> In [vite.config.ts](vite.config.ts), `build.emptyOutDir` is set to `false` and `rimraf` is used instead to remove the `dist` folder before the build. This is to avoid the `dist/types` folder generated by `tsc` being deleted when running `vite build`. - -### Configuration - -#### TypeScript - -In [tsconfig.json](tsconfig.js), set the following as recommended by Vite (since esbuild is used). However, enableing this option leads to https://github.com/vitejs/vite/issues/5814. The workaround is to also enable `compilerOptions.skipLibCheck`. - -```json -"compilerOptions": { - "isolatedModules": true -} -``` - -In [tsconfig.json](tsconfig.js), set the following to address [Issue #32](https://github.com/wuruoyun/vue-component-lib-starter/issues/32). The solution is from https://github.com/johnsoncodehk/volar/discussions/592. - -```json -"compilerOptions": { - "types": [ - "vite/client" - ] -} -``` - -## v4 Breaking Changes (from v3) - -### 1. Vuetify 4 CSS Reset Removed - -Vuetify 4 removed the aggressive global CSS reset that v3 included (universal `margin: 0; padding: 0`, list/heading/input resets, etc.). If your app relied on these defaults, opt in to the supplemental reset: - -```ts -import "@effect-app/vue-components/reset.css" -``` - -### 2. Nested union `_tag` handling - -`S.TaggedStruct` produces a bare `Literal` AST node, while legacy `S.Struct({ _tag: S.Literal("X") })` produces `Union([Literal("X")])` after `AST.toType`. Both patterns now correctly produce `"select"` metadata thanks to the `unwrapSingleLiteralUnion` helper. A `console.warn` is emitted for the legacy pattern to encourage migration to `TaggedStruct`. - -### 3. `UndefinedOr` defaults include the key with explicit `undefined` - -- **v3**: `defaultsValueFromSchema` skipped keys where the recursive call returned `undefined`, so `UndefinedOr` fields were omitted from the result object entirely. -- **v4**: The key is present in the result with an explicit `undefined` value. - -### 4. `S.optionalKey(X).pipe(S.withDecodingDefault(...))` support - -- **v3**: Not supported. The v3 equivalent `S.optionalWith` encoded defaults inside a `PropertySignatureTransformation`, opaque to AST inspection. -- **v4**: `defaultsValueFromSchema` detects `PropertySignatureTransformation` and extracts defaults, enabling `withDecodingDefault` as a new pattern for declaring field defaults directly on the schema. - -### On Submit event - -The :on-submit event could be tricky in `` component. -This is a prop that is basically a map of Tanstack Form `onSubmit` option and accept a function that return a Promise. If you want to use it as an event, you have to manage the state of loading yourself with `@submit` with a function returning `void` - -E.g. - -to use it as a Promise based Tanstack way: - -```html - -``` - -to use it as an event: - -```html - -``` - -this will give you a type error instead, because `:is-loading` is only accepted in event mode - -```html - -``` diff --git a/packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts b/packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts deleted file mode 100644 index 2e88b1f55..000000000 --- a/packages/vue-components/__tests__/FixedNuxtErrorBoundary.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { mount } from "@vue/test-utils" -import { CauseException } from "effect-app/client" -import * as Cause from "effect/Cause" -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" -import { defineComponent, h, nextTick, onErrorCaptured, ref } from "vue" -import { createMemoryHistory, createRouter, type Router } from "vue-router" -import FixedNuxtErrorBoundary from "../src/components/FixedNuxtErrorBoundary.vue" - -/** - * Behavior contract of the shared error boundary (extracted from the two - * app-local copies — this pins the "no behavior change" claim of the dedup): - * - a supported error (e.g. thrown in setup) renders the error slot and - * emits "error"; it is NOT reported via captureException - * - an unsupported error (e.g. native event handler) is reported via - * captureException (+ toast when debug) and does NOT take over the page - * - an interrupts-only Effect CauseException is ignored entirely - * - a route change clears the error state - * - scheduleAfterReady defers handling (backend hydration path) - * - enabled=false registers no handler, so errors propagate to the parent - */ - -const noopWarn = () => {} - -// Throws in setup only while `active` is true — a supported error ("setup -// function"), and togglable so the route-change test can stop re-throwing. -const makeThrowingChild = (active: { value: boolean }, err: unknown) => - defineComponent({ - name: "ThrowingChild", - setup() { - if (active.value) throw err - return () => h("div", { id: "child-ok" }, "ok") - } - }) - -// Throws from a native click handler — an unsupported error info. -const ClickThrowChild = defineComponent({ - name: "ClickThrowChild", - setup() { - return () => - h("button", { - id: "boom", - onClick: () => { - throw new Error("click boom") - } - }, "boom") - } -}) - -const makeRouter = () => - createRouter({ - history: createMemoryHistory(), - routes: [ - { path: "/", component: { render: () => null } }, - { path: "/other", component: { render: () => null } } - ] - }) - -describe("FixedNuxtErrorBoundary", () => { - let captureException: ReturnType - let toastError: ReturnType - let router: Router - let warnSpy: ReturnType - - beforeEach(() => { - captureException = vi.fn() - toastError = vi.fn() - router = makeRouter() - warnSpy = vi.spyOn(console, "warn").mockImplementation(noopWarn) - }) - - afterEach(() => { - warnSpy.mockRestore() - }) - - // Mount through a host component that renders the boundary with its slots, - // mirroring real usage (Suspender wraps it). Mounting the boundary directly - // as the test root breaks error propagation into its own onErrorCaptured. - const mountBoundary = ( - child: ReturnType, - props: Record = {} - ) => { - const emitted: Error[] = [] - const Host = defineComponent({ - name: "Host", - setup() { - return () => - h(FixedNuxtErrorBoundary, { - captureException, - toastError, - debug: true, - onError: (e: Error) => emitted.push(e), - ...props - }, { - default: () => h(child), - error: (params: { error: Error; clearError: () => void }) => - h("div", { id: "error-slot" }, params.error.message) - }) - } - }) - const wrapper = mount(Host, { global: { plugins: [router] } }) - return { wrapper, emitted } - } - - it("renders the error slot and emits on a supported (setup) error, without reporting it", async () => { - const active = ref(true) - const { emitted, wrapper } = mountBoundary(makeThrowingChild(active, new Error("setup boom"))) - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(true) - expect(wrapper.find("#error-slot").text()).toBe("setup boom") - expect(emitted).toHaveLength(1) - expect(captureException).not.toHaveBeenCalled() - expect(warnSpy).toHaveBeenCalled() - }) - - it("reports an unsupported (native handler) error via captureException + toast, without taking over the page", async () => { - const { emitted, wrapper } = mountBoundary(ClickThrowChild) - await wrapper.find("#boom").trigger("click") - await nextTick() - - expect(captureException).toHaveBeenCalledTimes(1) - expect(captureException.mock.calls[0][0]).toBeInstanceOf(Error) - expect(toastError).toHaveBeenCalledWith("An unexpected error has occurred: Error: click boom") - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(emitted).toHaveLength(0) - }) - - it("does not toast an unsupported error when debug is off", async () => { - const { wrapper } = mountBoundary(ClickThrowChild, { debug: false }) - await wrapper.find("#boom").trigger("click") - - expect(captureException).toHaveBeenCalledTimes(1) - expect(toastError).not.toHaveBeenCalled() - }) - - it("ignores an interrupts-only Effect CauseException", async () => { - const interrupted = new CauseException(Cause.interrupt(), "Interrupted") - const { emitted, wrapper } = mountBoundary(makeThrowingChild(ref(true), interrupted)) - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(emitted).toHaveLength(0) - expect(captureException).not.toHaveBeenCalled() - }) - - it("clears the error on route change", async () => { - const active = ref(true) - const { wrapper } = mountBoundary(makeThrowingChild(active, new Error("boom"))) - await nextTick() - expect(wrapper.find("#error-slot").exists()).toBe(true) - - active.value = false - await router.push("/other") - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(wrapper.find("#child-ok").exists()).toBe(true) - }) - - it("defers handling through scheduleAfterReady when provided", async () => { - const queue: (() => void)[] = [] - const { wrapper } = mountBoundary(makeThrowingChild(ref(true), new Error("deferred boom")), { - scheduleAfterReady: (fn: () => void) => queue.push(fn) - }) - await nextTick() - - expect(wrapper.find("#error-slot").exists()).toBe(false) - expect(queue).toHaveLength(1) - - queue.forEach((fn) => fn()) - await nextTick() - expect(wrapper.find("#error-slot").text()).toBe("deferred boom") - }) - - it("calls onHandled after emitting, before rendering the error slot", async () => { - const onHandled = vi.fn() - const { wrapper } = mountBoundary(makeThrowingChild(ref(true), new Error("hooked boom")), { onHandled }) - await nextTick() - - expect(onHandled).toHaveBeenCalledTimes(1) - expect(onHandled.mock.calls[0][0]).toBeInstanceOf(Error) - expect(onHandled.mock.calls[0][2]).toBe("setup function") - expect(wrapper.find("#error-slot").exists()).toBe(true) - }) - - it("registers no handler when enabled is false, letting the error propagate", async () => { - const parentCaught = vi.fn() - const Host = defineComponent({ - name: "Host", - setup() { - onErrorCaptured((err) => { - parentCaught(err) - return false - }) - return () => - h(FixedNuxtErrorBoundary, { - captureException, - toastError, - debug: true, - enabled: false - }, { - default: () => h(makeThrowingChild(ref(true), new Error("escaped boom"))), - error: () => h("div", { id: "error-slot" }) - }) - } - }) - const wrapper = mount(Host, { global: { plugins: [router] } }) - await nextTick() - - expect(parentCaught).toHaveBeenCalledTimes(1) - expect(wrapper.find("#error-slot").exists()).toBe(false) - }) -}) diff --git a/packages/vue-components/__tests__/IntegerValidation.test.ts b/packages/vue-components/__tests__/IntegerValidation.test.ts deleted file mode 100644 index 8ed3d9935..000000000 --- a/packages/vue-components/__tests__/IntegerValidation.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema, type NumberFieldMeta, toLocalizedStandardSchemaV1 } from "../src/components/OmegaForm" - -const germanTranslations: Record = { - "validation.integer.expected": "Es wird eine ganze Zahl erwartet, tatsächlich: {actualValue}", - "validation.number.expected": "Es wird eine Zahl erwartet, tatsächlich: {actualValue}", - "validation.empty": "Das Feld darf nicht leer sein" -} - -const germanTrans = (id: string, values?: Record) => { - let text = germanTranslations[id] || id - if (values) { - for (const [k, v] of Object.entries(values)) text = text.replace(`{${k}}`, String(v)) - } - return text -} - -describe("Integer validation with German translations (form-level localized schema)", () => { - it("should generate number metadata with int refinement for S.Int fields", () => { - const TestSchema = S.Struct({ value: S.Int }) - const { meta } = generateMetaFromSchema(TestSchema) - expect(meta.value?.type).toBe("number") - expect((meta.value as NumberFieldMeta).refinement).toBe("int") - }) - - it("shows German error for decimal value at form-level validation", async () => { - const standardSchema = toLocalizedStandardSchemaV1(S.Struct({ value: S.Int }) as any, germanTrans) - const result = await standardSchema["~standard"].validate({ value: 59.5 }) - expect(result.issues).toBeDefined() - const issue = result.issues?.find((i: any) => i.path?.[0] === "value") - expect(issue?.message).toContain("ganze Zahl") - }) - - it("shows German empty message for undefined required int at form-level validation", async () => { - const standardSchema = toLocalizedStandardSchemaV1(S.Struct({ value: S.Int }) as any, germanTrans) - const result = await standardSchema["~standard"].validate({ value: undefined }) - expect(result.issues).toBeDefined() - const issue = result.issues?.find((i: any) => i.path?.[0] === "value") - expect(issue?.message).toBe("Das Feld darf nicht leer sein") - }) - - it("accepts valid integer at form-level validation", async () => { - const standardSchema = toLocalizedStandardSchemaV1(S.Struct({ value: S.Int }) as any, germanTrans) - const result = await standardSchema["~standard"].validate({ value: 59 }) - expect(result.issues).toBeUndefined() - expect("value" in result && (result as any).value).toEqual({ value: 59 }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm.test.ts b/packages/vue-components/__tests__/OmegaForm.test.ts deleted file mode 100644 index cc5925957..000000000 --- a/packages/vue-components/__tests__/OmegaForm.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { NonEmptyString255 } from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema, type MetaRecord } from "../src/components/OmegaForm" -import OmegaForm from "./OmegaFormTest.vue" -import OmegaIntlProvider from "./OmegaIntlProvider.vue" - -class kkk extends S.Class("kkk")({ - lll: S.String.pipe(S.check(S.isMinLength(10)), S.check(S.isMaxLength(21))), - mmm: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))) -}) {} - -class bbb extends S.Class("bbb")({ - ccc: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(2))), - ddd: S.NullOr(S.String.pipe(S.check(S.isMaxLength(3)))), - jjj: S.UndefinedOr(S.String.pipe(S.check(S.isMaxLength(4)))), - nnn: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - kkk, - ooo: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(23))) -}) {} - -describe("OmegaForm", () => { - describe("addRecursiveAnnotations", () => { - it("should properly extract metadata from complex schema", () => { - const testSchema = S.Struct({ - aaa: NonEmptyString255, - bbb: S.NullOr(bbb), - eee: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - fff: S.Union([S.Literal("left"), S.Literal("right"), S.Literal("both")]), - zzz: S.NullOr(S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 })))).pipe( - S.annotate({ - message: () => "foobar" - }) - ) - }) - - const expectedMeta = { - aaa: { - maxLength: 255, - minLength: 1, - nullableOrUndefined: false, - required: true, - type: "string" - }, - "bbb.ccc": { - maxLength: 2, - minLength: 1, - nullableOrUndefined: false, - required: true, - type: "string" - }, - "bbb.ddd": { - maxLength: 3, - nullableOrUndefined: "null", - required: false, - type: "string" - }, - "bbb.jjj": { - maxLength: 4, - nullableOrUndefined: "undefined", - required: false, - type: "string" - }, - "bbb.kkk.lll": { - maxLength: 21, - minLength: 10, - nullableOrUndefined: false, - required: true, - type: "string" - }, - "bbb.kkk.mmm": { - maximum: 20, - minimum: 10, - nullableOrUndefined: false, - required: true, - type: "number" - }, - "bbb.nnn": { - maximum: 20, - minimum: 10, - nullableOrUndefined: false, - required: true, - type: "number" - }, - "bbb.ooo": { - maxLength: 23, - minLength: 1, - nullableOrUndefined: false, - required: true, - type: "string" - }, - eee: { - maximum: 20, - minimum: 10, - nullableOrUndefined: false, - required: true, - type: "number" - }, - fff: { - members: ["left", "right", "both"], - - nullableOrUndefined: false, - required: true, - type: "select" - }, - zzz: { - maximum: 20, - minimum: 10, - nullableOrUndefined: "null", - required: false, - type: "number" - } - } - - const result = generateMetaFromSchema(testSchema) - - expect(result).toHaveProperty("schema") - expect(result).toHaveProperty("meta") - - expect(result.schema).toBe(testSchema) - - expect(result.meta).toEqual(expectedMeta) - }) - }) - - describe("generateMetaFromSchema", () => { - it("should generate meta from schema", () => { - const testSchema = S.Struct({ - aaa: S.String, - bbb: S.NullOr( - S.Struct({ - ccc: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(2))), - ddd: S.NullOr(S.String.pipe(S.check(S.isMaxLength(3)))), - jjj: S.UndefinedOr(S.String.pipe(S.check(S.isMaxLength(4)))), - nnn: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - kkk: S.Struct({ - lll: S.String.pipe(S.check(S.isMinLength(10)), S.check(S.isMaxLength(21))), - mmm: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))) - }), - ooo: S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(23))) - }) - ), - eee: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))), - fff: S.Union([S.Literal("left"), S.Literal("right"), S.Literal("both")]), - zzz: S.NullOr(S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 })))) - }) - - const result = generateMetaFromSchema(testSchema) - - // Type check: ensure the meta record has the correct keys - type TestType = typeof testSchema extends S.Codec ? T : never - const meta: MetaRecord = result.meta - - // Value check - expect(meta["aaa"]).toBeDefined() - expect(meta["bbb.ccc"]).toBeDefined() - expect(meta["bbb.kkk.lll"]).toBeDefined() - expect(meta["eee"]).toBeDefined() - expect(meta["fff"]).toBeDefined() - expect(result.schema).toBe(testSchema) - }) - }) -}) - -// Create a wrapper component that includes the OmegaIntlProvider -const OmegaFormWithProvider = { - components: { OmegaForm, OmegaIntlProvider }, - template: ` - - - - ` -} - -describe("OmegaForm UI", () => { - it("renders", () => { - const wrapper = mount(OmegaFormWithProvider) - expect(wrapper.exists()).toBe(true) - }) - - it("should display the sum of first and second inputs", async () => { - const wrapper = mount(OmegaFormWithProvider) - - // Find the input elements - const firstInput = wrapper.find("input[id=\"first\"]") - const secondInput = wrapper.find("input[id=\"second\"]") - - console.log(firstInput) - - // Set values - await firstInput.setValue(10) - await secondInput.setValue(20) - - // Trigger change events - await firstInput.trigger("change") - await secondInput.trigger("change") - - // Get the element showing the sum - const sumElement = wrapper.find("[data-testid=\"valuez\"]") - - // Assert the sum is correct (10 + 20 = 30) - expect(sumElement.text()).toBe("30") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts b/packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts deleted file mode 100644 index c893d391e..000000000 --- a/packages/vue-components/__tests__/OmegaForm/CustomFilterFieldSchema.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { toLocalizedStandardSchemaV1 } from "../../src/components/OmegaForm" - -const identityTrans = (id: string, values?: Record) => { - if (!values) return id - return Object.entries(values).reduce( - (acc, [k, v]) => acc.replaceAll(`{${k}}`, String(v)), - id - ) -} - -const maxLineLengthCheck = (max: number) => - S.makeFilter((value: string) => { - const tooLong = value.split("\n").find((line) => line.length > max) - return tooLong !== undefined ? `Line "${tooLong}" exceeds ${max} chars` : undefined - }) - -describe("OmegaForm form-level schema custom checks", () => { - it("includes custom filter checks in the form-level localized schema", async () => { - const schema = S.Struct({ - height: S.NonEmptyString100.pipe( - S.check(S.isMinLength(10)), - S.check(maxLineLengthCheck(20)) - ) - }) - - const standardSchema = toLocalizedStandardSchemaV1(schema as any, identityTrans) - const invalid = "1234567890\n123456789012345678901" - const result = await standardSchema["~standard"].validate({ height: invalid }) - - expect(result.issues).toBeDefined() - const issue = result.issues?.find((i: any) => i.path?.[0] === "height") - expect(issue).toBeDefined() - expect(issue?.message).toContain("exceeds 20 chars") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts b/packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts deleted file mode 100644 index 9d794a84d..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DateValidation.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("Date field validation", () => { - it("accepts a raw YYYY-MM-DD string and decodes it to Date on submit", async () => { - let submittedValue: { date: Date } | null = null - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - date: S.DateFromString - }), - { - onSubmit: async ({ value }) => { - submittedValue = value - } - } - ) - - return { form } - } - }) - - await wrapper.find("[data-testid='date-input']").setValue("2024-06-01") - await wrapper.find("[data-testid='submit']").trigger("click") - - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - expect(submittedValue?.date).toBeInstanceOf(Date) - expect(submittedValue?.date.toISOString()).toContain("2024-06-01") - expect(wrapper.find("[data-testid='errors']").text()).toBe("[]") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts b/packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts deleted file mode 100644 index 9b2d7045c..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DecimalSeparatorInput.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { describe, expect, it } from "vitest" -import { handleDecimalSeparatorBeforeinput } from "../../src/components/OmegaForm/decimalSeparatorInput" - -// Simulates typing into a VNumberInput: the handler is attached in capture -// phase on the component root, the native input is the event target. -const setup = ( - value: string, - separator: string, - cursor?: { start: number; end: number } -) => { - const root = document.createElement("div") - const input = document.createElement("input") - root.appendChild(input) - document.body.appendChild(root) - input.value = value - const start = cursor?.start ?? value.length - const end = cursor?.end ?? value.length - input.setSelectionRange(start, end) - - let vuetifySawBeforeinput = false - let inputEventFired = false - input.addEventListener("beforeinput", () => { - vuetifySawBeforeinput = true - }) - input.addEventListener("input", () => { - inputEventFired = true - }) - root.addEventListener( - "beforeinput", - (e) => handleDecimalSeparatorBeforeinput(e, separator), - { capture: true } - ) - - const type = (data: string) => { - const e = new InputEvent("beforeinput", { - data, - inputType: "insertText", - bubbles: true, - cancelable: true - }) - input.dispatchEvent(e) - return e - } - - return { - input, - type, - sawVuetifyHandler: () => vuetifySawBeforeinput, - sawInputEvent: () => inputEventFired - } -} - -describe("handleDecimalSeparatorBeforeinput", () => { - it("translates '.' into ',' when the active separator is ','", () => { - const t = setup("1", ",") - const e = t.type(".") - expect(e.defaultPrevented).toBe(true) - expect(t.sawVuetifyHandler()).toBe(false) - expect(t.input.value).toBe("1,") - expect(t.sawInputEvent()).toBe(true) - }) - - it("translates ',' into '.' when the active separator is '.'", () => { - const t = setup("1", ".") - t.type(",") - expect(t.input.value).toBe("1.") - }) - - it("translates both '.' and ',' when the active separator is a custom one", () => { - const dot = setup("1", "٫") - dot.type(".") - expect(dot.input.value).toBe("1٫") - - const comma = setup("2", "٫") - comma.type(",") - expect(comma.input.value).toBe("2٫") - }) - - it("leaves the event alone when the typed char is the active separator", () => { - const t = setup("1", ",") - const e = t.type(",") - expect(e.defaultPrevented).toBe(false) - expect(t.sawVuetifyHandler()).toBe(true) - expect(t.input.value).toBe("1") - }) - - it("leaves plain digits alone", () => { - const t = setup("1", ",") - const e = t.type("5") - expect(e.defaultPrevented).toBe(false) - expect(t.sawVuetifyHandler()).toBe(true) - }) - - it("swallows the wrong separator when the value already has one", () => { - const t = setup("1,5", ",") - const e = t.type(".") - expect(e.defaultPrevented).toBe(true) - expect(t.input.value).toBe("1,5") - expect(t.sawInputEvent()).toBe(false) - }) - - it("inserts at the cursor position and replaces the selection", () => { - const t = setup("15", ",", { start: 1, end: 1 }) - t.type(".") - expect(t.input.value).toBe("1,5") - expect(t.input.selectionStart).toBe(2) - expect(t.input.selectionEnd).toBe(2) - }) - - it("normalizes the wrong separator inside pasted data", () => { - const t = setup("", ",") - t.type("1.5") - expect(t.input.value).toBe("1,5") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts b/packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts deleted file mode 100644 index 3abd2812c..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DecodeTransformFallback.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import * as SchemaGetter from "effect/SchemaGetter" -import { describe, expect, it } from "vitest" -import { createUseFormWithCustomInput, generateMetaFromSchema, type NumberFieldMeta, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const CustomNumberInput = { - template: ` -
- - {{ inputProps.errorMessages.join(', ') }} -
- `, - props: ["inputProps", "field"], - inheritAttrs: false -} - -describe("OmegaForm decodeTo fallback transform", () => { - const inputSchema = S.Struct({ amount: S.PositiveInt }) - - const transformedSchema = S.Struct({ amount: S.NonNegativeInt }).pipe( - S.decodeTo(inputSchema, { - decode: SchemaGetter.transform((input: { amount: number }) => input.amount === 0 ? { amount: 666 } : input), - encode: SchemaGetter.passthrough({ strict: false }) - }) - ) - - const createWrapper = (defaultValues: { amount: number }) => { - let submittedValue: any = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(transformedSchema, { - defaultValues, - onSubmit: async ({ value }) => { - submittedValue = value - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }) - - return wrapper - } - - it("should accept 0 at the input boundary and pass 666 to onSubmit via decode", async () => { - const wrapper = createWrapper({ amount: 0 }) - await wrapper.vm.$nextTick() - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ amount: 666 }) - }) - - it("should pass through a positive amount unchanged", async () => { - const wrapper = createWrapper({ amount: 5 }) - await wrapper.vm.$nextTick() - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ amount: 5 }) - }) - - it("meta extraction should reflect the outer (source) schema, not the inner decoded one", () => { - const { meta } = generateMetaFromSchema(transformedSchema) - const amount = meta.amount as NumberFieldMeta | undefined - - expect(amount).toBeDefined() - expect(amount!.type).toBe("number") - expect(amount!.refinement).toBe("int") - // NonNegativeInt ⇒ minimum: 0 (not exclusiveMinimum: 0 from PositiveInt) - expect(amount!.minimum).toBe(0) - expect(amount!.exclusiveMinimum).toBeUndefined() - }) - - it("should allow 0 typed into form.Input and submit with 666 via decode", async () => { - const useForm = createUseFormWithCustomInput(CustomNumberInput) - let submittedValue: any = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useForm(transformedSchema, { - defaultValues: { amount: 1 }, - onSubmit: async ({ value }) => { - submittedValue = value - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }, { - global: { stubs: { CustomNumberInput } } - }) - - await wrapper.vm.$nextTick() - - const input = wrapper.find("[data-testid=\"custom-input-field\"]") - await input.setValue("0") - await wrapper.vm.$nextTick() - - expect(wrapper.find("[data-testid=\"custom-input-error\"]").exists()).toBe(false) - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ amount: 666 }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts b/packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts deleted file mode 100644 index cce2d6ff4..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DefaultValuesPriority.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// __tests__/OmegaForm/DefaultValuesPriority.test.ts -import { describe, expect, it } from "vitest" -import { deepMerge } from "../../src/components/OmegaForm" - -describe("deepMerge", () => { - it("treats arrays as values, not as merged structures", () => { - expect(deepMerge({ xs: [1, 2, 3] }, { xs: [9] })).toEqual({ xs: [9] }) - }) - - it("recursively merges objects", () => { - expect(deepMerge({ a: { b: 1, c: 2 } }, { a: { c: 9, d: 3 } })) - .toEqual({ a: { b: 1, c: 9, d: 3 } }) - }) - - it("source wins for primitives", () => { - expect(deepMerge({ x: 1 }, { x: 2 })).toEqual({ x: 2 }) - }) -}) - -// NOTE: defaultValuesPriority resolution lives inside useOmegaForm's -// `defaultValues` computed and isn't exported. A behavioral test would mount -// a form. The deepMerge unit tests above pin the merge mechanic; the priority -// resolution itself is exercised indirectly by the Meta and Defaults stories. diff --git a/packages/vue-components/__tests__/OmegaForm/Defaults.test.ts b/packages/vue-components/__tests__/OmegaForm/Defaults.test.ts deleted file mode 100644 index 320d85dcd..000000000 --- a/packages/vue-components/__tests__/OmegaForm/Defaults.test.ts +++ /dev/null @@ -1,551 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("OmegaForm Defaults", () => { - // Setup the same schemas as in Defaults.vue story - const struct = { - a: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - b: S.NonEmptyString, - c: S.NonEmptyArray(S.String).pipe( - S.withConstructorDefault(Effect.sync(() => [S.NonEmptyString("C"), S.NonEmptyString("Non Empty Array")])) - ), - d: S - .NonEmptyArray(S.Struct({ - e: S.NonEmptyString - })) - .pipe( - S.withConstructorDefault(Effect.sync(() => [{ e: S.NonEmptyString("default") }])) - ), - f: S.Union([ - S.Struct({ - _tag: S.Literal("taggo1").pipe(S.withConstructorDefault(Effect.succeed("taggo1"))), - g: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - i: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))) - }), - S.Struct({ - _tag: S.Literal("taggo2").pipe(S.withConstructorDefault(Effect.succeed("taggo2"))), - h: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - i: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))) - }) - ]), - j: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))), - k: S.Boolean.pipe(S.withConstructorDefault(Effect.succeed(true))), - l: S.NullOr( - S.Union([ - S.Struct({ - a: S.NonEmptyString255, - common: S.NonEmptyString255, - _tag: S.Literal("A") - }), - S.Struct({ - b: S.NonEmptyString255, - common: S.NonEmptyString255, - _tag: S.Literal("B") - }) - ]) - ), - m: S.Struct({ - n: S.NullOr(S.Struct({ q: S.String })), - o: S.UndefinedOr(S.Struct({ q: S.String })) - }), - p: S.NullOr(S.Struct({ z: S.String })), - q: S.UndefinedOr(S.Struct({ z: S.String })), - r: S - .NullOr(S.Struct({ - p: S.NullOr(S.Struct({ z: S.String })), - r: S.UndefinedOr(S.Struct({ z: S.String })) - })) - .withConstructorDefault, - s: S.NullOr(S.Struct({ z: S.String })).withConstructorDefault, - t: S.FiniteFromString.pipe(S.withConstructorDefault(Effect.succeed(1000))), - u: S.NullOr(S.NonEmptyString), - v: S.UndefinedOr(S.NonEmptyString) - } - - class ClassSchema extends S.Class("ClassSchema")(struct) {} - const schema = S.Struct(struct) - - const Union = S.Union([ - S.Struct({ - _tag: S.Literal("tag1").pipe(S.withConstructorDefault(Effect.succeed("tag1"))), - a: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - b: schema - }), - S.Struct({ - _tag: S.Literal("tag2"), - a: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - b: S.NonEmptyString.pipe(S.withConstructorDefault(Effect.succeed(S.NonEmptyString("default")))), - c: schema - }) - ]) - - it("should have correct default values for form zero (ClassSchema)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(ClassSchema) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form one (schema)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form two (Union)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(Union) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "_tag": "tag1", - "a": "default", - "b": "default", - "c": { - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - } - }) - }) - - it("should not initialize _tag for a root union without explicit defaults", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(S.Union([ - S.Struct({ - _tag: S.Literal("one"), - a: S.Struct({ z: S.NonEmptyString100 }) - }), - S.Struct({ - _tag: S.Literal("two"), - a: S.Struct({ y: S.NonNegativeInt }) - }) - ])) - - return { form } - } - }) - - await wrapper.vm.$nextTick() - - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(JSON.parse(valuesText)).not.toHaveProperty("_tag") - }) - - it("should have correct default values for form three (with defaultValues merged)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "aaaaah", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form four (defaultFromSchema: only)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }, { - defaultValuesPriority: ["schema"] - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form five (defaultFromSchema: nope)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }, { - defaultValuesPriority: ["tanstack"] - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "aaaaah" - }) - }) - - it("should have correct default values for form six (defaultFromSchema: merge)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - a: "aaaaah" - } - }, { - defaultValuesPriority: ["tanstack", "schema"] - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "aaaaah", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form seven (Union with different structure)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(S.Union([ - S.Struct({ - _tag: S.Literal("tag1").pipe(S.withConstructorDefault(Effect.succeed("tag1"))), - a: S.NonEmptyString, - s: S.NullOr(S.Finite).withConstructorDefault - }), - S.Struct({ - _tag: S.Literal("tag2"), - b: S.NonEmptyString, - t: S.Finite - }) - ])) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "_tag": "tag1", - "a": "", - "s": null, - "b": "" - }) - }) - - it("should have correct default values for form eight (ClassSchema with filter)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(ClassSchema - .pipe( - S.check(S.makeFilter((form) => { - if (form.a !== form.b) { - return { - path: ["a"], - message: "Email and confirmation must match!" - } - } - })) - )) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) - - it("should have correct default values for form nine (ClassSchema with filter inline)", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(ClassSchema.pipe(S.check(S.makeFilter((form) => { - if (form.a !== form.b) { - return { - path: ["a"], - message: "Email and confirmation must match!" - } - } - })))) - return { form } - } - }) - - await wrapper.vm.$nextTick() - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - const values = JSON.parse(valuesText) - - expect(values).toEqual({ - "a": "default", - "b": "", - "c": ["C", "Non Empty Array"], - "d": [{ "e": "default" }], - "f": { "_tag": "taggo1", "g": "default", "i": "default", "h": "default" }, - "j": 0, - "k": true, - "l": null, - "m": { "n": null }, - "p": null, - "r": null, - "s": null, - "t": 1000, - "u": null - }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts b/packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts deleted file mode 100644 index dec4f4981..000000000 --- a/packages/vue-components/__tests__/OmegaForm/Defaults.values.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -// __tests__/OmegaForm/Defaults.values.test.ts -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defaultsValueFromSchema } from "../../src/components/OmegaForm" -import { fillNestedDefaults } from "../../src/components/OmegaForm/meta/defaults" - -describe("defaultsValueFromSchema", () => { - it("extracts withConstructorDefault values", () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("Bob"))) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ name: "Bob" }) - }) - - it("extracts withDecodingDefault on optionalKey", () => { - const schema = S.Struct({ - flag: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("on"))) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ flag: "on" }) - }) - - it("returns null for NullOr fields without explicit default", () => { - const schema = S.Struct({ - x: S.NullOr(S.String) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: null }) - }) - - it("returns undefined for UndefinedOr fields without explicit default", () => { - const schema = S.Struct({ - x: S.UndefinedOr(S.Finite) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: undefined }) - }) - - it("returns empty string for plain S.String at the leaf", () => { - const schema = S.Struct({ x: S.String }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: "" }) - }) - - it("returns false for plain S.Boolean at the leaf", () => { - const schema = S.Struct({ x: S.Boolean }) - expect(defaultsValueFromSchema(schema)).toEqual({ x: false }) - }) - - it("preserves values passed via the record argument", () => { - const schema = S.Struct({ x: S.String, y: S.String }) - expect(defaultsValueFromSchema(schema, { x: "preset" })).toEqual({ x: "preset", y: "" }) - }) - - it("merges fields across union members, picking explicit defaults", () => { - const schema = S.Union([ - S.TaggedStruct("A", { v: S.String.pipe(S.withConstructorDefault(Effect.succeed("a-default"))) }), - S.TaggedStruct("B", { v: S.String, extra: S.String }) - ]) - expect(defaultsValueFromSchema(schema)).toEqual({ _tag: "A", v: "a-default", extra: "" }) - }) -}) - -describe("fillNestedDefaults", () => { - it("fills missing nullable children for a materialized nullable struct", () => { - const schema = S.Struct({ - override: S.NullOr(S.Struct({ - min: S.NullOr(S.NonNegativeNumber), - max: S.NullOr(S.NonNegativeNumber) - })) - }) - - expect(fillNestedDefaults(schema.ast, { override: { min: 100 } })).toEqual({ - override: { - min: 100, - max: null - } - }) - }) - - it("does not add fields from another tagged union branch", () => { - const schema = S.Union([ - S.TaggedStruct("A", { a: S.NullOr(S.String), common: S.String }), - S.TaggedStruct("B", { b: S.Number, nullableB: S.NullOr(S.Number) }) - ]) - - expect(fillNestedDefaults(schema.ast, { _tag: "B", b: 1 })).toEqual({ - _tag: "B", - b: 1 - }) - }) - - it("fills missing nullable children for materialized struct elements of an array", () => { - const schema = S.Struct({ - items: S.Array(S.NullOr(S.Struct({ - a: S.NullOr(S.String), - b: S.NullOr(S.String) - }))) - }) - - expect(fillNestedDefaults(schema.ast, { items: [{ a: "x" }, null] })).toEqual({ - items: [{ a: "x", b: null }, null] - }) - }) - - it("returns the same reference when there is nothing to fill", () => { - const schema = S.Struct({ - items: S.Array(S.NullOr(S.Struct({ - a: S.NullOr(S.String), - b: S.NullOr(S.String) - }))) - }) - - const value = { items: [{ a: "x", b: null }, null] } - expect(fillNestedDefaults(schema.ast, value)).toBe(value) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts b/packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts deleted file mode 100644 index 234848dc5..000000000 --- a/packages/vue-components/__tests__/OmegaForm/DialogBlockingI18n.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { mount } from "@vue/test-utils" -import { describe, expect, it, vi } from "vitest" -import { defineComponent, h, ref } from "vue" -import { provideBus, usePreventClose } from "../../src/components/OmegaForm/blockDialog" -import { useIntlKey } from "../../src/utils" - -// Regression: when the dialog-closing handler ran with a dirty form, the -// previous fix destructured `formatMessage` from `useIntl()`. The storybook -// mocks shipped a shape without a top-level `formatMessage`, so the -// destructured value was `undefined` and triggered "formatMessage is not a -// function". This test pins the production wiring: as long as `useIntl()` -// exposes `formatMessage` directly (real shape from `@effect-app/vue`), -// the confirm message is the formatted string. -describe("usePreventClose i18n", () => { - it("calls window.confirm with the formatted message when dirty and dialog is closing", () => { - const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(true) - const formatMessage = vi.fn( - (descriptor: { id: string; defaultMessage?: string }) => `de:${descriptor.id}` - ) - - let bus: ReturnType | undefined - const Outer = defineComponent({ - setup() { - bus = provideBus() - return () => h(Inner) - } - }) - const Inner = defineComponent({ - setup() { - usePreventClose(() => ref(true)) - return () => h("div") - } - }) - - mount(Outer, { - global: { - provide: { - [useIntlKey as unknown as symbol]: () => ({ - trans: (id: string) => id, - formatMessage - }) - } - } - }) - - bus!.emit("dialog-closing", {}) - - expect(formatMessage).toHaveBeenCalledTimes(1) - expect(formatMessage.mock.calls[0]?.[0]).toMatchObject({ id: "form.unsaved_changes_confirm" }) - expect(confirmSpy).toHaveBeenCalledWith("de:form.unsaved_changes_confirm") - - confirmSpy.mockRestore() - }) - - it("skips the confirm entirely when the form is not dirty", () => { - const confirmSpy = vi.spyOn(window, "confirm") - - let bus: ReturnType | undefined - const Outer = defineComponent({ - setup() { - bus = provideBus() - return () => h(Inner) - } - }) - const Inner = defineComponent({ - setup() { - usePreventClose(() => ref(false)) - return () => h("div") - } - }) - - mount(Outer) - - const evt: { prevent?: boolean | Promise } = {} - bus!.emit("dialog-closing", evt) - - expect(confirmSpy).not.toHaveBeenCalled() - expect(evt.prevent).toBeUndefined() - - confirmSpy.mockRestore() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts b/packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts deleted file mode 100644 index ff1a800bf..000000000 --- a/packages/vue-components/__tests__/OmegaForm/EmailFormatDetection.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../../src/components/OmegaForm" - -describe("S.Email format detection", () => { - it("flags S.Email fields with format: 'email' on their meta", () => { - const schema = S.Struct({ x: S.Email }) - const { meta } = generateMetaFromSchema(schema) - expect((meta.x as any)?.format).toBe("email") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts b/packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts deleted file mode 100644 index be930ee75..000000000 --- a/packages/vue-components/__tests__/OmegaForm/EmptyStringValidation.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { flushPromises, mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("Empty string validation for nested NonEmptyString fields", () => { - it("demonstrates current empty string behavior", async () => { - const schema = S.Struct({ - asder2: S.Struct({ - value: S.NonEmptyString100 - }) - }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - asder2: { - value: "" - } - }, - onSubmit: async ({ value }) => { - console.log("Form submitted with:", value) - } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - - // Click submit with empty string - await wrapper.find("[data-testid='submit']").trigger("click") - await wrapper.vm.$nextTick() - await flushPromises() - - // Check for validation error - expect(wrapper.text()).toContain("validation.empty") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts b/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts deleted file mode 100644 index 33a03f7ea..000000000 --- a/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, h, nextTick } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaErrorsInternal from "../../src/components/OmegaForm/OmegaErrorsInternal.vue" - -describe("OmegaForm general errors", () => { - it("renders a string returned by an onSubmit validator and clears it after correction", async () => { - const message = "Passwörter stimmen nicht überein" - const Inner = defineComponent({ - setup() { - const form = useOmegaForm(S.Struct({ password: S.String, confirmation: S.String }), { - defaultValues: { password: "secret", confirmation: "different" }, - validators: { - onSubmit: ({ value }) => value.password === value.confirmation ? undefined : message - } - }) - return { form } - }, - template: `` - }) - const wrapper = mount(Inner) - - await wrapper.vm.form.handleSubmit() - await nextTick() - expect(wrapper.get("[role=\"alert\"]").text()).toContain(message) - - wrapper.vm.form.setFieldValue("confirmation", "secret") - await wrapper.vm.form.handleSubmit() - await nextTick() - expect(wrapper.find("[role=\"alert\"]").exists()).toBe(false) - wrapper.unmount() - }) - - it("preserves schema messages alongside strings and ignores unsupported values", () => { - const wrapper = mount(OmegaErrorsInternal, { - props: { - errors: [], - generalErrors: [ - "Form error", - { rows: [{ message: "Schema error" }, null, 42, { message: 123 }, { message: "" }] }, - null, - undefined, - false, - 42, - "", - { rows: "unsupported", other: null } - ] - }, - slots: { - default: ({ showedGeneralErrors }) => h("div", JSON.stringify(showedGeneralErrors)) - } - }) - - expect(wrapper.text()).toBe(JSON.stringify(["Form error", "Schema error"])) - wrapper.unmount() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts b/packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts deleted file mode 100644 index 5023651bd..000000000 --- a/packages/vue-components/__tests__/OmegaForm/IndexedInputErrors.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, nextTick, ref } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" - -describe("OmegaForm indexed inputs", () => { - it.each([0, 13])("renders rows[%i].text outside form.Array with one labelled error", async (index) => { - const wrapper = mount(defineComponent({ - setup() { - const form = useOmegaForm( - S.Struct({ - rows: S.Array(S.Struct({ text: S.String.pipe(S.check(S.isMinLength(2))) })) - }), - { - defaultValues: { - rows: Array.from({ length: index + 1 }, (_, i) => ({ text: i === index ? "" : "valid" })) - } - } - ) - return { form, name: `rows[${index}].text`, visible: ref(true) } - }, - template: ` - - - - - - - ` - })) - - const input = wrapper.get("input") - await wrapper.vm.form.handleSubmit() - await nextTick() - - expect(wrapper.get("[data-testid=\"field-errors\"]").text()).not.toBe("") - const entries = wrapper.findAll("[role=\"alert\"] .error-item") - expect(entries).toHaveLength(1) - expect(entries[0].get("label").text()).toBe("Row text") - expect(entries[0].get("label").attributes("for")).toBe(input.attributes("id")) - - wrapper.vm.visible = false - await nextTick() - await wrapper.vm.form.handleSubmit() - await nextTick() - - const unregistered = wrapper.findAll("[role=\"alert\"] .error-item") - expect(unregistered).toHaveLength(1) - expect(unregistered[0].get("label").attributes("for")).toBe(`rows.${index}.text`) - wrapper.unmount() - }) - - it("still resolves indexed metadata inside form.Array", () => { - const wrapper = mount(defineComponent({ - setup() { - return { - form: useOmegaForm(S.Struct({ rows: S.Array(S.Struct({ text: S.String })) }), { - defaultValues: { rows: [{ text: "existing" }] } - }) - } - }, - template: ` - - - - - - ` - })) - - expect(wrapper.get("input").element.value).toBe("existing") - wrapper.unmount() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts b/packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts deleted file mode 100644 index 6e8441959..000000000 --- a/packages/vue-components/__tests__/OmegaForm/InputAttributes.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { defineComponent } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const VTextField = defineComponent({ - props: { - modelValue: { - type: String, - default: "" - } - }, - emits: ["update:modelValue"], - template: ` - - ` -}) - -const VSelect = defineComponent({ - props: { - items: { - type: Array, - default: () => [] - }, - modelValue: { - type: String, - default: "" - } - }, - emits: ["update:modelValue"], - template: ` - - ` -}) - -describe("OmegaForm input attributes", () => { - it("does not forward the internal form object as a native form attribute", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - password: S.NonEmptyString255 - }), - { - defaultValues: { - password: "" - } - } - ) - - return { form } - } - }, { - global: { - components: { VTextField, VSelect } - } - }) - - await wrapper.vm.$nextTick() - - const input = wrapper.find("input") - const nativeForm = wrapper.find("form").element - - expect(input.attributes("form")).toBeUndefined() - expect(input.attributes("validators")).toBeUndefined() - expect(Object.values(input.attributes())).not.toContain("[object Object]") - expect((input.element as HTMLInputElement).form).toBe(nativeForm) - }) - - it("renders a newly-supported native input type (tel) on the text-field branch", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ phone: S.String }), - { defaultValues: { phone: "" } } - ) - - return { form } - } - }, { - global: { - components: { VTextField } - } - }) - - await wrapper.vm.$nextTick() - - expect(wrapper.find("input").attributes("type")).toBe("tel") - }) - - it("falls back to a text input and warns for a type no branch handles", async () => { - const warn = vi.spyOn(console, "warn").mockImplementation(() => {}) - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ mystery: S.String }), - { defaultValues: { mystery: "" } } - ) - - return { form } - } - }, { - global: { - components: { VTextField } - } - }) - - await wrapper.vm.$nextTick() - - // unhandled type → getInputType maps it to "text" - expect(wrapper.find("input").attributes("type")).toBe("text") - expect(warn).toHaveBeenCalledWith(expect.stringContaining("totally-custom")) - - warn.mockRestore() - }) - - it("keeps input configuration props off the renderer attrs", async () => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - choice: S.Literal("one") - }), - { - defaultValues: { - choice: "one" - } - } - ) - - return { form } - } - }, { - global: { - components: { VTextField, VSelect } - } - }) - - await wrapper.vm.$nextTick() - - const select = wrapper.find("select") - - expect(select.attributes("options")).toBeUndefined() - expect(select.attributes("items")).toBeUndefined() - expect(Object.values(select.attributes())).not.toContain("[object Object]") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts b/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts deleted file mode 100644 index 37b2b4996..000000000 --- a/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("OmegaForm input reactivity", () => { - it("reflects value changes from the slot `state` in the view", async () => { - const schema = S.Struct({ - name: S.String - }) - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { name: "" } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - - const input = wrapper.find("[data-testid='input']") - await input.setValue("hello") - await wrapper.vm.$nextTick() - - // The slot `state` must stay in sync with the form store - expect(wrapper.find("[data-testid='display']").text()).toBe("hello") - expect((input.element as HTMLInputElement).value).toBe("hello") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts b/packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts deleted file mode 100644 index 822fd6555..000000000 --- a/packages/vue-components/__tests__/OmegaForm/InputRegistry.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent } from "vue" -import { createUseFormWithCustomInput, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const VTextField = defineComponent({ - props: { modelValue: { type: String, default: "" } }, - emits: ["update:modelValue"], - template: `` -}) - -const RatingInput = defineComponent({ - props: { - inputProps: { type: Object, required: true }, - field: { type: Object, required: true }, - state: { type: Object, required: true } - }, - template: `
{{ inputProps.label }}
` -}) - -const UniversalInput = defineComponent({ - props: { - inputProps: { type: Object, required: true }, - field: { type: Object, required: true }, - state: { type: Object, required: true } - }, - template: `
{{ inputProps.label }}
` -}) - -const mountWith = (omegaConfig: any, type: string, name = "x") => - mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - `, - setup() { - const form = useOmegaForm(S.Struct({ x: S.String }), { defaultValues: { x: "" } }, omegaConfig) - return { form } - } - }, { global: { components: { VTextField } } }) - -describe("OmegaForm input registry", () => { - it("renders the registered component for a custom type", async () => { - const wrapper = mountWith({ inputs: { rating: RatingInput } }, "rating") - await wrapper.vm.$nextTick() - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(true) - }) - - it("overrides a built-in type when registered", async () => { - const wrapper = mountWith({ inputs: { string: RatingInput } }, "string") - await wrapper.vm.$nextTick() - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(true) - }) - - it("falls back to the built-in renderer when no entry matches", async () => { - const wrapper = mountWith({ inputs: { rating: RatingInput } }, "string") - await wrapper.vm.$nextTick() - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(false) - expect(wrapper.find("input").exists()).toBe(true) - }) -}) - -describe("createUseFormWithCustomInput + inputs", () => { - it("uses a per-type registered component, else the universal custom input", async () => { - const useForm = createUseFormWithCustomInput(UniversalInput) - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useForm( - S.Struct({ score: S.Number, nickname: S.String }), - { defaultValues: { score: 0, nickname: "" } }, - { inputs: { rating: RatingInput } } - ) - return { form } - } - }, { global: { components: { VTextField } } }) - await wrapper.vm.$nextTick() - // `score` (type="rating") resolves the registered component... - expect(wrapper.find("[data-testid=\"rating\"]").exists()).toBe(true) - // ...while `nickname` falls back to the universal custom input. - expect(wrapper.find("[data-testid=\"universal\"]").exists()).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts b/packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts deleted file mode 100644 index 00e3ba9a5..000000000 --- a/packages/vue-components/__tests__/OmegaForm/IntersectionError.test.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { flushPromises, mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { beforeEach, describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -// Mock components that integrate with TanStack Form -const mockComponents = { - CustomInput: { - template: ` -
- - - -
- `, - props: ["form", "name", "field", "label", "type", "options"], - inheritAttrs: false - }, - OmegaErrors: { - template: `
{{ JSON.stringify(errors) }}
`, - props: ["errors", "generalErrors", "showErrors"] - } -} - -describe("OmegaForm Intersection/Union", () => { - const AlphaSchema = S.Struct({ - first: S.Literal("alpha"), - alpha: S.NonEmptyString - }) - - const BetaSchema = S.Struct({ - first: S.Literal("beta"), - beta: S.NonEmptyString - }) - - const MySchema = S.Struct({ - myUnion: S.Union([AlphaSchema, BetaSchema]) - }) - - const createWrapper = () => - mount({ - components: { - OmegaIntlProvider, - CustomInput: mockComponents.CustomInput - }, - template: ` - - - - - - `, - setup() { - let submittedValue: any = null - const form = useOmegaForm(MySchema, { - onSubmit: async ({ value }) => { - submittedValue = value - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }, { - global: { - stubs: { - CustomInput: mockComponents.CustomInput, - OmegaErrorsInternal: mockComponents.OmegaErrors - } - } - }) - - let wrapper: ReturnType - - beforeEach(() => { - wrapper = createWrapper() - }) - - it("handles discriminated union with conditional fields", async () => { - // Initially, neither alpha nor beta input should be visible - expect(wrapper.find("[data-testid='alpha-input']").exists()).toBe(false) - expect(wrapper.find("[data-testid='beta-input']").exists()).toBe(false) - - // Select "alpha" from the dropdown - const selectInput = wrapper.find("select[name='myUnion.first']") - await selectInput.setValue("alpha") - await wrapper.vm.$nextTick() - - // Alpha input should now be visible, beta should not - expect(wrapper.find("[data-testid='alpha-input']").exists()).toBe(true) - expect(wrapper.find("[data-testid='beta-input']").exists()).toBe(false) - - // Switch to "beta" - await selectInput.setValue("beta") - await wrapper.vm.$nextTick() - - // Beta input should now be visible, alpha should not - expect(wrapper.find("[data-testid='alpha-input']").exists()).toBe(false) - expect(wrapper.find("[data-testid='beta-input']").exists()).toBe(true) - }) - - it("clears validation errors when switching union types", async () => { - const selectInput = wrapper.find("select[name='myUnion.first']") - - // Select "alpha" and submit without filling required field to trigger error - await selectInput.setValue("alpha") - - const submitButton = wrapper.find("[data-testid='submit']") - await submitButton.trigger("submit") - await wrapper.vm.$nextTick() - await flushPromises() - - // Verify error exists for alpha field - const errorDiv = wrapper.find(".mock-omega-errors") - expect(errorDiv.exists()).toBe(true) - expect(errorDiv.text()).toContain("alpha") - - // Switch to "beta" - error should be cleared - await selectInput.setValue("beta") - await wrapper.vm.$nextTick() - - const errorDivAfter = wrapper.find(".mock-omega-errors") - expect(errorDivAfter.text()).not.toContain("alpha") - }) - - it("clears form values when switching union types and handles submission", async () => { - const selectInput = wrapper.find("select[name='myUnion.first']") - - // Select "alpha" and fill the input - await selectInput.setValue("alpha") - await wrapper.vm.$nextTick() - - const alphaInput = wrapper.find("input[name='myUnion.alpha']") - await alphaInput.setValue("test") - await wrapper.vm.$nextTick() - - // Check that "test" is in the values - const valuesText = wrapper.find("[data-testid='values']").text() - expect(valuesText).toContain("test") - - // Switch to "beta" - await selectInput.setValue("beta") - await wrapper.vm.$nextTick() - - // // Check that alpha "test" value is not there anymore - // const valuesAfterSwitch = wrapper.find("[data-testid='values']").text() - // expect(valuesAfterSwitch).not.toContain("test") - - // Fill beta with "here we go" - const betaInput = wrapper.find("input[name='myUnion.beta']") - await betaInput.setValue("here we go") - await wrapper.vm.$nextTick() - - // Click submit and check submission works - const form = wrapper.find("form") - await form.trigger("submit") - await wrapper.vm.$nextTick() - // Wait a bit for async operations - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify the form data was submitted with the correct beta values - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ - myUnion: { - first: "beta", - beta: "here we go" - } - }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/Meta.test.ts b/packages/vue-components/__tests__/OmegaForm/Meta.test.ts deleted file mode 100644 index 0ed85b1b1..000000000 --- a/packages/vue-components/__tests__/OmegaForm/Meta.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -// __tests__/OmegaForm/Meta.test.ts -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../../src/components/OmegaForm" - -const subStruct = { - a: S.NullOr(S.String), - b: S.UndefinedOr(S.Finite), - c: S.NullishOr(S.Finite), - d: S.String, - e: S.Finite, - f: S.Boolean -} - -const schema = S.Struct({ - ...subStruct, - struct: S.Struct(subStruct), - nullableStruct: S.NullOr(S.Struct(subStruct)) -}) - -const expectedSubMeta = { - a: { required: false, nullableOrUndefined: "null", type: "string" }, - b: { required: false, nullableOrUndefined: "undefined", type: "number" }, - c: { required: false, nullableOrUndefined: "undefined", type: "number" }, - d: { type: "string", required: false, nullableOrUndefined: false }, - e: { type: "number", required: true, nullableOrUndefined: false }, - f: { type: "boolean", required: true, nullableOrUndefined: false } -} - -describe("Meta story characterization", () => { - it("matches the rendered meta keys and values", () => { - const { meta } = generateMetaFromSchema(schema) - - for (const [key, value] of Object.entries(expectedSubMeta)) { - expect(meta[key as keyof typeof meta], `root.${key}`).toMatchObject(value) - expect(meta[`struct.${key}` as keyof typeof meta], `struct.${key}`).toMatchObject(value) - expect(meta[`nullableStruct.${key}` as keyof typeof meta], `nullableStruct.${key}`).toMatchObject(value) - } - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts b/packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts deleted file mode 100644 index a59acb468..000000000 --- a/packages/vue-components/__tests__/OmegaForm/NullableFieldInitialization.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("Nullable field initialization", () => { - it("should initialize nullable fields with null when missing from defaultValues", async () => { - const schema = S.Struct({ - aString: S.NullOr(S.NonEmptyString255).withConstructorDefault, - bString: S.NullOr(S.NonEmptyString255).withConstructorDefault, - cStruct: S - .NullOr(S.Struct({ - dString: S.NullOr(S.NonEmptyString255).withConstructorDefault - })) - .withConstructorDefault, - cLiteral: S.NullOr(S.Literal("test", "test2")).withConstructorDefault - }) - - let submittedValue: Record | null = null - let submitError = null - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - defaultValues: { - aString: null - }, - onSubmit: async ({ value }) => { - try { - submittedValue = value - } catch (error) { - submitError = error - } - } - }) - return { form } - } - }) - - // Check that both fields are initialized with null (not empty string) - await vi.waitFor(() => { - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(valuesText).toContain("\"aString\":null") - expect(valuesText).toContain("\"bString\":null") - expect(valuesText).toContain("\"cStruct\":null") - }) - - // Submit the form - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - - // Wait for the submission to complete - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - // Check that form submitted successfully without errors - expect(submitError).toBeNull() - expect(submittedValue).toEqual({ - aString: null, - bString: null, - cStruct: null, - cLiteral: null - }) - }) - - it("should convert empty string to null for nullable fields", async () => { - const schema = S.Struct({ - aString: S.NullOr(S.NonEmptyString255).withConstructorDefault, - bString: S.NonEmptyString255 - }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - onSubmit: async () => { - // Should not be called since bString is required and missing - } - }) - return { form } - } - }) - - await wrapper.vm.$nextTick() - - // aString should be null (nullable field with empty string) - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(valuesText).toContain("\"aString\":null") - - // Submit the form - should fail because bString is required - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - - // Check that there's an error for bString - const errors = wrapper.find("[data-testid=\"omega-errors\"]") - if (errors.exists()) { - expect(errors.text()).toContain("bString") - } - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts b/packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts deleted file mode 100644 index 7e8b487f3..000000000 --- a/packages/vue-components/__tests__/OmegaForm/NullableNestedStructValidation.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it, vi } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -/** - * Regression repro for the configurator `LinkedOption.override` schema. - * - * `override` is a nullable struct (`S.NullOr(S.Struct({...})).withConstructorDefault`) - * whose own children are themselves nullable (`S.NullOr(...)`). The form starts - * with `override === null`. As soon as the user fills ONE child (`override.min`), - * the struct materialises while its siblings (`override.max`, `override.readOnly`) - * are still `undefined`. - * - * `undefined` is not a valid member of `S.NullOr(...)`, so validation rejected the - * untouched siblings with "field must not be empty" and the form couldn't submit. - * The configurator worked around this by making every child `S.optional`; the - * correct behaviour is for OmegaForm to treat an untouched nullable child as - * `null` once its nullable parent struct materialises. - */ -describe("Nullable nested struct validation", () => { - const schema = S.Struct({ - optionId: S.NullOr(S.String), - override: S - .NullOr(S.Struct({ - min: S.NullOr(S.NonNegativeNumber), - max: S.NullOr(S.NonNegativeNumber), - readOnly: S.NullOr(S.Boolean).withConstructorDefault, - isInteger: S.optional(S.NullOr(S.Boolean)) - })) - .withConstructorDefault - }) - - it("submits when only one child of a nullable struct is filled", async () => { - let submittedValue: Record | null = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - onSubmit: async ({ value }) => { - submittedValue = value as Record - } - }) - return { form } - } - }) - - // The whole struct starts null. - await vi.waitFor(() => { - expect(wrapper.find("[data-testid=\"values\"]").text()).toContain("\"override\":null") - }) - - // User fills only `override.min`. - await wrapper.find("[data-testid=\"min-input\"]").setValue("100") - - // The struct materialised: its untouched nullable siblings are backfilled - // into the live form state, not left `undefined`. - await vi.waitFor(() => { - const valuesText = wrapper.find("[data-testid=\"values\"]").text() - expect(valuesText).toContain("\"min\":100") - expect(valuesText).toContain("\"max\":null") - expect(valuesText).toContain("\"readOnly\":null") - }) - - // Submit. - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - - // The untouched nullable siblings must NOT block submission; they decode as null. - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - expect(submittedValue).toMatchObject({ - override: { - min: 100, - max: null, - readOnly: null - } - }) - }) - - it("keeps an untouched nullable struct as null", async () => { - let submittedValue: Record | null = null - - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema, { - onSubmit: async ({ value }) => { - submittedValue = value as Record - } - }) - return { form } - } - }) - - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - - await vi.waitFor(() => { - expect(submittedValue).not.toBeNull() - }) - - // Nothing was filled — `override` must stay null, not materialise. - expect(submittedValue).toMatchObject({ override: null }) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts b/packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts deleted file mode 100644 index 2884bfb4c..000000000 --- a/packages/vue-components/__tests__/OmegaForm/OmegaAutoGen.test.ts +++ /dev/null @@ -1,331 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import * as Order from "effect/Order" -import { describe, expect, it } from "vitest" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -// We need to mock the components used by OmegaAutoGen.vue since they depend on Vuetify -const mockComponents = { - OmegaInput: { - template: ` -
- - -
- `, - props: ["form", "name", "label"] - } -} - -describe("OmegaAutoGen", () => { - it("generates components from schema with the correct props", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - const inputs = wrapper.findAll(".mock-omega-input") - expect(inputs.length).toBe(2) // Should generate 2 inputs - - const firstInput = inputs[0] - expect(firstInput.find("label").attributes("for")).toBe("string") - expect(firstInput.find("input").attributes("name")).toBe("string") - - const secondInput = inputs[1] - expect(secondInput.find("label").attributes("for")).toBe("number") - expect(secondInput.find("input").attributes("name")).toBe("number") - }) - - // Pick option test - it("uses the pick option to include only specific fields", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite, - boolean: S.Boolean - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Should only have inputs for fields in the pick array - const inputs = wrapper.findAll(".mock-omega-input") - expect(inputs.length).toBe(1) - expect(inputs[0].find("input").attributes("name")).toBe("string") - }) - - // Omit option test - it("uses the omit option to exclude specific fields", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite, - boolean: S.Boolean - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Should only have inputs for fields not in the omit array - const inputs = wrapper.findAll(".mock-omega-input") - expect(inputs.length).toBe(1) - expect(inputs[0].find("input").attributes("name")).toBe("string") - }) - - // Order option test - it("uses the order option to control field order", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite, - boolean: S.Boolean - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Check that the fields are ordered according to the specified order - const inputs = wrapper.findAll(".mock-omega-input input") - const inputNames = inputs.map((input) => input.attributes("name")) - - // The order should match the specified order - expect(inputNames[0]).toBe("boolean") - expect(inputNames[1]).toBe("number") - expect(inputNames[2]).toBe("string") - }) - - // Sort option test - it("uses the sort option to sort fields", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - charlie: S.String, - alpha: S.String, - bravo: S.String - }) - ) - - // Create a sorter that sorts by name alphabetically - const sortByName = Order.mapInput( - Order.String, - (item: { name: string }) => item.name - ) - - return { form, sortByName } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - // Check that the fields are sorted alphabetically by name - const inputs = wrapper.findAll(".mock-omega-input input") - const inputNames = inputs.map((input) => input.attributes("name")) - - // The fields should be sorted alphabetically - expect(inputNames[0]).toBe("alpha") - expect(inputNames[1]).toBe("bravo") - expect(inputNames[2]).toBe("charlie") - }) - - // LabelMap - it("applies the labelMap option", () => { - const wrapper = mount( - { - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite - }) - ) - return { form } - } - }, - { - global: { - stubs: { - OmegaInput: mockComponents.OmegaInput - } - } - } - ) - - const labels = wrapper.findAll("label") - expect(labels[0].text()).toBe("Custom String Label") - expect(labels[1].text()).toBe("number") // Default label is the key - }) - - // Custom slot - it("allows custom slot content", () => { - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - - - `, - setup() { - const form = useOmegaForm( - S.Struct({ - string: S.String, - number: S.Finite - }) - ) - return { form } - } - }) - - const customElements = wrapper.findAll(".custom-slot") - expect(customElements.length).toBe(2) - - expect(wrapper.find("[data-testid=\"string-custom\"]").exists()).toBe(true) - expect(wrapper.find("[data-testid=\"number-custom\"]").exists()).toBe(true) - - const labels = wrapper.findAll("label") - expect(labels[0].text()).toBe("Custom string") - expect(labels[1].text()).toBe("Custom number") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts b/packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts deleted file mode 100644 index 03363ab36..000000000 --- a/packages/vue-components/__tests__/OmegaForm/OptionalKey.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../../src/components/OmegaForm" - -describe("optionalKey required handling", () => { - it("should mark optionalKey fields as not required", () => { - const schema = S.Struct({ - number: S.optionalKey(S.Int.pipe(S.check(S.isBetween({ minimum: 1, maximum: 20 })))), - height: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))) - }) - - const { meta } = generateMetaFromSchema(schema) - - // optionalKey field should NOT be required - expect(meta.number?.required).toBe(false) - - // regular field should still be required - expect(meta.height?.required).toBe(true) - }) - - it("should mark optionalKey with decodingDefault as not required", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("defaultName"))), - age: S.NonEmptyString255 - }) - - const { meta } = generateMetaFromSchema(schema) - - expect(meta.name?.required).toBe(false) - expect(meta.age?.required).toBe(true) - }) - - it("should handle optionalKey in tagged union branches", () => { - const schema = S.Union([ - S.TaggedStruct("one", { - a: S.Struct({ - number: S.optionalKey(S.Int.pipe(S.check(S.isBetween({ minimum: 1, maximum: 20 })))), - height: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))), - z: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))) - }) - }), - S.TaggedStruct("two", { - a: S.Struct({ - number: S.optionalKey(S.Int.pipe(S.check(S.isBetween({ minimum: 1, maximum: 20 })))), - height: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))), - y: S.NonEmptyString100.pipe(S.check(S.isMinLength(10))) - }) - }) - ]) - - const { meta } = generateMetaFromSchema(schema) - - // optionalKey in both union branches should not be required - expect(meta["a.number"]?.required).toBe(false) - - // regular fields should be required - expect(meta["a.height"]?.required).toBe(true) - expect(meta["a.z"]?.required).toBe(true) - expect(meta["a.y"]?.required).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts b/packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts deleted file mode 100644 index e2d46c0a5..000000000 --- a/packages/vue-components/__tests__/OmegaForm/RedactedMeta.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as S from "effect-app/Schema" -import * as Redacted from "effect/Redacted" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema, toFormSchema } from "../../src/components/OmegaForm" - -describe("S.Redacted meta generation", () => { - it("should generate correct meta for S.Redacted(S.NonEmptyString255)", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) - - const result = generateMetaFromSchema(schema) - - expect(result.meta["email"]).toMatchObject({ - type: "string", - minLength: 1, - maxLength: 255, - required: true, - nullableOrUndefined: false - }) - - expect(result.meta["password"]).toMatchObject({ - type: "string", - minLength: 1, - maxLength: 255, - required: true, - nullableOrUndefined: false - }) - }) - - it("should generate correct meta for S.Redacted(S.String) — not required since empty string is valid", () => { - const schema = S.Struct({ - secret: S.Redacted(S.String) - }) - - const result = generateMetaFromSchema(schema) - - expect(result.meta["secret"]).toMatchObject({ - type: "string", - required: false, - nullableOrUndefined: false - }) - }) - - it("should generate correct meta for S.NullOr(S.Redacted(S.NonEmptyString255))", () => { - const schema = S.Struct({ - optionalSecret: S.NullOr(S.Redacted(S.NonEmptyString255)) - }) - - const result = generateMetaFromSchema(schema) - - expect(result.meta["optionalSecret"]).toMatchObject({ - type: "string", - required: false, - nullableOrUndefined: "null" - }) - }) -}) - -describe("toFormSchema — S.Redacted validation", () => { - it("should accept plain strings for S.Redacted fields and decode to Redacted", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) - - const formSchema = toFormSchema(schema) - const result = S.decodeUnknownSync(formSchema)({ - email: "test@test.com", - password: "secret123" - }) - - expect(result.email).toBe("test@test.com") - expect(Redacted.isRedacted(result.password)).toBe(true) - expect(Redacted.value(result.password as Redacted.Redacted)).toBe("secret123") - }) - - it("should accept plain strings for S.Redacted(S.String)", () => { - const schema = S.Struct({ - secret: S.Redacted(S.String) - }) - - const formSchema = toFormSchema(schema) - const result = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - - expect(Redacted.isRedacted(result.secret)).toBe(true) - }) - - it("should pass standard schema validation with plain string values", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) - - const formSchema = toFormSchema(schema) - const ssv = S.toStandardSchemaV1(formSchema) - const result = (ssv as any)["~standard"].validate({ - email: "test@test.com", - password: "secret123" - }) - - expect(result.issues).toBeUndefined() - }) - - it("should still reject invalid inner values", () => { - const schema = S.Struct({ - password: S.Redacted(S.NonEmptyString255) - }) - - const formSchema = toFormSchema(schema) - expect(() => S.decodeUnknownSync(formSchema)({ password: "" })).toThrow() - }) - - it("should handle NullOr(S.Redacted(...))", () => { - const schema = S.Struct({ - secret: S.NullOr(S.Redacted(S.NonEmptyString255)) - }) - - const formSchema = toFormSchema(schema) - - const result1 = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(Redacted.isRedacted(result1.secret)).toBe(true) - - const result2 = S.decodeUnknownSync(formSchema)({ secret: null }) - expect(result2.secret).toBeNull() - }) - - it("should return the original schema when no S.Redacted fields exist", () => { - const schema = S.Struct({ - email: S.NonEmptyString255, - name: S.String - }) - - const formSchema = toFormSchema(schema) - expect(formSchema).toBe(schema) - }) - - it("should work with S.Class schemas", () => { - class Credentials extends S.Class("Credentials")({ - email: S.NonEmptyString255, - password: S.Redacted(S.NonEmptyString255) - }) {} - - const formSchema = toFormSchema(Credentials) - const result = S.decodeUnknownSync(formSchema)({ - email: "test@test.com", - password: "secret" - }) - - expect(Redacted.isRedacted(result.password)).toBe(true) - }) - - it("toFormSchema rewrites NullOr(Redacted)", () => { - const schema = S.Struct({ - secret: S.NullOr(S.Redacted(S.String)) - }) - const formSchema = toFormSchema(schema) - // formSchema must accept a plain string (encoded side) for the secret field - const decoded = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(decoded.secret).toBeDefined() - }) - - it("toFormSchema rewrites UndefinedOr(Redacted)", () => { - const schema = S.Struct({ - secret: S.UndefinedOr(S.Redacted(S.String)) - }) - const formSchema = toFormSchema(schema) - const decoded = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(decoded.secret).toBeDefined() - }) - - it("toFormSchema rewrites NullishOr(Redacted)", () => { - const schema = S.Struct({ - secret: S.NullishOr(S.Redacted(S.String)) - }) - const formSchema = toFormSchema(schema) - const decoded = S.decodeUnknownSync(formSchema)({ secret: "hello" }) - expect(decoded.secret).toBeDefined() - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts b/packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts deleted file mode 100644 index 542a0b302..000000000 --- a/packages/vue-components/__tests__/OmegaForm/SelectMultipleLocalization.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -// __tests__/OmegaForm/SelectMultipleLocalization.test.ts -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const mountForm = (setup: () => T): T => { - let captured: T | undefined - const Inner = defineComponent({ - setup() { - captured = setup() - return {} - }, - template: "
" - }) - mount(defineComponent({ - components: { OmegaIntlProvider, Inner }, - template: "" - })) - if (!captured) throw new Error("no capture") - return captured -} - -const firstFieldError = (form: any, name: string): string | undefined => { - const errs = form.getAllErrors().fields[name]?.errors - if (!errs) return undefined - const flat = errs.flat ? errs.flat() : errs - return flat[0]?.message -} - -describe("select / multiple validation messages carry localized payload", () => { - it("S.Literals — invalid (non-empty) member emits 'select' message with members", async () => { - const schema = S.Struct({ color: S.Literals(["red", "blue", "green"]) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - // OmegaIntlProvider's mock `trans` returns `${key}_translated` and - // ignores variables — so we just need the i18n key to flow, not the - // substituted variables. - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("S.Array(S.Literals) — non-array value emits 'multiple' message", async () => { - const schema = S.Struct({ tags: S.Array(S.Literals(["a", "b", "c"])) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { tags: "not-an-array" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "tags") - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("S.NullOr(S.Literals) — invalid member emits localized select message", async () => { - const schema = S.Struct({ color: S.NullOr(S.Literals(["red", "blue", "green"])) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("S.UndefinedOr(S.Literals) — invalid member emits localized select message", async () => { - const schema = S.Struct({ color: S.UndefinedOr(S.Literals(["red", "blue", "green"])) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - expect(msg, `got: ${msg}`).toBe("validation.not_a_valid_translated") - }) - - it("user-supplied .annotate({ message }) on S.Literals is preserved", async () => { - const schema = S.Struct({ - color: S.Literals(["red", "blue", "green"]).annotate({ - message: "user.custom.message" - }) - }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: "purple" } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - // User annotation wins — verbatim, not piped through trans. - expect(msg).toBe("user.custom.message") - }) - - it("empty value on a required select also emits the localized 'select' message (matches main)", async () => { - // Main's `generateInputStandardSchemaFromFieldMeta` annotated the entire - // S.Literals schema with a `validation.not_a_valid` message, so any - // failure (including undefined) surfaced it. We preserve that behavior. - const schema = S.Struct({ color: S.Literals(["red", "blue", "green"]) }) - const form = mountForm(() => - useOmegaForm(schema, { - defaultValues: { color: undefined } as any, - onSubmit: async () => undefined - }) - ) - await Effect.runPromise(form.handleSubmitEffect()) - const msg = firstFieldError(form, "color") - expect(msg).toBe("validation.not_a_valid_translated") - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts b/packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts deleted file mode 100644 index 2c4fd11fb..000000000 --- a/packages/vue-components/__tests__/OmegaForm/SubmitEffect.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -// __tests__/OmegaForm/SubmitEffect.test.ts -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import * as Cause from "effect/Cause" -import * as Exit from "effect/Exit" -import { describe, expect, it } from "vitest" -import { defineComponent } from "vue" -import { FormErrors, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const mountForm = (setupForm: () => T): T => { - let captured: T | undefined - const Inner = defineComponent({ - setup() { - captured = setupForm() - return {} - }, - template: "
" - }) - const Wrapper = defineComponent({ - components: { OmegaIntlProvider, Inner }, - template: "" - }) - mount(Wrapper) - if (!captured) throw new Error("setupForm did not return") - return captured -} - -describe("handleSubmitEffect", () => { - it("succeeds with void on valid input (no checkErrors)", async () => { - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String.pipe(S.check(S.isMinLength(2))) }), - { defaultValues: { x: "ok" }, onSubmit: async () => undefined } - ) - ) - const exit = await Effect.runPromise(Effect.exit(form.handleSubmitEffect())) - expect(Exit.isSuccess(exit)).toBe(true) - }) - - it("succeeds with void even on invalid input without checkErrors", async () => { - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String.pipe(S.check(S.isMinLength(2))) }), - { defaultValues: { x: "" }, onSubmit: async () => undefined } - ) - ) - const exit = await Effect.runPromise(Effect.exit(form.handleSubmitEffect())) - expect(Exit.isSuccess(exit)).toBe(true) - }) - - it("fails with FormErrors when checkErrors and validation fails", async () => { - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String.pipe(S.check(S.isMinLength(2))) }), - { defaultValues: { x: "" }, onSubmit: async () => undefined } - ) - ) - const exit = await Effect.runPromise( - Effect.exit(form.handleSubmitEffect({ checkErrors: true })) - ) - expect(Exit.isFailure(exit)).toBe(true) - if (Exit.isFailure(exit)) { - const failReason = exit.cause.reasons.find(Cause.isFailReason) - expect(failReason?.error).toBeInstanceOf(FormErrors) - } - }) - - it("delivers decoded To to onSubmit (not raw From)", async () => { - let received: unknown - const form = mountForm(() => - useOmegaForm( - S.Struct({ n: S.FiniteFromString }), - { - defaultValues: { n: "42" }, - onSubmit: async ({ value }) => { - received = value - } - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(received).toEqual({ n: 42 }) - }) - - it("awaits Promise-returning onSubmit", async () => { - let resolved = false - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String }), - { - defaultValues: { x: "" }, - onSubmit: async () => { - await new Promise((r) => setTimeout(r, 5)) - resolved = true - } - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(resolved).toBe(true) - }) - - it("awaits Effect-returning onSubmit", async () => { - let resolved = false - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String }), - { - defaultValues: { x: "" }, - onSubmit: () => - Effect.sync(() => { - resolved = true - }) - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(resolved).toBe(true) - }) - - it("awaits Fiber-returning onSubmit", async () => { - let resolved = false - const form = mountForm(() => - useOmegaForm( - S.Struct({ x: S.String }), - { - defaultValues: { x: "" }, - onSubmit: () => - Effect.runFork( - Effect.sync(() => { - resolved = true - }) - ) - } - ) - ) - await Effect.runPromise(form.handleSubmitEffect()) - expect(resolved).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts b/packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts deleted file mode 100644 index 7d4bb7200..000000000 --- a/packages/vue-components/__tests__/OmegaForm/SubmitErrorClear.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -// __tests__/OmegaForm/SubmitErrorClear.test.ts -import { mount } from "@vue/test-utils" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, nextTick } from "vue" -import { useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -describe("post-submit revalidation (TanStack default)", () => { - it("revalidates the form-level schema on value change after a failed submit", async () => { - const schema = S.Struct({ - a: S.String.pipe(S.check(S.isMinLength(2))), - b: S.String.pipe(S.check(S.isMinLength(2))) - }) - - let captured: ReturnType> | undefined - const Inner = defineComponent({ - setup() { - const form = useOmegaForm(schema, { defaultValues: { a: "", b: "" } }) - captured = form as any - return { form } - }, - // Render fields so they register with the form. Without mounted fields, - // setFieldValue cannot trigger validation (FormApi.validateField bails - // when fieldInfo[name].instance is undefined). - template: ` - - - - - ` - }) - const Wrapper = defineComponent({ - components: { OmegaIntlProvider, Inner }, - template: "" - }) - mount(Wrapper) - if (!captured) throw new Error("setupForm did not return") - const form = captured - - await form.handleSubmit() - await nextTick() - - // After failed submit, fix only field a; field b should still fail validation - // because TanStack revalidates the entire form-level schema. - form.setFieldValue("a", "ok") - await nextTick() - await nextTick() - - // canSubmit should still be false because b is still invalid - expect(form.store.state.canSubmit).toBe(false) - - // Now fix b too - form.setFieldValue("b", "ok") - await nextTick() - await nextTick() - expect(form.store.state.canSubmit).toBe(true) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts b/packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts deleted file mode 100644 index 7e229904e..000000000 --- a/packages/vue-components/__tests__/OmegaForm/TaggedUnionBehavior.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defineComponent, nextTick } from "vue" -import { createUseFormWithCustomInput } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -const HeadlessInput = defineComponent({ - inheritAttrs: false, - props: ["inputProps", "field", "state", "options"], - template: ` -
- - - {{ inputProps.errorMessages?.join(', ') }} -
- `, - methods: { - onInput(this: any, e: Event) { - const target = e.target as HTMLInputElement - const value = target.value - if (this.inputProps.type === "number") { - this.field.handleChange(value === "" ? null : Number(value)) - } else { - this.field.handleChange(value) - } - } - } -}) - -const useForm = createUseFormWithCustomInput(HeadlessInput) - -const flushAfterTagChange = async () => { - // OmegaTaggedUnionInternal watches _tag and on change calls form.reset(), - // then setTimeout(validate, 0). Give it room. - await nextTick() - await nextTick() - await new Promise((r) => setTimeout(r, 10)) - await nextTick() -} - -describe("RootLevelTaggedUnion behavior", () => { - const schema = S.Union([ - S.TaggedStruct("A", { - a: S.NonEmptyString255.pipe( - S.withConstructorDefault(Effect.succeed(S.NonEmptyString255("aaaa"))) - ), - common: S.NonEmptyString255 - }), - S.TaggedStruct("B", { - b: S.Finite, - nullableB: S.NullOr(S.Finite), - common: S.NullOr(S.String) - }) - ]) - - const mountForm = (onSubmit?: (value: any) => void) => { - return mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useForm(schema as any, { - defaultValues: { _tag: "A" } as any, - onSubmit: async ({ value }: any) => onSubmit?.(value) - }) - const tagOptions = [ - { value: "A", title: "Option A" }, - { value: "B", title: "Option B" } - ] - return { form, tagOptions } - } - }) - } - - it("renders branch A's slot by default and not B's", async () => { - const wrapper = mountForm() - await nextTick() - - expect(wrapper.find("[data-testid=\"input-a\"]").exists()).toBe(true) - expect(wrapper.find("[data-testid=\"input-common\"]").exists()).toBe(true) - expect(wrapper.find("[data-testid=\"input-b\"]").exists()).toBe(false) - expect(wrapper.find("[data-testid=\"input-nullableB\"]").exists()).toBe(false) - }) - - it("toggling _tag from A to B swaps the rendered branch slot", async () => { - const wrapper = mountForm() - await nextTick() - - const select = wrapper.find("[data-testid=\"select-_tag\"]") - expect(select.exists()).toBe(true) // Drive the change through the form API to mirror what the UI handler - // produces. setValue + change events on the bare - {{ inputProps.errorMessages.join(', ') }} - - `, - props: ["inputProps", "field"], - inheritAttrs: false -} - -describe("createUseFormWithCustomInput", () => { - const useForm = createUseFormWithCustomInput(CustomTestInput) - const schema = S.Struct({ testField: S.String }) - - const createWrapper = (options?: { - defaultValues?: any - onSubmit?: (data: any) => void | Promise - }) => { - let submittedValue: any = null - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useForm(schema, { - defaultValues: options?.defaultValues || { testField: "" }, - onSubmit: async ({ value }) => { - submittedValue = value - options?.onSubmit?.(value) - } - }) - return { form, getSubmittedValue: () => submittedValue } - } - }, { - global: { - stubs: { - CustomTestInput - } - } - }) - - return wrapper - } - - it("should render custom input component with correct props", async () => { - const wrapper = createWrapper({ defaultValues: { testField: "test value" } }) - await wrapper.vm.$nextTick() - - // Check that custom input was rendered - const customInput = wrapper.find("[data-testid=\"custom-input\"]") - expect(customInput.exists()).toBe(true) - expect(customInput.classes()).toContain("custom-test-input") - - // Verify the input has the correct value - const inputElement = wrapper.find("[data-testid=\"custom-input-field\"]") - expect(inputElement.exists()).toBe(true) - expect((inputElement.element as HTMLInputElement).value).toBe("test value") - - // Verify the label is rendered - const label = customInput.find("label") - expect(label.exists()).toBe(true) - expect(label.text()).toContain("Test Field") - }) - - it("should handle input changes and submission", async () => { - const wrapper = createWrapper({ defaultValues: { testField: "initial" } }) - await wrapper.vm.$nextTick() - - // Verify initial value - const input = wrapper.find("[data-testid=\"custom-input-field\"]") - expect((input.element as HTMLInputElement).value).toBe("initial") - - // Change the value - await input.setValue("changed value") - await wrapper.vm.$nextTick() - - // Verify the value changed in the form state - const valuesDiv = wrapper.find("[data-testid=\"values\"]") - expect(valuesDiv.text()).toContain("\"testField\":\"changed value\"") - - // Submit the form - await wrapper.find("[data-testid=\"submit\"]").trigger("click") - await wrapper.vm.$nextTick() - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Check that the form received the changed value - const vm = wrapper.vm as any - expect(vm.getSubmittedValue()).toEqual({ testField: "changed value" }) - }) - - it("should pass inputProps to custom component", async () => { - // Custom component that validates props - const PropsValidatingInput = { - template: ` -
-
{{ !!inputProps }}
-
{{ !!field }}
-
{{ inputProps.label }}
-
{{ field.name }}
-
{{ inputProps.id }}
-
- `, - props: ["inputProps", "field"] - } - - const useFormWithValidator = createUseFormWithCustomInput(PropsValidatingInput) - const validationSchema = S.Struct({ myField: S.String }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithValidator(validationSchema, { - defaultValues: { myField: "test-value" } - }) - return { form } - } - }, { - global: { - stubs: { - PropsValidatingInput - } - } - }) - - await wrapper.vm.$nextTick() - - const validator = wrapper.find("[data-testid=\"props-validator\"]") - expect(validator.exists()).toBe(true) - - // Check all required props are passed - expect(wrapper.find("[data-testid=\"has-inputprops\"]").text()).toBe("true") - expect(wrapper.find("[data-testid=\"has-field\"]").text()).toBe("true") - expect(wrapper.find("[data-testid=\"has-label\"]").text()).toContain("My Label") - expect(wrapper.find("[data-testid=\"has-id\"]").text()).toBeTruthy() - }) - - it("forwards an explicit `required` override to inputProps (survives OmegaInput's prop whitelist)", async () => { - const RequiredProbe = { - template: `
{{ inputProps.required }}
`, - props: ["inputProps", "field"] - } - - const useFormWithProbe = createUseFormWithCustomInput(RequiredProbe) - // optionalKey field => meta.required is false, so a `true` result proves the - // explicit prop overrode meta AND was not dropped on the way to the input. - const optionalSchema = S.Struct({ maybe: S.optionalKey(S.Number) }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithProbe(optionalSchema, { defaultValues: {} }) - return { form } - } - }, { - global: { - stubs: { - RequiredProbe - } - } - }) - - await wrapper.vm.$nextTick() - - expect(wrapper.find("[data-testid=\"required-probe\"]").text()).toBe("true") - }) - - it("falls back to schema-derived required when the prop is omitted", async () => { - const RequiredProbe = { - template: ` -
-
{{ inputProps.required }}
-
{{ inputProps.label }}
-
- `, - props: ["inputProps", "field"] - } - - const useFormWithProbe = createUseFormWithCustomInput(RequiredProbe) - const requiredSchema = S.Struct({ number: S.Number }) - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithProbe(requiredSchema) - return { form } - } - }, { - global: { - stubs: { - RequiredProbe - } - } - }) - - await wrapper.vm.$nextTick() - - expect(wrapper.find("[data-testid=\"required-probe\"]").text()).toBe("true") - expect(wrapper.find("[data-testid=\"label-probe\"]").text()).toBe("Number *") - }) - - it("should pass event listeners to custom input component", async () => { - // Custom component that emits focus and blur events - const EventEmittingInput = { - template: ` -
- -
- `, - props: ["inputProps", "field"], - emits: ["focus", "blur"], - inheritAttrs: false - } - - const useFormWithEvents = createUseFormWithCustomInput(EventEmittingInput) - const eventSchema = S.Struct({ eventField: S.String }) - - let focusCount = 0 - let blurCount = 0 - - const wrapper = mount({ - components: { - OmegaIntlProvider - }, - template: ` - - - - - - `, - setup() { - const form = useFormWithEvents(eventSchema, { - defaultValues: { eventField: "" } - }) - - const handleFocus = () => { - focusCount++ - } - - const handleBlur = () => { - blurCount++ - } - - return { form, handleFocus, handleBlur } - } - }, { - global: { - stubs: { - EventEmittingInput - } - } - }) - - await wrapper.vm.$nextTick() - - // Verify the custom input was rendered - const eventInput = wrapper.find("[data-testid=\"event-input\"]") - expect(eventInput.exists()).toBe(true) - - // Trigger focus event - await eventInput.trigger("focusin") - await wrapper.vm.$nextTick() - - // Verify focus handler was called - expect(focusCount).toBe(1) - - // Trigger blur event - await eventInput.trigger("focusout") - await wrapper.vm.$nextTick() - - // Verify blur handler was called - expect(blurCount).toBe(1) - - // Trigger multiple times to ensure it keeps working - await eventInput.trigger("focusin") - await eventInput.trigger("focusout") - await wrapper.vm.$nextTick() - - expect(focusCount).toBe(2) - expect(blurCount).toBe(2) - }) -}) diff --git a/packages/vue-components/__tests__/OmegaFormTest.vue b/packages/vue-components/__tests__/OmegaFormTest.vue deleted file mode 100644 index b84195d3f..000000000 --- a/packages/vue-components/__tests__/OmegaFormTest.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - diff --git a/packages/vue-components/__tests__/OmegaIntlProvider.vue b/packages/vue-components/__tests__/OmegaIntlProvider.vue deleted file mode 100644 index bfcf31cbc..000000000 --- a/packages/vue-components/__tests__/OmegaIntlProvider.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/vue-components/__tests__/migration/schema-v4-migration.test.ts b/packages/vue-components/__tests__/migration/schema-v4-migration.test.ts deleted file mode 100644 index ad5f1393c..000000000 --- a/packages/vue-components/__tests__/migration/schema-v4-migration.test.ts +++ /dev/null @@ -1,703 +0,0 @@ -/** - * OmegaForm Schema Tests - * - * These tests pin the current behavior of generateMetaFromSchema and defaultsValueFromSchema, - * ensuring regression safety during refactors. - * - * Strategy: - * 1. "current" tests → pin behavior with current schema patterns - * 2. When refactoring internals, ALL tests must stay green - * - * NOTE: Uses `S.withConstructorDefault` from effect-app (wraps in Effect), - * NOT `S.withConstructorDefault` from native effect (expects raw Option return). - * - * ---- v4 breaking changes (from v3) ---- - * - * 1. Nested union _tag: FIXED — both patterns now produce "select" - * S.TaggedStruct produces a bare Literal AST node. - * Legacy S.Struct({ _tag: S.Literal("X") }) produces Union([Literal("X")]) - * after AST.toType. Fixed via unwrapSingleLiteralUnion helper. - * A console.warn is emitted for the legacy pattern to encourage migration. - * - * 2. Root-level union unionMeta: FIXED — both patterns now generate unionMeta - * Same root cause as #1: single-element Union wrapping prevented tag extraction. - * Fixed via the same unwrapSingleLiteralUnion helper in metadataFromAst. - * - * 3. UndefinedOr defaults include the key with explicit undefined - * v3: defaultsValueFromSchema skips keys where the recursive call returns - * undefined (if fieldValue !== undefined), so UndefinedOr fields are - * omitted from the result object entirely. - * v4: the key will be present in the result with an explicit undefined value. - * - * 4. S.optionalKey(X).pipe(S.withDecodingDefault(...)) support in defaultsValueFromSchema - * v3: not supported (v3 equivalent was S.optionalWith which encoded defaults - * inside a PropertySignatureTransformation, opaque to AST inspection). - * v4: defaultsValueFromSchema detects PropertySignatureTransformation - * and extracts defaults, enabling withDecodingDefault as a new pattern for - * declaring field defaults directly on the schema. - * - * ---- API mapping ---- - * - * | v4 | v3 | - * |--------------------------------------------|--------------------------------| - * | `S.check(S.isGreaterThanOrEqualTo(x))` | `S.greaterThanOrEqualTo(x)` | - * | `S.check(S.isLessThanOrEqualTo(x))` | `S.lessThanOrEqualTo(x)` | - * | `S.check(S.isGreaterThan(x))` | `S.greaterThan(x)` | - * | `S.check(S.isLessThan(x))` | `S.lessThan(x)` | - * | `S.check(S.isBetween({minimum, maximum}))` | `S.between(minimum, maximum)` | - * | `S.check(S.isInt())` | `S.int()` | - * | `S.check(S.isMinLength(x))` | `S.minLength(x)` | - * | `S.check(S.isMaxLength(x))` | `S.maxLength(x)` | - * | `S.Union([...members])` | `S.Union(...members)` | - * | `S.optionalKey(S.X)` | `S.optionalWith` | - * | `.pipe(S.withDecodingDefault(...))` | `(S.X, { default: ... })` | - */ - -import { mount } from "@vue/test-utils" -import * as Effect from "effect-app/Effect" -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { defaultsValueFromSchema, generateMetaFromSchema, useOmegaForm } from "../../src/components/OmegaForm" -import OmegaIntlProvider from "../OmegaIntlProvider.vue" - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** Mount a form and extract its default values */ -const mountAndGetDefaults = async (schema: S.Schema, opts?: any) => { - const wrapper = mount({ - components: { OmegaIntlProvider }, - template: ` - - - - - - `, - setup() { - const form = useOmegaForm(schema as any, opts) - return { form } - } - }) - await wrapper.vm.$nextTick() - return JSON.parse(wrapper.find("[data-testid=\"values\"]").text()) -} - -// ============================================================================ -// 1. generateMetaFromSchema — pin current behavior -// ============================================================================ - -describe("generateMetaFromSchema", () => { - describe("simple struct", () => { - const schema = S.Struct({ - name: S.NonEmptyString255, - age: S.Finite.pipe(S.check(S.isGreaterThanOrEqualTo(0))), - active: S.Boolean - }) - - it("produces correct metadata for basic types", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta.name?.type).toBe("string") - expect(meta.name?.required).toBe(true) - expect(meta.name?.minLength).toBe(1) - expect(meta.name?.maxLength).toBe(255) - expect(meta.age?.type).toBe("number") - expect(meta.age?.required).toBe(true) - expect(meta.age?.minimum).toBe(0) - expect(meta.active?.type).toBe("boolean") - expect(meta.active?.required).toBe(true) - }) - }) - - describe("nullable fields", () => { - const schema = S.Struct({ - required: S.NonEmptyString255, - nullable: S.NullOr(S.String), - optional: S.UndefinedOr(S.String) - }) - - it("nullable/undefined fields are not required", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta.required?.required).toBe(true) - expect(meta.nullable?.required).toBe(false) - expect(meta.nullable?.nullableOrUndefined).toBe("null") - expect(meta.optional?.required).toBe(false) - expect(meta.optional?.nullableOrUndefined).toBe("undefined") - }) - }) - - describe("nested struct", () => { - const schema = S.Struct({ - outer: S.Struct({ - inner: S.NonEmptyString255, - deep: S.Struct({ - value: S.Finite - }) - }) - }) - - it("produces dot-notation keys for nested fields", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta["outer.inner"]).toBeDefined() - expect(meta["outer.inner"]?.type).toBe("string") - expect(meta["outer.deep.value"]).toBeDefined() - expect(meta["outer.deep.value"]?.type).toBe("number") - }) - }) - - describe("discriminated union (nested in struct)", () => { - const schema = S.Struct({ - union: S.Union([ - S.Struct({ _tag: S.tag("A"), a: S.NonEmptyString255 }), - S.Struct({ _tag: S.tag("B"), b: S.Finite }) - ]) - }) - - it("generates _tag as select with member values", () => { - const { meta } = generateMetaFromSchema(schema) - // Works for both S.TaggedStruct and legacy S.Struct({ _tag: S.Literal(...) }) - // thanks to single-element Union unwrapping in createMeta - expect(meta["union._tag"]?.type).toBe("select") - expect(meta["union._tag"]?.required).toBe(true) - expect(meta["union._tag"]?.members).toBeDefined() - }) - - it("generates field metadata for each union branch", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta["union.a"]?.type).toBe("string") - expect(meta["union.b"]?.type).toBe("number") - }) - - it("nested union now populates unionMeta after unification (Phase 2.4)", () => { - // After unification of root + nested union handling, nested unions also - // populate unionMeta, mirroring root-level behavior. - const { unionMeta } = generateMetaFromSchema(schema) - expect(Object.keys(unionMeta).length).toBe(2) - expect(unionMeta["A"]).toBeDefined() - expect(unionMeta["B"]).toBeDefined() - }) - }) - - describe("nullable discriminated union", () => { - const schema = S.Struct({ - union: S.NullOr( - S.Union([ - S.Struct({ _tag: S.Literal("A"), a: S.NonEmptyString255, common: S.String }), - S.Struct({ _tag: S.Literal("B"), b: S.Finite, common: S.String }) - ]) - ) - }) - - it("_tag is not required, other fields are required based on their type", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta["union._tag"]?.required).toBe(false) - expect(meta["union.a"]?.required).toBe(true) - expect(meta["union.b"]?.required).toBe(true) - }) - }) - - describe("root-level discriminated union (legacy Literal pattern)", () => { - const schema = S.Union([ - S.Struct({ _tag: S.Literal("A"), a: S.NonEmptyString255 }), - S.Struct({ _tag: S.Literal("B"), b: S.Finite }) - ]) - - it("generates _tag select and per-member fields with unionMeta", () => { - const { meta, unionMeta } = generateMetaFromSchema(schema) - - expect(meta["_tag"]?.type).toBe("select") - expect(meta["_tag"]?.required).toBe(true) - expect(meta.a?.type).toBe("string") - expect(meta.b?.type).toBe("number") - expect(Object.keys(unionMeta).length).toBe(2) - }) - }) - - describe("root-level discriminated union (TaggedStruct pattern)", () => { - const schema = S.Union([ - S.TaggedStruct("A", { a: S.NonEmptyString255 }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - it("generates _tag select and per-member fields with unionMeta", () => { - const { meta, unionMeta } = generateMetaFromSchema(schema) - - expect(meta["_tag"]?.type).toBe("select") - expect(meta["_tag"]?.required).toBe(true) - expect(meta["_tag"]?.members).toEqual(["A", "B"]) - expect(meta.a?.type).toBe("string") - expect(meta.b?.type).toBe("number") - expect(Object.keys(unionMeta).length).toBe(2) - expect(unionMeta["A"]).toBeDefined() - expect(unionMeta["B"]).toBeDefined() - }) - - it("unionMeta contains per-branch field metadata", () => { - const { unionMeta } = generateMetaFromSchema(schema) - - expect(unionMeta["A"]?.["a"]?.type).toBe("string") - expect(unionMeta["A"]?.["a"]?.required).toBe(true) - expect(unionMeta["B"]?.["b"]?.type).toBe("number") - expect(unionMeta["B"]?.["b"]?.required).toBe(true) - // Each branch should NOT contain the other branch's fields - expect(unionMeta["A"]?.["b"]).toBeUndefined() - expect(unionMeta["B"]?.["a"]).toBeUndefined() - }) - }) - - describe("literal union (select)", () => { - const schema = S.Struct({ - direction: S.Union([S.Literal("left"), S.Literal("right"), S.Literal("both")]) - }) - - it("generates select type with members", () => { - const { meta } = generateMetaFromSchema(schema) - expect(meta.direction?.type).toBe("select") - expect(meta.direction?.members).toEqual(["left", "right", "both"]) - }) - }) -}) - -// ============================================================================ -// 2. defaultsValueFromSchema — pin current behavior -// ============================================================================ - -describe("defaultsValueFromSchema", () => { - describe("primitive defaults", () => { - it("String defaults to empty string", () => { - const schema = S.Struct({ name: S.String }) - expect(defaultsValueFromSchema(schema)).toEqual({ name: "" }) - }) - - it("Boolean defaults to false", () => { - const schema = S.Struct({ active: S.Boolean }) - expect(defaultsValueFromSchema(schema)).toEqual({ active: false }) - }) - - it("NullOr defaults to null", () => { - const schema = S.Struct({ value: S.NullOr(S.String) }) - expect(defaultsValueFromSchema(schema)).toEqual({ value: null }) - }) - - it("UndefinedOr defaults include the key with undefined value", () => { - const schema = S.Struct({ value: S.UndefinedOr(S.String) }) - const result = defaultsValueFromSchema(schema) - expect("value" in result).toBe(true) - expect(result.value).toBeUndefined() - }) - }) - - describe("withConstructorDefault (effect-app wrapper)", () => { - it("respects constructor defaults", () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("hello"))), - count: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(42))) - }) - const defaults = defaultsValueFromSchema(schema) - expect(defaults.name).toBe("hello") - expect(defaults.count).toBe(42) - }) - - it("constructor defaults in union members", () => { - const schema = S.Union([ - S.Struct({ - _tag: S.Literal("A").pipe(S.withConstructorDefault(Effect.succeed("A"))), - a: S.String.pipe(S.withConstructorDefault(Effect.succeed("defaultA"))) - }), - S.Struct({ - _tag: S.Literal("B"), - b: S.String - }) - ]) - const defaults = defaultsValueFromSchema(schema) - expect(defaults._tag).toBe("A") - expect(defaults.a).toBe("defaultA") - }) - }) - - describe("nested struct defaults", () => { - it("recursively extracts defaults from nested structs", () => { - const schema = S.Struct({ - outer: S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("nested"))), - value: S.Boolean - }) - }) - const defaults = defaultsValueFromSchema(schema) - expect(defaults.outer.name).toBe("nested") - expect(defaults.outer.value).toBe(false) - }) - }) - - describe("NullOr struct defaults", () => { - it("NullOr struct defaults to null", () => { - const schema = S.Struct({ - nested: S.NullOr(S.Struct({ x: S.String })) - }) - expect(defaultsValueFromSchema(schema)).toEqual({ nested: null }) - }) - }) -}) - -// ============================================================================ -// 3. withDecodingDefault — decoding defaults -// ============================================================================ - -describe("withDecodingDefault decoding defaults", () => { - it("withDecodingDefault fills value during decoding", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("defaultName"))), - age: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(0))) - }) - - const decoded = S.decodeUnknownSync(schema)({}) - expect(decoded).toEqual({ name: "defaultName", age: 0 }) - }) - - it("withDecodingDefault respects provided values", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("defaultName"))) - }) - - const decoded = S.decodeUnknownSync(schema)({ name: "provided" }) - expect(decoded).toEqual({ name: "provided" }) - }) - - it("mixed withConstructorDefault and withDecodingDefault", () => { - const schema = S.Struct({ - constructorDefault: S.String.pipe(S.withConstructorDefault(Effect.succeed("fromConstructor"))), - decodingDefault: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("fromDecoding"))) - }) - - // defaultsValueFromSchema should pick up constructor default - const defaults = defaultsValueFromSchema(schema) - expect(defaults.constructorDefault).toBe("fromConstructor") - - // Decoding should pick up decoding default - const decoded = S.decodeUnknownSync(schema)({ constructorDefault: "x" }) - expect(decoded).toEqual({ constructorDefault: "x", decodingDefault: "fromDecoding" }) - }) - - it("defaultsValueFromSchema extracts withDecodingDefault alongside regular fields", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("decodingDefault"))), - required: S.String - }) - - const defaults = defaultsValueFromSchema(schema) - expect(defaults.required).toBe("") - expect(defaults.name).toBe("decodingDefault") - }) - - it("withDecodingDefault can replace manual form default initialization", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("John"))), - age: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(25))), - active: S.optionalKey(S.Boolean).pipe(S.withDecodingDefault(Effect.succeed(true))) - }) - - // Single decode call produces all defaults — no AST walking needed - const defaults = S.decodeUnknownSync(schema)({}) - expect(defaults).toEqual({ name: "John", age: 25, active: true }) - - // Partial input fills in missing defaults - const partial = S.decodeUnknownSync(schema)({ name: "Dave" }) - expect(partial).toEqual({ name: "Dave", age: 25, active: true }) - }) -}) - -// ============================================================================ -// 4. Form-level integration tests — defaults flow end-to-end -// ============================================================================ - -describe("Form defaults integration", () => { - it("schema defaults flow into form values", async () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("hello"))), - active: S.Boolean, - nullable: S.NullOr(S.String) - }) - - const values = await mountAndGetDefaults(schema) - expect(values.name).toBe("hello") - expect(values.active).toBe(false) - expect(values.nullable).toBe(null) - }) - - it("union defaults flow into form values", async () => { - const schema = S.Union([ - S.Struct({ - _tag: S.Literal("A").pipe(S.withConstructorDefault(Effect.succeed("A"))), - value: S.String.pipe(S.withConstructorDefault(Effect.succeed("default"))) - }), - S.Struct({ - _tag: S.Literal("B"), - value: S.Finite - }) - ]) - - const values = await mountAndGetDefaults(schema) - expect(values._tag).toBe("A") - expect(values.value).toBe("default") - }) - - it("tanstack defaultValues override schema defaults", async () => { - const schema = S.Struct({ - name: S.String.pipe(S.withConstructorDefault(Effect.succeed("fromSchema"))), - age: S.Finite.pipe(S.withConstructorDefault(Effect.succeed(0))) - }) - - const values = await mountAndGetDefaults(schema, { - defaultValues: { name: "fromTanstack" } - }) - expect(values.name).toBe("fromTanstack") - expect(values.age).toBe(0) - }) -}) - -// ============================================================================ -// 5. Meta generation for number constraints -// ============================================================================ - -describe("number constraint metadata", () => { - it("separate isGreaterThanOrEqualTo + isLessThanOrEqualTo extracts min and max", () => { - const schema = S.Struct({ - value: S.Finite.pipe( - S.check(S.isGreaterThanOrEqualTo(10)), - S.check(S.isLessThanOrEqualTo(20)) - ) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.type).toBe("number") - expect(meta.value?.minimum).toBe(10) - expect(meta.value?.maximum).toBe(20) - }) - - it("isBetween extracts min and max", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isBetween({ minimum: 10, maximum: 20 }))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.type).toBe("number") - expect(meta.value?.minimum).toBe(10) - expect(meta.value?.maximum).toBe(20) - }) - - it("isInt extracts refinement", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isInt())) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.refinement).toBe("int") - }) - - it("isGreaterThan extracts exclusive minimum", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isGreaterThan(5))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.exclusiveMinimum).toBe(5) - }) - - it("isLessThan extracts exclusive maximum", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isLessThan(100))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.exclusiveMaximum).toBe(100) - }) -}) - -// ============================================================================ -// 6. Meta generation for string constraints -// ============================================================================ - -describe("string constraint metadata", () => { - it("isMinLength and isMaxLength", () => { - const schema = S.Struct({ - value: S.String.pipe(S.check(S.isMinLength(3)), S.check(S.isMaxLength(50))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.minLength).toBe(3) - expect(meta.value?.maxLength).toBe(50) - }) - - it("Email format detection", () => { - const schema = S.Struct({ - email: S.Email - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.email?.format).toBe("email") - expect(meta.email?.type).toBe("string") - }) -}) - -// ============================================================================ -// 7. TaggedStruct API -// ============================================================================ - -describe("TaggedStruct API", () => { - it("TaggedStruct creates struct with _tag literal", () => { - const ts = S.TaggedStruct("A", { a: S.String }) - expect(ts.fields._tag).toBeDefined() - expect(ts.fields.a).toBeDefined() - - const tagProp = ts.ast.propertySignatures.find((p: any) => p.name === "_tag") - expect(S.AST.isLiteral(tagProp!.type)).toBe(true) - expect((tagProp!.type as any).literal).toBe("A") - }) - - it("Union([TaggedStruct, TaggedStruct]) generates field metadata", () => { - const taggedSchema = S.Struct({ - union: S.Union([ - S.TaggedStruct("A", { a: S.NonEmptyString255 }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - }) - - const { meta } = generateMetaFromSchema(taggedSchema) - - expect(meta["union.a"]?.type).toBe("string") - expect(meta["union.b"]?.type).toBe("number") - expect(meta["union._tag"]).toBeDefined() - }) - - it("NullOr(Union([TaggedStruct])) works for nullable discriminated unions", () => { - const schema = S.Struct({ - union: S.NullOr( - S.Union([ - S.TaggedStruct("A", { a: S.NonEmptyString255, common: S.String }), - S.TaggedStruct("B", { b: S.Finite, common: S.String }) - ]) - ) - }) - - const { meta } = generateMetaFromSchema(schema) - expect(meta["union._tag"]?.required).toBe(false) - expect(meta["union.a"]?.required).toBe(true) - expect(meta["union.b"]?.required).toBe(true) - }) - - it("TaggedStruct defaults work with withConstructorDefault", () => { - const schema = S.Union([ - S.TaggedStruct("A", { - a: S.String.pipe(S.withConstructorDefault(Effect.succeed("defaultA"))) - }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - const defaults = defaultsValueFromSchema(schema) - expect(defaults.a).toBe("defaultA") - }) - - it("TaggedStruct decoding works correctly", () => { - const schema = S.Union([ - S.TaggedStruct("A", { a: S.String }), - S.TaggedStruct("B", { b: S.Finite }) - ]) - - const decoded = S.decodeUnknownSync(schema)({ _tag: "A", a: "hello" }) - expect(decoded).toEqual({ _tag: "A", a: "hello" }) - - const decoded2 = S.decodeUnknownSync(schema)({ _tag: "B", b: 42 }) - expect(decoded2).toEqual({ _tag: "B", b: 42 }) - }) -}) - -// ============================================================================ -// 8. Array metadata -// ============================================================================ - -describe("array metadata", () => { - it("Array of primitives generates multiple type", () => { - const schema = S.Struct({ - tags: S.Array(S.String) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.tags?.type).toBe("multiple") - }) - - it("Array of structs generates nested field metadata", () => { - const schema = S.Struct({ - items: S.Array(S.Struct({ - name: S.NonEmptyString255, - value: S.Finite - })) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta["items.name"]).toBeDefined() - expect(meta["items.name"]?.type).toBe("string") - expect(meta["items.value"]?.type).toBe("number") - }) -}) - -// ============================================================================ -// 9. withDecodingDefault form integration -// ============================================================================ - -describe("withDecodingDefault form integration", () => { - it("withDecodingDefault defaults flow into form values", async () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("John"))), - age: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(25))), - active: S.Boolean - }) - - const values = await mountAndGetDefaults(schema) - expect(values.name).toBe("John") - expect(values.age).toBe(25) - expect(values.active).toBe(false) - }) - - it("withDecodingDefault mixed with withConstructorDefault", async () => { - const schema = S.Struct({ - fromDecoding: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("decoding"))), - fromConstructor: S.String.pipe(S.withConstructorDefault(Effect.succeed("constructor"))), - plain: S.Boolean - }) - - const values = await mountAndGetDefaults(schema) - expect(values.fromDecoding).toBe("decoding") - expect(values.fromConstructor).toBe("constructor") - expect(values.plain).toBe(false) - }) - - it("tanstack defaultValues override withDecodingDefault", async () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("fromSchema"))), - other: S.optionalKey(S.Finite).pipe(S.withDecodingDefault(Effect.succeed(99))) - }) - - const values = await mountAndGetDefaults(schema, { - defaultValues: { name: "fromTanstack" } - }) - expect(values.name).toBe("fromTanstack") - expect(values.other).toBe(99) - }) -}) - -// ============================================================================ -// 10. Regression guards -// ============================================================================ - -describe("regression guards", () => { - it("isBetween extracts min and max metadata", () => { - const schema = S.Struct({ - value: S.Finite.pipe(S.check(S.isBetween({ minimum: 5, maximum: 15 }))) - }) - const { meta } = generateMetaFromSchema(schema) - expect(meta.value?.minimum).toBe(5) - expect(meta.value?.maximum).toBe(15) - }) - - it("withDecodingDefault extracted by defaultsValueFromSchema", () => { - const schema = S.Struct({ - name: S.optionalKey(S.String).pipe(S.withDecodingDefault(Effect.succeed("myDefault"))) - }) - const defaults = defaultsValueFromSchema(schema) - expect(defaults.name).toBe("myDefault") - }) -}) diff --git a/packages/vue-components/__tests__/setup.ts b/packages/vue-components/__tests__/setup.ts deleted file mode 100644 index 45a27c16d..000000000 --- a/packages/vue-components/__tests__/setup.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { config } from "@vue/test-utils" -import { useIntlKey } from "../src/utils" - -const mockUseIntl = () => - ({ - trans: (k: string) => `${k}_translated`, - formatMessage: ( - descriptor: { id?: string; defaultMessage?: string }, - _values?: Record - ) => `${descriptor.id ?? descriptor.defaultMessage ?? ""}_formatted` - }) as any - -config.global.provide = { - ...(config.global.provide ?? {}), - [useIntlKey]: mockUseIntl -} diff --git a/packages/vue-components/__tests__/test-array-meta.test.ts b/packages/vue-components/__tests__/test-array-meta.test.ts deleted file mode 100644 index ec3566229..000000000 --- a/packages/vue-components/__tests__/test-array-meta.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../src/components/OmegaForm" - -const schema = S.Struct({ - a: S.Array(S.Struct({ - b: S.String, - c: S.Array(S.Struct({ - d: S.String - })), - e: S.Array(S.Finite) - })), - aMultiple: S.Array(S.String), - aNullable: S.NullOr(S.Struct({ - a: S.String - })), - foo: S - .NullOr( - S.Struct({ - bar: S.UndefinedOr(S.Array(S.Struct({ - baz: S.String, - items: S.NonEmptyArray(S.Struct({ - baz: S.String, - value: S.Finite - })) - }))) - }) - ) -}) - -describe("test-array-meta", () => { - it("should generate metadata for arrays", () => { - const result = generateMetaFromSchema(schema) - console.log("Generated metadata:", JSON.stringify(result.meta, null, 2)) - console.log("Keys:", Object.keys(result.meta)) - - expect(result.meta).toHaveProperty("a.b") - expect(result.meta["a.b"].type).toBe("string") - expect(result.meta).toHaveProperty("a.c.d") - expect(result.meta["a.c.d"].type).toBe("string") - expect(result.meta).toHaveProperty("a.e") - expect(result.meta["a.e"].type).toBe("multiple") - expect(result.meta).toHaveProperty("aMultiple") - expect(result.meta["aMultiple"]?.type).toBe("multiple") - expect(result.meta).toHaveProperty("aNullable.a") - expect(result.meta["aNullable.a"]?.type).toBe("string") - expect(result.meta).not.toHaveProperty("aNullable") - expect(result.meta).toHaveProperty("foo.bar.items.baz") - expect(result.meta["foo.bar.items.baz"]?.type).toBe("string") - expect(result.meta).toHaveProperty("foo.bar.items.value") - expect(result.meta["foo.bar.items.value"]?.type).toBe("number") - expect(result.meta).toHaveProperty("foo.bar.baz") - expect(result.meta["foo.bar.baz"]?.type).toBe("string") - }) -}) diff --git a/packages/vue-components/__tests__/test-nullor-duplicate.test.ts b/packages/vue-components/__tests__/test-nullor-duplicate.test.ts deleted file mode 100644 index 9a39284e9..000000000 --- a/packages/vue-components/__tests__/test-nullor-duplicate.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../src/components/OmegaForm" - -// Test schema from Array.vue story -const schema = S.Struct({ - height: S - .NullOr( - S.Struct({ - left: S.String, - right: S.String, - internal: S.String - }) - ), - width: S.Struct({ - left: S.String, - right: S.String - }) -}) - -describe("test-nullor-duplicate", () => { - it("should not create duplicate height keys for NullOr struct", () => { - const result = generateMetaFromSchema(schema) - console.log("Generated metadata:", JSON.stringify(result.meta, null, 2)) - console.log("Keys:", Object.keys(result.meta)) - - // Should have nested fields for height - expect(result.meta).toHaveProperty("height.left") - expect(result.meta).toHaveProperty("height.right") - expect(result.meta).toHaveProperty("height.internal") - - // Should have nested fields for width - expect(result.meta).toHaveProperty("width.left") - expect(result.meta).toHaveProperty("width.right") - - // Should NOT have a parent "height" key (this was the duplicate issue) - expect(result.meta).not.toHaveProperty("height") - - // Regular structs also don't get parent keys, only their nested fields - expect(result.meta).not.toHaveProperty("width") - }) -}) diff --git a/packages/vue-components/__tests__/test-union-meta.test.ts b/packages/vue-components/__tests__/test-union-meta.test.ts deleted file mode 100644 index 5e11fe6cc..000000000 --- a/packages/vue-components/__tests__/test-union-meta.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as S from "effect-app/Schema" -import { describe, expect, it } from "vitest" -import { generateMetaFromSchema } from "../src/components/OmegaForm" - -const schema = S.Struct({ - mine: S.Struct({ their: S.Union([S.String, S.Struct({ yours: S.String })]) }) -}) - -describe("test-union-meta", () => { - it("should generate metadata for union", () => { - const result = generateMetaFromSchema(schema) - console.log("Generated metadata:", JSON.stringify(result.meta, null, 2)) - - expect(result.meta).toHaveProperty("mine.their") - expect(result.meta).toHaveProperty("mine.their.yours") - }) -}) diff --git a/packages/vue-components/__tests__/tsconfig.json b/packages/vue-components/__tests__/tsconfig.json deleted file mode 100644 index be9137dfa..000000000 --- a/packages/vue-components/__tests__/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "types": [ - "vite/client", - "vitest/globals" - ] - } -} diff --git a/packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md b/packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md deleted file mode 100644 index db53cdf8e..000000000 --- a/packages/vue-components/docs/superpowers/plans/2026-04-27-omegaform-refactor.md +++ /dev/null @@ -1,2655 +0,0 @@ -# OmegaForm Refactor Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Decompose `OmegaFormStuff.ts` (1422 lines) and `useOmegaForm.ts` (1022 lines) into focused modules, slim the schema-meta extraction, centralize validation onto a single localized form-level schema, and remove dead workarounds. Public API unchanged. - -**Architecture:** Five sequential phases. Phase 0 writes characterization tests against current behavior. Phase 1 splits files by concern (no behavior change). Phase 2 slims `createMeta` and adopts the v4 `format` annotation for email detection. Phase 3 centralizes validation and deletes the per-field schema and the errorMap-clearing workaround. Phase 4 is independent cleanup. Each task makes one self-contained, atomically committable change. - -**Tech Stack:** - -- TypeScript, Vue 3 (` -``` - -And replace the template's `` with: - -```vue - -``` - -Note the deletions: `:key="fieldKey"`, `:validators="{ ...validators, onSubmit: schema }"`, the `schema` computed, the `composeStandardSchemas` helper, the `hasIssues` helper, the `useIntl` call, the `fieldKey` computed. - -- [ ] **Step 2: Run tests + smoke** - -Run: `pnpm test:run` -Expected: green. - -Manually open the `IntegerValidationGerman` story and verify German validation messages still appear. They should — they now come from the form-level localized schema, not the per-field one. If they don't appear, debug Task 3.1. - -- [ ] **Step 3: Commit** - -```bash -git add src/components/OmegaForm/OmegaInput.vue -git commit -m "refactor(omegaform): delete per-field schema and fieldKey re-mount" -``` - ---- - -### Task 3.5: Delete `originalSchema` plumbing and `generateInputStandardSchemaFromFieldMeta` - -**Files:** - -- Modify: `src/components/OmegaForm/meta/types.ts` -- Modify: `src/components/OmegaForm/meta/createMeta.ts` -- Modify: `src/components/OmegaForm/validation/localized.ts` -- Modify: `src/components/OmegaForm/index.ts` -- Delete: `__tests__/OmegaForm/DiscriminatorFieldValidation.test.ts` - -- [ ] **Step 1: Delete the type field** - -In `meta/types.ts`, remove the `originalSchema?: StandardSchemaV1` field from `BaseFieldMeta`. - -- [ ] **Step 2: Delete the attachment plumbing** - -In `meta/createMeta.ts`, delete: - -- `attachOriginalSchemas` -- `toFieldStandardSchema` -- `fieldAstByPath` collection variable everywhere it's plumbed through `createMeta` / `walk` -- All call sites of `attachOriginalSchemas(...)` - -- [ ] **Step 3: Delete the per-field schema generator** - -In `validation/localized.ts`, delete `generateInputStandardSchemaFromFieldMeta` (~150 lines). - -- [ ] **Step 4: Update `index.ts`** - -Remove the export of `generateInputStandardSchemaFromFieldMeta`. - -- [ ] **Step 5: Delete `DiscriminatorFieldValidation.test.ts`** - -This test directly references `originalSchema` / `originalCodec` / `generateInputStandardSchemaFromFieldMeta` via `Reflect.get`. With those gone, the test is testing a deleted API. The form-level schema already validates discriminator values; that's covered by `TaggedUnionRoot.test.ts`'s `_tag` assertions plus the runtime story. - -```bash -git rm __tests__/OmegaForm/DiscriminatorFieldValidation.test.ts -``` - -- [ ] **Step 6: Run tests + tsc + lint** - -Run: `pnpm test:run && pnpm check && pnpm lint` -Expected: green. - -- [ ] **Step 7: Commit** - -```bash -git add -A -git commit -m "refactor(omegaform): delete originalSchema plumbing and per-field schema generator" -``` - ---- - -### Task 3.6: Phase 3 green-bar checkpoint - -- [ ] **Step 1: Full test suite** - -Run: `pnpm test:run && pnpm check && pnpm lint` -Expected: green. - -- [ ] **Step 2: Smoke check all five stories** - -Especially `IntegerValidationGerman` (proves form-level localization works) and `RootLevelTaggedUnion` (proves union meta still drives the right-required-flag-per-tag behavior). - -- [ ] **Step 3: Tag** - -```bash -git tag omegaform-refactor-phase3-green -``` - ---- - -## Phase 4 — Cleanup (each task is an independent commit) - -These tasks can be done in any order or interleaved with bug-fix work; they don't have inter-dependencies. - -### Task 4.1: Translate the German confirm dialog - -**Files:** - -- Modify: `src/components/OmegaForm/blockDialog.ts` -- Modify: `stories/OmegaForm.stories.ts` (add the translation key to the German mock) - -- [ ] **Step 1: Modify `blockDialog.ts`** - -Find the line: - -```ts -if (!confirm("Es sind ungespeicherte Änderungen vorhanden. Wirklich schließen?")) { -``` - -Replace with: - -```ts -const message = formatMessage({ - id: "form.unsaved_changes_confirm", - defaultMessage: "There are unsaved changes. Are you sure you want to close?" -}) -if (!confirm(message)) { -``` - -You'll need to inject `useIntl` at the top of the function: - -```ts -import { useIntl } from "../../utils" -``` - -(Or accept `formatMessage` as an optional dependency injected by the caller — match the existing pattern in `errors.ts`.) - -- [ ] **Step 2: Add the German translation in storybook decorator** - -In `stories/OmegaForm.stories.ts`, add to `germanTranslations`: - -```ts -"form.unsaved_changes_confirm": "Es sind ungespeicherte Änderungen vorhanden. Wirklich schließen?" -``` - -- [ ] **Step 3: Smoke-check** - -Open the `DialogBlockingExamples` story (with German locale toggled on) and trigger the confirm dialog by editing a field then attempting to close. Confirm the German text appears. - -- [ ] **Step 4: Commit** - -```bash -git add src/components/OmegaForm/blockDialog.ts stories/OmegaForm.stories.ts -git commit -m "i18n(omegaform): translate unsaved changes confirm dialog" -``` - ---- - -### Task 4.2: Drop deprecated string-typed fields _(conditional on consumer audit)_ - -**Files:** - -- Modify: `src/components/OmegaForm/types.ts` (formerly `OmegaFormStuff.ts`) -- Modify: `src/components/OmegaForm/useOmegaForm.ts` - -- [ ] **Step 1: Audit consumer apps** - -Run from the repo root: - -```bash -grep -rn 'overrideDefaultValues\|"deprecated:' apps/ 2>/dev/null -grep -rn 'defaultFromSchema' apps/ 2>/dev/null -grep -rn 'OmegaArrayProps.*items' apps/ 2>/dev/null -``` - -If any matches: skip this task, file an issue, keep the deprecated field one more cycle. - -If no matches: proceed. - -- [ ] **Step 2: Remove the deprecated string-typed fields** - -In `OmegaConfig` (in `useOmegaForm.ts` or wherever it landed): - -- Remove `overrideDefaultValues?: "deprecated: use defaultValuesPriority"` -- Remove `defaultFromSchema?: "deprecated: use defaultValuesPriority"` - -In `OmegaArrayProps`: - -- Remove `items?: "please use \`defaultItems\` instead"` - -- [ ] **Step 3: Run tests + tsc** - -Run: `pnpm test:run && pnpm check` -Expected: green. - -- [ ] **Step 4: Commit** - -```bash -git add -A -git commit -m "refactor(omegaform): drop deprecated string-typed config fields" -``` - ---- - -### Task 4.3: Drop legacy `field` slot in OmegaArray.vue _(conditional)_ - -**Files:** - -- Modify: `src/components/OmegaForm/OmegaArray.vue` - -- [ ] **Step 1: Audit consumer apps** - -```bash -grep -rn '

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